From 7c200a77a6ed48b273dda22075a038207f48f9cb Mon Sep 17 00:00:00 2001 From: amackillop Date: Mon, 30 Mar 2026 12:58:57 -0700 Subject: [PATCH] Add release workflow for tag-based publishing Triggered on v* tags. Verifies the tag matches the Cargo.toml version, builds static binaries for x86_64 and aarch64 via Nix, creates a GitHub Release with both binaries attached, then builds and pushes multi-arch container images to GHCR. The container pipeline builds arch-specific images on native runners (avoiding slow QEMU emulation), pushes per-arch tags, then assembles a multi-arch manifest. The "latest" tag only moves forward when the pushed tag is the highest semver, so publishing a patch for an older release line won't clobber it. All third-party actions are pinned to full commit SHAs. --- .github/workflows/release.yml | 180 ++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..637dba2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,180 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + packages: write + id-token: write + +jobs: + check-version: + name: Verify tag matches Cargo.toml + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v 6.02 + + - name: Compare tag to Cargo.toml version + run: | + CARGO_VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1) + TAG_VERSION="${GITHUB_REF_NAME#v}" + if [ "$CARGO_VERSION" != "$TAG_VERSION" ]; then + echo "::error::Tag ${GITHUB_REF_NAME} does not match Cargo.toml version ${CARGO_VERSION}" + exit 1 + fi + + build: + name: Build Static (${{ matrix.arch }}) + needs: check-version + runs-on: ${{ matrix.runner }} + strategy: + matrix: + include: + - runner: ubuntu-latest + arch: x86_64-linux + - runner: ubuntu-24.04-arm + arch: aarch64-linux + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v 6.02 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@c5a866b6ab867e88becbed4467b93592bce69f8a # v21 + + - name: Setup Magic Nix Cache + uses: DeterminateSystems/magic-nix-cache-action@565684385bcd71bad329742eefe8d12f2e765b39 # v13 + + - name: Build static binary + run: nix build .#static --print-build-logs + + - name: Upload artifact + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: mdkd-${{ matrix.arch }} + path: result/bin/mdkd + + release: + name: Create GitHub Release + needs: build + runs-on: ubuntu-latest + steps: + - name: Download x86_64 artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: mdkd-x86_64-linux + path: artifacts/x86_64-linux + + - name: Download aarch64 artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: mdkd-aarch64-linux + path: artifacts/aarch64-linux + + - name: Prepare release assets + run: | + mv artifacts/x86_64-linux/mdkd mdkd-x86_64-linux + mv artifacts/aarch64-linux/mdkd mdkd-aarch64-linux + chmod +x mdkd-x86_64-linux mdkd-aarch64-linux + + - name: Create GitHub Release + uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 + with: + generate_release_notes: true + files: | + mdkd-x86_64-linux + mdkd-aarch64-linux + + image: + name: Build & Push Image (${{ matrix.arch }}) + needs: build + runs-on: ${{ matrix.runner }} + strategy: + matrix: + include: + - runner: ubuntu-latest + arch: x86_64-linux + docker_arch: amd64 + - runner: ubuntu-24.04-arm + arch: aarch64-linux + docker_arch: arm64 + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v 6.02 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@c5a866b6ab867e88becbed4467b93592bce69f8a # v21 + + - name: Setup Magic Nix Cache + uses: DeterminateSystems/magic-nix-cache-action@565684385bcd71bad329742eefe8d12f2e765b39 # v13 + + - name: Build container image + run: nix build .#image --print-build-logs + + - name: Load image + run: docker load < result + + - name: Log in to GHCR + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push arch-specific tag + env: + VERSION: ${{ github.ref_name }} + IMAGE: ghcr.io/${{ github.repository }} + run: | + docker tag mdkd:latest ${IMAGE}:${VERSION}-${{ matrix.docker_arch }} + docker push ${IMAGE}:${VERSION}-${{ matrix.docker_arch }} + + manifest: + name: Create Multi-Arch Manifest + needs: image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v 6.02 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Log in to GHCR + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if this is the latest version + id: check_latest + env: + VERSION: ${{ github.ref_name }} + run: | + LATEST=$(git tag -l 'v*' | sort -V | tail -1) + echo "is_latest=$( [ "$VERSION" = "$LATEST" ] && echo true || echo false )" >> "$GITHUB_OUTPUT" + + - name: Create and push version manifest + env: + VERSION: ${{ github.ref_name }} + IMAGE: ghcr.io/${{ github.repository }} + run: | + docker manifest create ${IMAGE}:${VERSION} \ + ${IMAGE}:${VERSION}-amd64 \ + ${IMAGE}:${VERSION}-arm64 + docker manifest push ${IMAGE}:${VERSION} + + - name: Tag as latest + if: steps.check_latest.outputs.is_latest == 'true' + env: + VERSION: ${{ github.ref_name }} + IMAGE: ghcr.io/${{ github.repository }} + run: | + docker manifest create ${IMAGE}:latest \ + ${IMAGE}:${VERSION}-amd64 \ + ${IMAGE}:${VERSION}-arm64 + docker manifest push ${IMAGE}:latest