Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 114 additions & 61 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ on:
push:
branches:
- main
tags:
- "v[0-9]+.*"
workflow_dispatch: {}

permissions:
contents: write
Expand All @@ -17,37 +16,48 @@ env:

jobs:
# ------------------------------------------------------------------
# 1. Create a draft GitHub release (tags only)
# 0. Detect whether this is a release run
# ------------------------------------------------------------------
create-release:
name: Create release
if: startsWith(github.ref, 'refs/tags/')
check-release:
name: Check release
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.tag.outputs.tag }}
is_release: ${{ steps.check.outputs.is_release }}
version: ${{ steps.check.outputs.version }}
version_major: ${{ steps.check.outputs.version_major }}
version_minor: ${{ steps.check.outputs.version_minor }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2
with:
fetch-depth: 1
fetch-tags: true

- name: Get tag
id: tag
run: echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"

- name: Verify tag matches Cargo.toml version
- name: Detect release
id: check
run: |
TAG="${{ steps.tag.outputs.tag }}"
CARGO_VERSION="v$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')"
if [ "$TAG" != "$CARGO_VERSION" ]; then
echo "::error::Tag $TAG does not match Cargo.toml version $CARGO_VERSION"
exit 1
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="v$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')"
RAW="${VERSION#v}"
MAJOR=$(echo "$RAW" | cut -d. -f1)
MINOR="${MAJOR}.$(echo "$RAW" | cut -d. -f2)"
echo "is_release=true" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "version_major=$MAJOR" >> "$GITHUB_OUTPUT"
echo "version_minor=$MINOR" >> "$GITHUB_OUTPUT"

if git tag -l "$VERSION" | grep -q .; then
echo "::error::Tag $VERSION already exists"
exit 1
fi
echo "Detected release: $VERSION"
else
echo "is_release=false" >> "$GITHUB_OUTPUT"
echo "version=" >> "$GITHUB_OUTPUT"
echo "Not a release run"
fi

- name: Create draft release
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "${{ steps.tag.outputs.tag }}" --draft --verify-tag --title "${{ steps.tag.outputs.tag }}"

# ------------------------------------------------------------------
# 2. Build binaries for each target (always)
# 1. Build binaries for each target (always)
# ------------------------------------------------------------------
build-binaries:
name: Build ${{ matrix.name }}
Expand Down Expand Up @@ -148,17 +158,17 @@ jobs:
echo "archive=${ARCHIVE}.tar.gz" >> "$GITHUB_OUTPUT"
echo "checksum=${ARCHIVE}.tar.gz.sha256" >> "$GITHUB_OUTPUT"

- name: Upload to release
if: startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "${GITHUB_REF#refs/tags/}" \
"${{ steps.package.outputs.archive }}" \
"${{ steps.package.outputs.checksum }}"
- name: Upload build artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # ratchet:actions/upload-artifact@v7.0.0
with:
name: binary-${{ matrix.name }}
path: |
${{ steps.package.outputs.archive }}
${{ steps.package.outputs.checksum }}
retention-days: 1

# ------------------------------------------------------------------
# 3. Build per-platform Docker images on native runners
# 2. Build per-platform Docker images on native runners
# ------------------------------------------------------------------
docker-build:
name: Docker ${{ matrix.platform }}
Expand Down Expand Up @@ -218,10 +228,11 @@ jobs:
retention-days: 1

# ------------------------------------------------------------------
# 3a. Build SIMD-optimized Docker images (single-arch each)
# 2a. Build SIMD-optimized Docker images (single-arch each)
# ------------------------------------------------------------------
docker-build-simd:
name: Docker ${{ matrix.name }}
needs: [check-release]
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
Expand Down Expand Up @@ -267,9 +278,9 @@ jobs:
with:
images: ghcr.io/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}-${{ matrix.tag_suffix }},enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=raw,value=latest-${{ matrix.tag_suffix }},enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=raw,value=dev-${{ matrix.tag_suffix }},enable=${{ github.ref == 'refs/heads/main' }}
type=raw,value=${{ needs.check-release.outputs.version }}-${{ matrix.tag_suffix }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
type=raw,value=latest-${{ matrix.tag_suffix }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
type=raw,value=dev-${{ matrix.tag_suffix }}

- name: Build and push
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # ratchet:docker/build-push-action@v7.0.0
Expand All @@ -285,11 +296,11 @@ jobs:
cache-to: type=gha,scope=${{ matrix.name }},mode=max

# ------------------------------------------------------------------
# 3b. Merge per-platform images into a multi-arch manifest
# 2b. Merge per-platform images into a multi-arch manifest
# ------------------------------------------------------------------
docker-merge:
name: Docker merge
needs: docker-build
needs: [check-release, docker-build]
runs-on: ubuntu-latest
steps:
- name: Lowercase image name
Expand All @@ -308,11 +319,11 @@ jobs:
with:
images: ghcr.io/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=semver,pattern={{major}},enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=raw,value=dev,enable=${{ github.ref == 'refs/heads/main' }}
type=raw,value=${{ needs.check-release.outputs.version }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
type=raw,value=${{ needs.check-release.outputs.version_minor }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
type=raw,value=${{ needs.check-release.outputs.version_major }},enable=${{ needs.check-release.outputs.is_release == 'true' }}
type=raw,value=latest,enable=${{ needs.check-release.outputs.is_release == 'true' }}
type=raw,value=dev

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # ratchet:docker/setup-buildx-action@v4.0.0
Expand All @@ -332,12 +343,69 @@ jobs:
$(printf 'ghcr.io/${{ env.IMAGE_NAME }}@sha256:%s ' *)

# ------------------------------------------------------------------
# 4. Publish to crates.io (tags only)
# 3. Create tag and draft release (release only, after all builds)
# ------------------------------------------------------------------
create-tag-and-release:
name: Create tag and release
if: needs.check-release.outputs.is_release == 'true'
needs: [check-release, build-binaries, docker-merge, docker-build-simd]
runs-on: ubuntu-latest
outputs:
tag: ${{ needs.check-release.outputs.version }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2

- name: Create tag
run: |
git tag "${{ needs.check-release.outputs.version }}"
git push origin "${{ needs.check-release.outputs.version }}"

- name: Extract changelog
run: |
RAW="${{ needs.check-release.outputs.version }}"
RAW="${RAW#v}"
awk '/^## \[Version '"$RAW"'\]/{found=1; next} /^## \[/{if(found) exit} found' CHANGELOG.md > /tmp/release-notes.md

- name: Create release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "${{ needs.check-release.outputs.version }}" \
--target "${{ github.sha }}" \
--title "RustQC ${{ needs.check-release.outputs.version }}" \
--notes-file /tmp/release-notes.md

# ------------------------------------------------------------------
# 4. Upload binaries to the release
# ------------------------------------------------------------------
upload-binaries:
name: Upload binaries
if: needs.check-release.outputs.is_release == 'true'
needs: [check-release, create-tag-and-release, build-binaries]
runs-on: ubuntu-latest
steps:
- name: Download all binary artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # ratchet:actions/download-artifact@v8.0.1
with:
pattern: binary-*
path: /tmp/binaries
merge-multiple: true

- name: Upload to release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "${{ needs.create-tag-and-release.outputs.tag }}" \
/tmp/binaries/*.tar.gz \
/tmp/binaries/*.sha256

# ------------------------------------------------------------------
# 5. Publish to crates.io (release only, after all builds)
# ------------------------------------------------------------------
publish-crate:
name: Publish to crates.io
if: startsWith(github.ref, 'refs/tags/')
needs: [create-release, build-binaries]
if: needs.check-release.outputs.is_release == 'true'
needs: [check-release, create-tag-and-release]
runs-on: ubuntu-latest
permissions:
id-token: write
Expand All @@ -353,18 +421,3 @@ jobs:
run: cargo publish --no-verify
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}

# ------------------------------------------------------------------
# 5. Publish the release (tags only)
# ------------------------------------------------------------------
publish-release:
name: Publish release
if: startsWith(github.ref, 'refs/tags/')
needs: [create-release, build-binaries, docker-merge, docker-build-simd, publish-crate]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v6.0.2
- name: Publish release
env:
GH_TOKEN: ${{ github.token }}
run: gh release edit "${{ needs.create-release.outputs.tag }}" --draft=false
Loading