Enhance release workflow with detailed comments and conditional artif… #16
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| # This workflow creates a GitHub Release when a tag is pushed and builds/distributes: | |
| # - Multi-architecture (x86_64 + aarch64) Linux Docker images and binary tarballs | |
| # - A universal (fat) macOS binary (combining aarch64 + x86_64) packaged as a tarball | |
| # - Multi-arch container manifest referencing per-arch images | |
| # | |
| # Key notes / caveats: | |
| # - Tag filter currently uses a regex-like pattern string ("v[0-9]+.[0-9]+.[0-9]+") but GitHub interprets these as globs, not regex. | |
| # This pattern will literally match tags containing brackets/plus signs only if such tags are pushed. Consider replacing with 'v*.*.*'. | |
| # - Additional explicit tag 'test' triggers a non-latest release (see Create Release step logic). | |
| # - Runner label 'ubuntu-24.04-arm' may not exist in GitHub-hosted runners at all times; verify actual available ARM labels. | |
| # - Docker images are only tagged as latest-<arch>; version-specific per-arch tags are not produced (manifest step synthesizes version tag from those latest-* tags). | |
| # - Release creation is not idempotent on reruns (gh release create fails if release already exists). Consider guarding or using 'gh release view || gh release edit'. | |
| # - Using nightly Rust on macOS; unless nightly features are required, stable may improve reproducibility. | |
| # - No build caching is configured (Cargo + Docker) which could lengthen CI times. | |
| # - Security hardening (e.g., minimal permissions, SBOM, provenance attestations) could be added in future. | |
| # - File now contains verbose comments for clarity; functional YAML content unchanged. | |
| on: | |
| push: | |
| tags: | |
| - "v[0-9]+.[0-9]+.[0-9]+" # Intended semantic version tag pattern (BUT this is a glob, not regex). Replace with 'v*.*.*' for broad SemVer matching. | |
| - test # Special test tag to exercise the pipeline without marking release as latest. | |
| permissions: {} # Explicitly sets default job permissions to none (principle of least privilege). Each job grants what it needs. | |
| env: | |
| CARGO_TERM_COLOR: always # Ensures colored Cargo output in logs. | |
| RELEASE_BIN: mira-oxide # Canonical binary name used across packaging steps. | |
| REGISTRY: ghcr.io # Container registry host (GitHub Container Registry). | |
| jobs: | |
| create-release: | |
| name: Create New Release | |
| runs-on: ubuntu-latest # Standard Linux runner for release orchestration. | |
| permissions: | |
| contents: write # Required to create a GitHub Release and upload assets. | |
| outputs: | |
| upload_url: ${{ steps.create_release.outputs.upload_url }} # (Currently unused downstream; could remove if not consumed.) | |
| steps: | |
| - uses: actions/checkout@v4 # Fetch repository code to access changelog script and metadata. | |
| - name: Extract Changelog | |
| id: extract_changelog | |
| shell: bash | |
| run: perl .github/scripts/last_release_notes.pl > $RUNNER_TEMP/release_notes.md # Generates notes for this release. | |
| - name: Create Release | |
| id: create_release | |
| if: startsWith(github.ref_name, 'v') # Only create a release for version tags, skip entirely for 'test'. | |
| shell: bash | |
| run: | | |
| # Create the GitHub Release using GitHub CLI. Fails if release with same tag already exists. | |
| gh release create "${{ github.ref_name }}" \ | |
| --title "${RELEASE_BIN} ${{ github.ref_name }}" \ | |
| --notes-file "$RUNNER_TEMP/release_notes.md" | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GitHub-provided token with contents:write permission. | |
| build-release-image-and-linux-artifacts: | |
| name: Build Linux Artifacts and Images | |
| needs: [create-release] # Ensures the release exists before uploading assets. | |
| strategy: | |
| matrix: | |
| include: | |
| - arch: x86_64 | |
| runner: ubuntu-latest # Standard x86_64 runner. | |
| - arch: aarch64 | |
| runner: ubuntu-24.04-arm # Intended ARM64 runner label (verify availability; may differ, e.g., ubuntu-22.04-arm64). | |
| runs-on: ${{ matrix.runner }} # Dynamically select runner based on matrix entry. | |
| permissions: | |
| contents: write # Needed to upload release assets. | |
| packages: write # Needed to push images to GHCR. | |
| steps: | |
| - uses: actions/checkout@v4 # Source code for Docker build context. | |
| - name: Log in to the Container registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} # ghcr.io | |
| username: ${{ github.actor }} # Actor initiating the workflow (token auth is sufficient). | |
| password: ${{ secrets.GITHUB_TOKEN }}# Token automatically scoped to repo; sufficient for GHCR push. | |
| - name: Extract metadata for Docker | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ github.repository }} # Image repository path (may contain uppercase -> manifests later lowercase). | |
| tags: | | |
| type=raw,value=latest-${{ matrix.arch }},enable=${{ startsWith(github.ref_name, 'v') }} | |
| type=raw,value=${{ github.ref_name }}-${{ matrix.arch }},enable=${{ startsWith(github.ref_name, 'v') }} | |
| type=raw,value=test-${{ matrix.arch }},enable=${{ github.ref_name == 'test' }} | |
| - name: Debug Git ref | |
| run: | | |
| # Helpful diagnostic output to confirm tag context and repository naming. | |
| echo "GITHUB_REF: $GITHUB_REF" | |
| echo "GITHUB_REF_NAME: $GITHUB_REF_NAME" | |
| echo "GITHUB_SHA: $GITHUB_SHA" | |
| echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY" | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| file: Dockerfile # Uses repository Dockerfile at root. | |
| context: . # Full repo as build context. | |
| push: true # Push images to registry. | |
| tags: ${{ steps.meta.outputs.tags }} # Tag list from metadata action (only latest-arch currently). | |
| # Potential enhancements: buildx cache-from/to for faster rebuilds; provenance/SBOM generation. | |
| - name: Prepare Linux Binary | |
| shell: bash | |
| run: | | |
| # Pull the just-built image tag matching this architecture and extract the compiled binary from /app. | |
| IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | grep -- '-${{ matrix.arch }}$' | head -n 1) | |
| docker create --name temp-container $IMAGE_TAG \ | |
| && docker cp temp-container:/app/${RELEASE_BIN} ./${RELEASE_BIN} \ | |
| && docker rm temp-container | |
| # If this is a version tag (v*), append the version to the binary filename inside the archive. | |
| if [[ "${GITHUB_REF_NAME}" == v* ]]; then | |
| mv "${RELEASE_BIN}" "${RELEASE_BIN}-${GITHUB_REF_NAME}" | |
| BIN_NAME="${RELEASE_BIN}-${GITHUB_REF_NAME}" | |
| else | |
| BIN_NAME="${RELEASE_BIN}" | |
| fi | |
| tar -czf ${RELEASE_BIN}-linux-${{ matrix.arch }}-${{ github.ref_name }}.tar.gz "$BIN_NAME" | |
| - name: Upload Linux Release Asset | |
| if: startsWith(github.ref_name, 'v') # Only upload to a GitHub Release for version tags. | |
| shell: bash | |
| run: | | |
| # Upload architecture-specific Linux binary tarball to the existing GitHub Release. | |
| gh release upload "${{ github.ref_name }}" ${RELEASE_BIN}-linux-${{ matrix.arch }}-${{ github.ref_name }}.tar.gz | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload Linux Test Artifact | |
| if: github.ref_name == 'test' # For test tag, store as workflow artifact instead of release asset. | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ env.RELEASE_BIN }}-linux-${{ matrix.arch }}-test | |
| path: ${{ env.RELEASE_BIN }}-linux-${{ matrix.arch }}-${{ github.ref_name }}.tar.gz | |
| create-image-manifest: | |
| name: Create Image Manifest | |
| needs: build-release-image-and-linux-artifacts # Wait until both arch images are pushed. | |
| runs-on: ubuntu-latest | |
| permissions: | |
| packages: write # Required to push multi-arch manifests. | |
| contents: read # Minimal read (not strictly required here, but harmless). | |
| steps: | |
| - name: Log in to the Container registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create and push manifests (version tags) | |
| if: startsWith(github.ref_name, 'v') | |
| shell: bash | |
| run: | | |
| REPO=$(echo "${{ env.REGISTRY }}/${{ github.repository }}" | tr '[A-Z]' '[a-z]') | |
| VERSION=${REPO}:${{ github.ref_name }} | |
| LATEST=${REPO}:latest | |
| docker manifest create $VERSION --amend ${VERSION}-x86_64 --amend ${VERSION}-aarch64 || \ | |
| docker manifest create $VERSION --amend ${LATEST}-x86_64 --amend ${LATEST}-aarch64 | |
| docker manifest push $VERSION | |
| docker manifest create $LATEST --amend ${LATEST}-x86_64 --amend ${LATEST}-aarch64 | |
| docker manifest push $LATEST | |
| - name: Create and push manifests (test tag) | |
| if: github.ref_name == 'test' | |
| shell: bash | |
| run: | | |
| REPO=$(echo "${{ env.REGISTRY }}/${{ github.repository }}" | tr '[A-Z]' '[a-z]') | |
| TEST=${REPO}:test | |
| docker manifest create $TEST --amend ${TEST}-x86_64 --amend ${TEST}-aarch64 | |
| docker manifest push $TEST | |
| build-mac-artifacts: | |
| name: Build Mac Artifacts | |
| needs: create-release # Independent of Linux build path; both upload assets to same release. | |
| runs-on: macos-latest | |
| permissions: | |
| contents: write # Needed for asset upload. | |
| steps: | |
| - uses: actions/checkout@v4 # Fetch code. | |
| - name: Install latest nightly Rust | |
| run: | | |
| # Nightly toolchain installation; replace with stable if not using nightly-only features. | |
| rustup update nightly | |
| rustup default nightly | |
| rustup target add aarch64-apple-darwin x86_64-apple-darwin | |
| - name: Build | |
| run: | | |
| # Compile both target architectures in release mode. | |
| cargo build --release --target aarch64-apple-darwin --target x86_64-apple-darwin | |
| - name: Package Binary | |
| shell: bash | |
| run: | | |
| # Combine the two architecture-specific binaries into one universal (fat) binary via lipo. | |
| lipo -create -output ${{ env.RELEASE_BIN }} \ | |
| target/aarch64-apple-darwin/release/${{ env.RELEASE_BIN }} \ | |
| target/x86_64-apple-darwin/release/${{ env.RELEASE_BIN }} | |
| # Rename binary to include version if tag is v* | |
| if [[ "${GITHUB_REF_NAME}" == v* ]]; then | |
| mv "${{ env.RELEASE_BIN }}" "${{ env.RELEASE_BIN }}-${GITHUB_REF_NAME}" | |
| MAC_BIN="${{ env.RELEASE_BIN }}-${GITHUB_REF_NAME}" | |
| else | |
| MAC_BIN="${{ env.RELEASE_BIN }}" | |
| fi | |
| tar -czf ${{ env.RELEASE_BIN }}-apple-universal-${{ github.ref_name }}.tar.gz "$MAC_BIN" | |
| - name: Upload Release Asset | |
| if: startsWith(github.ref_name, 'v') # Only upload to release for version tags. | |
| shell: bash | |
| run: | | |
| # Upload macOS universal binary tarball (wildcard allows potential accompanying files; could be made explicit). | |
| gh release upload "${{ github.ref_name }}" ${{ env.RELEASE_BIN }}-apple-universal-${{ github.ref_name }}.* | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Upload Mac Test Artifact | |
| if: github.ref_name == 'test' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ env.RELEASE_BIN }}-apple-universal-test | |
| path: ${{ env.RELEASE_BIN }}-apple-universal-${{ github.ref_name }}.tar.gz |