Pin runners for glibc compatibility #35
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
| # Copyright (c) 2025 Erick Bourgeois, firestoned | |
| # SPDX-License-Identifier: Apache-2.0 | |
| name: Build | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - 'src/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| - 'Dockerfile*' | |
| - '.github/workflows/build.yaml' | |
| - '.github/actions/**' | |
| push: | |
| branches: | |
| - main | |
| release: | |
| types: | |
| - published | |
| # Defaults for jobs that do not declare their own permissions block. | |
| # Jobs with special needs (docker, attest, sign, upload) override at job level. | |
| permissions: | |
| contents: read | |
| id-token: write | |
| security-events: write | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| # Override the cross-compilation linkers set in .cargo/config.toml. | |
| # config.toml specifies the Homebrew cross-compilers needed when a macOS | |
| # developer runs `cargo build --target x86_64-unknown-linux-gnu` locally. | |
| # On Linux CI runners the native target IS x86_64/aarch64-unknown-linux-gnu, | |
| # so cargo would try to invoke those cross-compilers — which are not | |
| # installed. Setting these vars to `cc` restores the default system linker. | |
| CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER: cc | |
| CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: cc | |
| jobs: | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # License headers — always runs (PRs are pre-filtered by path at the trigger). | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| license-check: | |
| name: ⚖️ Verify SPDX License Headers | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check license headers | |
| uses: firestoned/github-actions/security/license-check@v1.3.6 | |
| with: | |
| copyright-holder: "Erick Bourgeois, firestoned" | |
| license-id: "Apache-2.0" | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Commit signatures — verify-mode differs by event type. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| verify-commits: | |
| name: 🔐 Verify Signed Commits | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Verify commits (PR) | |
| if: github.event_name == 'pull_request' | |
| uses: firestoned/github-actions/security/verify-signed-commits@v1.3.6 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| base-ref: origin/${{ github.base_ref }} | |
| verify-mode: pr | |
| - name: Verify commits (push to main) | |
| if: github.event_name == 'push' | |
| uses: firestoned/github-actions/security/verify-signed-commits@v1.3.6 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| verify-mode: push | |
| - name: Verify commits (release) | |
| if: github.event_name == 'release' | |
| uses: firestoned/github-actions/security/verify-signed-commits@v1.3.6 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| verify-mode: release | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # PR only: formatting + clippy. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| format: | |
| name: 🎨 Check Formatting | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| needs: [license-check, verify-commits] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt | |
| - name: Check formatting | |
| run: cargo fmt -- --check | |
| clippy: | |
| name: 📎 Clippy | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| needs: [format] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy | |
| - name: Cache cargo dependencies | |
| uses: firestoned/github-actions/rust/cache-cargo@v1.3.6 | |
| - name: Run clippy | |
| run: cargo clippy --all-targets --all-features -- -D warnings | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Extract version — all events; downstream build jobs depend on this. | |
| # format/clippy are independent PR quality checks and do not gate the build. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| extract-version: | |
| name: 🏷️ Extract Version Information | |
| runs-on: ubuntu-latest | |
| needs: [license-check, verify-commits] | |
| outputs: | |
| version: ${{ steps.version-chainguard.outputs.version }} | |
| tag_name: ${{ steps.version-chainguard.outputs.tag-name }} | |
| image-tag-chainguard: ${{ steps.version-chainguard.outputs.image-tag }} | |
| image-tag-distroless: ${{ steps.version-distroless.outputs.image-tag }} | |
| image-repository-chainguard: ${{ steps.version-chainguard.outputs.image-repository }} | |
| image-repository-distroless: ${{ steps.version-distroless.outputs.image-repository }} | |
| short-sha: ${{ steps.version-chainguard.outputs.short-sha }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Extract version for Chainguard | |
| id: version-chainguard | |
| uses: ./.github/actions/extract-version | |
| with: | |
| repository: ${{ github.repository }} | |
| workflow-type: ${{ github.event_name == 'pull_request' && 'pr' || github.event_name == 'push' && 'main' || 'release' }} | |
| pr-number: ${{ github.event.pull_request.number }} | |
| release-tag: ${{ github.event.release.tag_name }} | |
| image-tag-suffix: "" | |
| - name: Extract version for Distroless | |
| id: version-distroless | |
| uses: ./.github/actions/extract-version | |
| with: | |
| repository: ${{ github.repository }} | |
| workflow-type: ${{ github.event_name == 'pull_request' && 'pr' || github.event_name == 'push' && 'main' || 'release' }} | |
| pr-number: ${{ github.event.pull_request.number }} | |
| release-tag: ${{ github.event.release.tag_name }} | |
| image-tag-suffix: "-distroless" | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Build binary — all events. | |
| # On release the Cargo.toml version is bumped to match the release tag. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| build: | |
| name: 🦀 Build - ${{ matrix.platform.name }} | |
| runs-on: ${{ matrix.platform.runner }} | |
| needs: [extract-version] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: | |
| - name: Linux x86_64 | |
| runner: ubuntu-24.04 | |
| artifact_name: 5spot-linux-amd64 | |
| binary_name: 5spot | |
| - name: Linux ARM64 | |
| runner: ubuntu-24.04-arm | |
| artifact_name: 5spot-linux-arm64 | |
| binary_name: 5spot | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Update Cargo.toml version | |
| if: github.event_name == 'release' | |
| run: | | |
| perl -i -pe 's/^version = ".*"/version = "${{ needs.extract-version.outputs.version }}"/' Cargo.toml | |
| echo "Updated Cargo.toml to version ${{ needs.extract-version.outputs.version }}" | |
| grep '^version = ' Cargo.toml | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo dependencies | |
| uses: firestoned/github-actions/rust/cache-cargo@v1.3.6 | |
| - name: Build binary | |
| run: cargo build --release | |
| - name: Install cargo-cyclonedx | |
| run: cargo install cargo-cyclonedx --locked --quiet | |
| - name: Generate SBOM | |
| run: | | |
| cargo cyclonedx --format json | |
| find . -maxdepth 1 -name "*.cdx.json" -exec mv {} target/release/ \; | |
| # PR and push artifacts expire after 1 day — they are not release assets. | |
| - name: Upload artifacts (PR / push) | |
| if: github.event_name != 'release' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.platform.artifact_name }} | |
| path: | | |
| target/release/${{ matrix.platform.binary_name }} | |
| target/release/*.cdx.json | |
| retention-days: 1 | |
| # Release artifacts keep the default retention so re-runs remain possible. | |
| - name: Upload artifacts (release) | |
| if: github.event_name == 'release' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.platform.artifact_name }} | |
| path: | | |
| target/release/${{ matrix.platform.binary_name }} | |
| target/release/*.cdx.json | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Tests — all events; running unconditionally lets docker depend on it simply. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| test: | |
| name: 🧪 Test | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Download x86_64 build artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: 5spot-linux-amd64 | |
| path: target/release/ | |
| - name: Cache cargo dependencies | |
| uses: firestoned/github-actions/rust/cache-cargo@v1.3.6 | |
| - name: Run tests | |
| run: cargo test --all --verbose | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Docker build and push — all events. | |
| # | |
| # Metadata tags vary by event (three separate metadata steps, only one runs | |
| # per invocation; empty outputs from skipped steps are filtered out by | |
| # docker/build-push-action). On release, Cosign signing and SBOM generation | |
| # steps are also enabled. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| docker: | |
| name: 🐳 Build and Push Docker Image - ${{ matrix.variant.name }} | |
| runs-on: ubuntu-latest | |
| needs: [build, extract-version, test] | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| outputs: | |
| digest-chainguard: ${{ steps.export-digest.outputs.digest-chainguard }} | |
| digest-distroless: ${{ steps.export-digest.outputs.digest-distroless }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| variant: | |
| - name: Chainguard | |
| dockerfile: Dockerfile.chainguard | |
| suffix: "" | |
| description: "5-Spot Chainguard Zero-CVE (glibc, FIPS-ready)" | |
| image-tag: ${{ needs.extract-version.outputs.image-tag-chainguard }} | |
| image-repository: ${{ needs.extract-version.outputs.image-repository-chainguard }} | |
| - name: Distroless | |
| dockerfile: Dockerfile | |
| suffix: "-distroless" | |
| description: "5-Spot Google Distroless (glibc)" | |
| image-tag: ${{ needs.extract-version.outputs.image-tag-distroless }} | |
| image-repository: ${{ needs.extract-version.outputs.image-repository-distroless }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Prepare Docker binaries | |
| uses: ./.github/actions/prepare-docker-binaries | |
| - name: Setup Docker environment | |
| uses: firestoned/github-actions/docker/setup-docker@v1.3.6 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| # Only one of the three metadata steps below runs per job instance. | |
| # docker/build-push-action concatenates all tags/labels outputs and | |
| # silently drops the empty lines produced by the two skipped steps. | |
| - name: Extract metadata (PR) | |
| id: meta-pr | |
| if: github.event_name == 'pull_request' | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ matrix.variant.image-repository }} | |
| tags: | | |
| type=raw,value=${{ matrix.variant.image-tag }} | |
| type=raw,value=sha-${{ needs.extract-version.outputs.short-sha }} | |
| flavor: | | |
| latest=false | |
| - name: Extract metadata (push to main) | |
| id: meta-main | |
| if: github.event_name == 'push' | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ matrix.variant.image-repository }} | |
| tags: | | |
| type=raw,value=${{ matrix.variant.image-tag }} | |
| type=raw,value=main-{{date 'YYYY-MM-DD'}} | |
| type=raw,value=sha-${{ needs.extract-version.outputs.short-sha }} | |
| type=raw,value=latest | |
| flavor: | | |
| latest=false | |
| - name: Extract metadata (release) | |
| id: meta-release | |
| if: github.event_name == 'release' | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ matrix.variant.image-repository }} | |
| tags: | | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=raw,value=sha-${{ needs.extract-version.outputs.short-sha }}${{ matrix.variant.suffix }} | |
| flavor: | | |
| latest=false | |
| prefix=v | |
| suffix=${{ matrix.variant.suffix }} | |
| - name: Build and push Docker image (${{ matrix.variant.name }}) | |
| id: docker_build | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ${{ matrix.variant.dockerfile }} | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: | | |
| ${{ steps.meta-pr.outputs.tags }} | |
| ${{ steps.meta-main.outputs.tags }} | |
| ${{ steps.meta-release.outputs.tags }} | |
| labels: | | |
| ${{ steps.meta-pr.outputs.labels }} | |
| ${{ steps.meta-main.outputs.labels }} | |
| ${{ steps.meta-release.outputs.labels }} | |
| org.opencontainers.image.description=${{ matrix.variant.description }} | |
| build-args: | | |
| ${{ github.event_name == 'release' && format('VERSION={0}', needs.extract-version.outputs.version) || '' }} | |
| cache-from: type=gha,scope=${{ matrix.variant.name }} | |
| cache-to: type=gha,mode=max,scope=${{ matrix.variant.name }} | |
| sbom: true | |
| provenance: true | |
| # Push to main and release: sign the image with Cosign (keyless, Sigstore). | |
| # PR images are ephemeral and not deployed, so signing them is skipped. | |
| # Sign by digest (not tag) to guarantee we sign exactly what was pushed. | |
| - name: Install Cosign | |
| if: github.event_name != 'pull_request' | |
| uses: sigstore/cosign-installer@v3 | |
| - name: Sign container image with Cosign | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| cosign sign --yes \ | |
| ghcr.io/${{ matrix.variant.image-repository }}@${{ steps.docker_build.outputs.digest }} | |
| # Release only: generate a CycloneDX SBOM for the published image. | |
| - name: Generate Docker SBOM for ${{ matrix.variant.name }} | |
| if: github.event_name == 'release' | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| image: ghcr.io/${{ matrix.variant.image-repository }}:${{ matrix.variant.image-tag }} | |
| format: cyclonedx-json | |
| output-file: docker-sbom-${{ matrix.variant.name }}.json | |
| upload-release-assets: false | |
| - name: Upload Docker SBOM as artifact | |
| if: github.event_name == 'release' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-sbom-${{ matrix.variant.name }} | |
| path: docker-sbom-${{ matrix.variant.name }}.json | |
| - name: Export image digest | |
| id: export-digest | |
| run: | | |
| case "${{ matrix.variant.name }}" in | |
| Chainguard) echo "digest-chainguard=${{ steps.docker_build.outputs.digest }}" >> "$GITHUB_OUTPUT" ;; | |
| Distroless) echo "digest-distroless=${{ steps.docker_build.outputs.digest }}" >> "$GITHUB_OUTPUT" ;; | |
| esac | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # GitHub Artifact Attestation — all events. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| attest: | |
| name: 🔏 Attest Docker Image - ${{ matrix.variant.name }} | |
| runs-on: ubuntu-latest | |
| needs: [docker, extract-version] | |
| permissions: | |
| id-token: write | |
| attestations: write | |
| packages: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| variant: | |
| - name: Chainguard | |
| image-repository: ${{ needs.extract-version.outputs.image-repository-chainguard }} | |
| digest: ${{ needs.docker.outputs.digest-chainguard }} | |
| - name: Distroless | |
| image-repository: ${{ needs.extract-version.outputs.image-repository-distroless }} | |
| digest: ${{ needs.docker.outputs.digest-distroless }} | |
| steps: | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Attest ${{ matrix.variant.name }} image provenance | |
| uses: actions/attest-build-provenance@v2 | |
| with: | |
| subject-name: ghcr.io/${{ matrix.variant.image-repository }} | |
| subject-digest: ${{ matrix.variant.digest }} | |
| push-to-registry: true | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Security scan — all events. | |
| # Gitleaks secret scan only runs on PRs; release uploads the audit report. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| security: | |
| name: 🛡️ Security Vulnerability Scan | |
| runs-on: ubuntu-latest | |
| needs: [verify-commits] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Run security scan (PR / push) | |
| if: github.event_name != 'release' | |
| uses: firestoned/github-actions/rust/security-scan@v1.3.6 | |
| with: | |
| cargo-audit-version: "0.22.0" | |
| - name: Run security scan (release) | |
| if: github.event_name == 'release' | |
| uses: firestoned/github-actions/rust/security-scan@v1.3.6 | |
| with: | |
| cargo-audit-version: "0.22.0" | |
| upload-artifact-name: cargo-audit-report-release | |
| - name: Run gitleaks (secret scanning) | |
| if: github.event_name == 'pull_request' | |
| run: make gitleaks | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Container security scan — all events. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| trivy: | |
| name: 🔭 Container Security Scan - ${{ matrix.variant.name }} | |
| runs-on: ubuntu-latest | |
| needs: [docker, extract-version] | |
| permissions: | |
| contents: read | |
| packages: read | |
| id-token: write | |
| security-events: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| variant: | |
| - name: Chainguard | |
| image-tag: ${{ needs.extract-version.outputs.image-tag-chainguard }} | |
| image-repository: ${{ needs.extract-version.outputs.image-repository-chainguard }} | |
| - name: Distroless | |
| image-tag: ${{ needs.extract-version.outputs.image-tag-distroless }} | |
| image-repository: ${{ needs.extract-version.outputs.image-repository-distroless }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Run Trivy scan on ${{ matrix.variant.name }} image | |
| uses: firestoned/github-actions/security/trivy-scan@v1.3.6 | |
| with: | |
| image-ref: ghcr.io/${{ matrix.variant.image-repository }}:${{ matrix.variant.image-tag }} | |
| sarif-category: trivy-${{ github.event_name }}-${{ matrix.variant.name }} | |
| upload-artifact-name: ${{ github.event_name == 'release' && format('trivy-release-{0}', matrix.variant.name) || format('trivy-main-{0}-{1}', matrix.variant.name, github.run_number) }} | |
| output-filename: trivy-${{ github.event_name }}-${{ matrix.variant.name }}.json | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Release only: sign binaries with Cosign. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| sign-artifacts: | |
| name: ✍️ Sign Binary Artifacts | |
| if: github.event_name == 'release' | |
| runs-on: ubuntu-latest | |
| needs: [build, extract-version] | |
| permissions: | |
| id-token: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| platform: | |
| - artifact_name: 5spot-linux-amd64 | |
| binary_name: 5spot | |
| - artifact_name: 5spot-linux-arm64 | |
| binary_name: 5spot | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download binary artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ${{ matrix.platform.artifact_name }} | |
| path: ./artifacts/${{ matrix.platform.artifact_name }} | |
| - name: Create tarball for signing | |
| run: | | |
| cd artifacts/${{ matrix.platform.artifact_name }} | |
| chmod +x ${{ matrix.platform.binary_name }} | |
| tar czf ${{ matrix.platform.artifact_name }}.tar.gz ${{ matrix.platform.binary_name }} | |
| ls -lh ${{ matrix.platform.artifact_name }}.tar.gz | |
| - name: Sign binary tarball with Cosign | |
| uses: firestoned/github-actions/security/cosign-sign@v1.3.6 | |
| with: | |
| artifact-path: artifacts/${{ matrix.platform.artifact_name }}/${{ matrix.platform.artifact_name }}.tar.gz | |
| - name: Upload signed artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.platform.artifact_name }}-signed | |
| path: | | |
| artifacts/${{ matrix.platform.artifact_name }}/${{ matrix.platform.artifact_name }}.tar.gz | |
| artifacts/${{ matrix.platform.artifact_name }}/${{ matrix.platform.artifact_name }}.tar.gz.bundle | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Release only: compute SHA-256 hashes of signed tarballs for SLSA. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| generate-provenance-subjects: | |
| name: 🔬 Generate SLSA Provenance Subjects | |
| if: github.event_name == 'release' | |
| runs-on: ubuntu-latest | |
| needs: [sign-artifacts, extract-version] | |
| outputs: | |
| hashes: ${{ steps.hash.outputs.hashes }} | |
| steps: | |
| - name: Download signed artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: 5spot-*-signed | |
| path: ./artifacts | |
| merge-multiple: true | |
| - name: Generate hashes for SLSA provenance | |
| id: hash | |
| run: | | |
| cd artifacts | |
| sha256sum *.tar.gz > checksums.txt | |
| cat checksums.txt | |
| echo "hashes=$(cat checksums.txt | base64 -w0)" >> "$GITHUB_OUTPUT" | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Release only: SLSA Level 3 provenance via the official generator. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| slsa-provenance: | |
| name: 🏛️ Generate SLSA Provenance | |
| if: github.event_name == 'release' | |
| needs: [generate-provenance-subjects, extract-version] | |
| permissions: | |
| actions: read | |
| id-token: write | |
| contents: write | |
| uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 | |
| with: | |
| base64-subjects: "${{ needs.generate-provenance-subjects.outputs.hashes }}" | |
| upload-assets: true | |
| provenance-name: "${{ needs.extract-version.outputs.version }}.intoto.jsonl" | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Release only: regenerate and package the CRD deploy manifests. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| package-deploy-manifests: | |
| name: 📦 Package Deployment Manifests | |
| if: github.event_name == 'release' | |
| runs-on: ubuntu-latest | |
| needs: [extract-version] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo dependencies | |
| uses: firestoned/github-actions/rust/cache-cargo@v1.3.6 | |
| - name: Regenerate CRDs | |
| run: cargo run --bin crdgen > deploy/crds/scheduledmachine.yaml | |
| - name: Package deploy manifests | |
| run: tar czf deploy-manifests.tar.gz deploy/ | |
| - name: Upload deploy manifests | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: deploy-manifests | |
| path: deploy-manifests.tar.gz | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Release only: collate all artifacts and upload to the GitHub Release. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| upload-release-assets: | |
| name: 🚀 Upload Release Assets | |
| if: github.event_name == 'release' | |
| runs-on: ubuntu-latest | |
| needs: [build, docker, attest, sign-artifacts, slsa-provenance, package-deploy-manifests] | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: ./artifacts | |
| pattern: "{5spot-*,docker-sbom-*,*.intoto.jsonl,deploy-manifests}" | |
| - name: 🧹 Organize release artifacts | |
| run: | | |
| cd artifacts | |
| mkdir -p release sboms signatures provenance | |
| # Copy signed tarballs and signature bundles | |
| for dir in 5spot-*-signed; do | |
| if [ -d "$dir" ]; then | |
| platform="${dir%-signed}" | |
| if compgen -G "$dir/*.tar.gz" > /dev/null; then | |
| cp "$dir"/*.tar.gz release/ | |
| fi | |
| if compgen -G "$dir/*.tar.gz.bundle" > /dev/null; then | |
| cp "$dir"/*.tar.gz.bundle signatures/ | |
| fi | |
| fi | |
| done | |
| # Collect binary SBOMs (Linux builds only) | |
| for dir in 5spot-linux-amd64 5spot-linux-arm64; do | |
| if [ -d "$dir" ] && compgen -G "$dir/*.cdx.json" > /dev/null; then | |
| cp "$dir"/*.cdx.json "sboms/${dir}-sbom.json" | |
| fi | |
| done | |
| # Copy Docker SBOMs | |
| for variant in Chainguard Distroless; do | |
| if [ -d "docker-sbom-${variant}" ] && [ -f "docker-sbom-${variant}/docker-sbom-${variant}.json" ]; then | |
| cp "docker-sbom-${variant}/docker-sbom-${variant}.json" "sboms/docker-sbom-${variant}.json" | |
| fi | |
| done | |
| # Copy SLSA provenance | |
| find . -name "*.intoto.jsonl" -type f -exec cp {} provenance/ \; | |
| # Copy deploy manifests | |
| if [ -f "deploy-manifests/deploy-manifests.tar.gz" ]; then | |
| cp deploy-manifests/deploy-manifests.tar.gz release/ | |
| fi | |
| # Generate SHA256 checksums | |
| cd release && sha256sum * > checksums.sha256 | |
| cd ../sboms && sha256sum *.json >> ../release/checksums.sha256 | |
| cd ../signatures && sha256sum *.bundle >> ../release/checksums.sha256 | |
| cd ../provenance && compgen -G "*.intoto.jsonl" > /dev/null && sha256sum *.intoto.jsonl >> ../release/checksums.sha256 || true | |
| echo "Release artifacts:" | |
| ls -lh ../release/ | |
| - name: Upload release assets | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| artifacts/release/*.tar.gz | |
| artifacts/sboms/*.json | |
| artifacts/signatures/*.bundle | |
| artifacts/provenance/*.intoto.jsonl | |
| artifacts/release/checksums.sha256 |