44 push :
55 branches :
66 - main
7- tags :
8- - " v[0-9]+.*"
7+ workflow_dispatch : {}
98
109permissions :
1110 contents : write
1716
1817jobs :
1918 # ------------------------------------------------------------------
20- # 1. Create a draft GitHub release (tags only)
19+ # 0. Detect whether this is a release run
2120 # ------------------------------------------------------------------
22- create-release :
23- name : Create release
24- if : startsWith(github.ref, 'refs/tags/')
21+ check-release :
22+ name : Check release
2523 runs-on : ubuntu-latest
2624 outputs :
27- tag : ${{ steps.tag.outputs.tag }}
25+ is_release : ${{ steps.check.outputs.is_release }}
26+ version : ${{ steps.check.outputs.version }}
27+ version_major : ${{ steps.check.outputs.version_major }}
28+ version_minor : ${{ steps.check.outputs.version_minor }}
2829 steps :
2930 - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2
31+ with :
32+ fetch-depth : 1
33+ fetch-tags : true
3034
31- - name : Get tag
32- id : tag
33- run : echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
34-
35- - name : Verify tag matches Cargo.toml version
35+ - name : Detect release
36+ id : check
3637 run : |
37- TAG="${{ steps.tag.outputs.tag }}"
38- CARGO_VERSION="v$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')"
39- if [ "$TAG" != "$CARGO_VERSION" ]; then
40- echo "::error::Tag $TAG does not match Cargo.toml version $CARGO_VERSION"
41- exit 1
38+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
39+ VERSION="v$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')"
40+ RAW="${VERSION#v}"
41+ MAJOR=$(echo "$RAW" | cut -d. -f1)
42+ MINOR="${MAJOR}.$(echo "$RAW" | cut -d. -f2)"
43+ echo "is_release=true" >> "$GITHUB_OUTPUT"
44+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
45+ echo "version_major=$MAJOR" >> "$GITHUB_OUTPUT"
46+ echo "version_minor=$MINOR" >> "$GITHUB_OUTPUT"
47+
48+ if git tag -l "$VERSION" | grep -q .; then
49+ echo "::error::Tag $VERSION already exists"
50+ exit 1
51+ fi
52+ echo "Detected release: $VERSION"
53+ else
54+ echo "is_release=false" >> "$GITHUB_OUTPUT"
55+ echo "version=" >> "$GITHUB_OUTPUT"
56+ echo "Not a release run"
4257 fi
4358
44- - name : Create draft release
45- env :
46- GH_TOKEN : ${{ github.token }}
47- run : gh release create "${{ steps.tag.outputs.tag }}" --draft --verify-tag --title "${{ steps.tag.outputs.tag }}"
48-
4959 # ------------------------------------------------------------------
50- # 2 . Build binaries for each target (always)
60+ # 1 . Build binaries for each target (always)
5161 # ------------------------------------------------------------------
5262 build-binaries :
5363 name : Build ${{ matrix.name }}
@@ -148,17 +158,17 @@ jobs:
148158 echo "archive=${ARCHIVE}.tar.gz" >> "$GITHUB_OUTPUT"
149159 echo "checksum=${ARCHIVE}.tar.gz.sha256" >> "$GITHUB_OUTPUT"
150160
151- - name : Upload to release
152- if : startsWith(github.ref, 'refs/tags/')
153- env :
154- GH_TOKEN : ${{ github.token }}
155- run : |
156- gh release upload "${GITHUB_REF#refs/tags/}" \
157- " ${{ steps.package.outputs.archive }}" \
158- "${{ steps.package.outputs.checksum }}"
161+ - name : Upload build artifact
162+ uses : actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # ratchet:actions/upload-artifact@v7.0.0
163+ with :
164+ name : binary- ${{ matrix.name }}
165+ path : |
166+ ${{ steps.package.outputs.archive }}
167+ ${{ steps.package.outputs.checksum }}
168+ retention-days : 1
159169
160170 # ------------------------------------------------------------------
161- # 3 . Build per-platform Docker images on native runners
171+ # 2 . Build per-platform Docker images on native runners
162172 # ------------------------------------------------------------------
163173 docker-build :
164174 name : Docker ${{ matrix.platform }}
@@ -218,10 +228,11 @@ jobs:
218228 retention-days : 1
219229
220230 # ------------------------------------------------------------------
221- # 3a . Build SIMD-optimized Docker images (single-arch each)
231+ # 2a . Build SIMD-optimized Docker images (single-arch each)
222232 # ------------------------------------------------------------------
223233 docker-build-simd :
224234 name : Docker ${{ matrix.name }}
235+ needs : [check-release]
225236 runs-on : ${{ matrix.runner }}
226237 strategy :
227238 fail-fast : false
@@ -267,9 +278,9 @@ jobs:
267278 with :
268279 images : ghcr.io/${{ env.IMAGE_NAME }}
269280 tags : |
270- type=semver,pattern={{ version}}-${{ matrix.tag_suffix }},enable=${{ startsWith(github.ref, 'refs/tags/') }}
271- type=raw,value=latest-${{ matrix.tag_suffix }},enable=${{ startsWith(github.ref, 'refs/tags/') }}
272- type=raw,value=dev-${{ matrix.tag_suffix }},enable=${{ github.ref == 'refs/heads/main' }}
281+ type=raw,value=${{ needs.check-release.outputs. version }}-${{ matrix.tag_suffix }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
282+ type=raw,value=latest-${{ matrix.tag_suffix }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
283+ type=raw,value=dev-${{ matrix.tag_suffix }}
273284
274285 - name : Build and push
275286 uses : docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # ratchet:docker/build-push-action@v7.0.0
@@ -285,11 +296,11 @@ jobs:
285296 cache-to : type=gha,scope=${{ matrix.name }},mode=max
286297
287298 # ------------------------------------------------------------------
288- # 3b . Merge per-platform images into a multi-arch manifest
299+ # 2b . Merge per-platform images into a multi-arch manifest
289300 # ------------------------------------------------------------------
290301 docker-merge :
291302 name : Docker merge
292- needs : docker-build
303+ needs : [check-release, docker-build]
293304 runs-on : ubuntu-latest
294305 steps :
295306 - name : Lowercase image name
@@ -308,11 +319,11 @@ jobs:
308319 with :
309320 images : ghcr.io/${{ env.IMAGE_NAME }}
310321 tags : |
311- type=semver,pattern={{ version}},enable=${{ startsWith(github.ref, 'refs/tags/') }}
312- type=semver,pattern={{major}}.{{minor }},enable=${{ startsWith(github.ref, 'refs/tags/') }}
313- type=semver,pattern={{major }},enable=${{ startsWith(github.ref, 'refs/tags/') }}
314- type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
315- type=raw,value=dev,enable=${{ github.ref == 'refs/heads/main' }}
322+ type=raw,value=${{ needs.check-release.outputs. version }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
323+ type=raw,value=${{ needs.check-release.outputs.version_minor }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
324+ type=raw,value=${{ needs.check-release.outputs.version_major }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
325+ type=raw,value=latest,enable=${{ needs.check-release.outputs.is_release == 'true' }}
326+ type=raw,value=dev
316327
317328 - name : Set up Docker Buildx
318329 uses : docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # ratchet:docker/setup-buildx-action@v4.0.0
@@ -332,12 +343,69 @@ jobs:
332343 $(printf 'ghcr.io/${{ env.IMAGE_NAME }}@sha256:%s ' *)
333344
334345 # ------------------------------------------------------------------
335- # 4. Publish to crates.io (tags only)
346+ # 3. Create tag and draft release (release only, after all builds)
347+ # ------------------------------------------------------------------
348+ create-tag-and-release :
349+ name : Create tag and release
350+ if : needs.check-release.outputs.is_release == 'true'
351+ needs : [check-release, build-binaries, docker-merge, docker-build-simd]
352+ runs-on : ubuntu-latest
353+ outputs :
354+ tag : ${{ needs.check-release.outputs.version }}
355+ steps :
356+ - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2
357+
358+ - name : Create tag
359+ run : |
360+ git tag "${{ needs.check-release.outputs.version }}"
361+ git push origin "${{ needs.check-release.outputs.version }}"
362+
363+ - name : Extract changelog
364+ run : |
365+ RAW="${{ needs.check-release.outputs.version }}"
366+ RAW="${RAW#v}"
367+ awk '/^## \[Version '"$RAW"'\]/{found=1; next} /^## \[/{if(found) exit} found' CHANGELOG.md > /tmp/release-notes.md
368+
369+ - name : Create release
370+ env :
371+ GH_TOKEN : ${{ github.token }}
372+ run : |
373+ gh release create "${{ needs.check-release.outputs.version }}" \
374+ --target "${{ github.sha }}" \
375+ --title "RustQC ${{ needs.check-release.outputs.version }}" \
376+ --notes-file /tmp/release-notes.md
377+
378+ # ------------------------------------------------------------------
379+ # 4. Upload binaries to the release
380+ # ------------------------------------------------------------------
381+ upload-binaries :
382+ name : Upload binaries
383+ if : needs.check-release.outputs.is_release == 'true'
384+ needs : [check-release, create-tag-and-release, build-binaries]
385+ runs-on : ubuntu-latest
386+ steps :
387+ - name : Download all binary artifacts
388+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8.0.1
389+ with :
390+ pattern : binary-*
391+ path : /tmp/binaries
392+ merge-multiple : true
393+
394+ - name : Upload to release
395+ env :
396+ GH_TOKEN : ${{ github.token }}
397+ run : |
398+ gh release upload "${{ needs.create-tag-and-release.outputs.tag }}" \
399+ /tmp/binaries/*.tar.gz \
400+ /tmp/binaries/*.sha256
401+
402+ # ------------------------------------------------------------------
403+ # 5. Publish to crates.io (release only, after all builds)
336404 # ------------------------------------------------------------------
337405 publish-crate :
338406 name : Publish to crates.io
339- if : startsWith(github.ref, 'refs/tags/')
340- needs : [create -release, build-binaries ]
407+ if : needs.check-release.outputs.is_release == 'true'
408+ needs : [check -release, create-tag-and-release ]
341409 runs-on : ubuntu-latest
342410 permissions :
343411 id-token : write
@@ -353,18 +421,3 @@ jobs:
353421 run : cargo publish --no-verify
354422 env :
355423 CARGO_REGISTRY_TOKEN : ${{ steps.auth.outputs.token }}
356-
357- # ------------------------------------------------------------------
358- # 5. Publish the release (tags only)
359- # ------------------------------------------------------------------
360- publish-release :
361- name : Publish release
362- if : startsWith(github.ref, 'refs/tags/')
363- needs : [create-release, build-binaries, docker-merge, docker-build-simd, publish-crate]
364- runs-on : ubuntu-latest
365- steps :
366- - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2
367- - name : Publish release
368- env :
369- GH_TOKEN : ${{ github.token }}
370- run : gh release edit "${{ needs.create-release.outputs.tag }}" --draft=false
0 commit comments