release.yml
Snapshot 2263a414fe628107f433d708f83dd82cc29e55a6 from main.
1# Publish a tagged release to GHCR and GitHub Releases.
2# No server rollout. The container tags are the version and a short sha.
3# The floating tag named latest is disabled below.
4name: release
5
6on:
7 push:
8 tags: ["v*.*.*"]
9
10permissions:
11 contents: read
12
13defaults:
14 run:
15 shell: bash
16
17jobs:
18 ci:
19 # Workspace-relative on purpose: actionlint 1.7.12 rejects the $/ form.
20 uses: ./.github/workflows/ci.yml # zizmor: ignore[self-repository]
21 permissions:
22 actions: read
23 contents: read
24 security-events: write
25
26 publish:
27 needs: ci
28 runs-on: ubuntu-latest
29 timeout-minutes: 20
30 permissions:
31 actions: read
32 attestations: write
33 contents: write
34 id-token: write
35 packages: write
36 steps:
37 - name: Download distributions
38 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
39 with:
40 name: dist
41 path: dist
42
43 - name: Download SBOMs
44 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
45 with:
46 name: sbom
47 path: sbom
48
49 - name: Download the scanned image
50 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
51 with:
52 name: image
53 path: image
54
55 - name: Log in to GHCR
56 uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
57 with:
58 registry: ghcr.io
59 username: ${{ github.actor }}
60 password: ${{ secrets.GITHUB_TOKEN }}
61
62 - name: Image tags
63 id: meta
64 uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
65 with:
66 images: ghcr.io/${{ github.repository }}
67 flavor: |
68 latest=false
69 tags: |
70 type=semver,pattern={{version}}
71 type=sha,prefix=sha-,format=short
72
73 - name: Load, tag, and push the scanned image
74 env:
75 TAGS: ${{ steps.meta.outputs.tags }}
76 run: |
77 set -euo pipefail
78 archive="$(find image -name 'image.tar' -print -quit)"
79 test -n "${archive}"
80 docker load -i "${archive}"
81 test -n "${TAGS}"
82 printf '%s\n' "${TAGS}" | while IFS= read -r tag; do
83 if [ -z "${tag}" ]; then
84 continue
85 fi
86 docker tag ratecard-api:ci "${tag}"
87 docker push "${tag}"
88 done
89
90 - name: Read the registry digest
91 id: digest
92 env:
93 TAGS: ${{ steps.meta.outputs.tags }}
94 run: |
95 set -euo pipefail
96 first="${TAGS%%$'\n'*}"
97 test -n "${first}"
98 name="${first%%:*}"
99 digest="$(docker image inspect "${first}" --format '{{range .RepoDigests}}{{println .}}{{end}}' | head -n 1)"
100 digest="${digest##*@}"
101 case "${digest}" in
102 sha256:*) ;;
103 *)
104 echo "registry digest missing" >&2
105 exit 1
106 ;;
107 esac
108 printf 'name=%s\n' "${name}" >> "${GITHUB_OUTPUT}"
109 printf 'digest=%s\n' "${digest}" >> "${GITHUB_OUTPUT}"
110
111 - name: Attest the image
112 uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
113 with:
114 subject-name: ${{ steps.digest.outputs.name }}
115 subject-digest: ${{ steps.digest.outputs.digest }}
116 push-to-registry: true
117
118 - name: Locate the wheel and sdist
119 id: packages
120 run: |
121 set -euo pipefail
122 wheel="$(find dist -name '*.whl' -print -quit)"
123 sdist="$(find dist -name '*.tar.gz' -print -quit)"
124 test -n "${wheel}"
125 test -n "${sdist}"
126 printf 'wheel=%s\n' "${wheel}" >> "${GITHUB_OUTPUT}"
127 printf 'sdist=%s\n' "${sdist}" >> "${GITHUB_OUTPUT}"
128
129 - name: Attest the wheel
130 uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
131 with:
132 subject-path: ${{ steps.packages.outputs.wheel }}
133
134 - name: Attest the sdist
135 uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
136 with:
137 subject-path: ${{ steps.packages.outputs.sdist }}
138
139 - name: Write SHA256SUMS
140 run: |
141 set -euo pipefail
142 find dist sbom -type f -print0 | sort -z | xargs -0 sha256sum > SHA256SUMS
143
144 - name: GitHub Release
145 uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
146 with:
147 generate_release_notes: true
148 fail_on_unmatched_files: true
149 files: |
150 dist/*
151 sbom/*
152 SHA256SUMS
153 token: ${{ secrets.GITHUB_TOKEN }}