cachekit-core 0.4.0

LZ4 compression, xxHash3 integrity, AES-256-GCM encryption for byte payloads
Documentation
name: Release

on:
  push:
    branches:
      - main
  # Manual re-publish escape hatch: lets the publish job run for the version
  # currently on main when an automated release published the tag but the
  # publish job failed downstream (e.g. a transient Sigstore/Fulcio outage).
  workflow_dispatch: {}

permissions:
  contents: write
  pull-requests: write

# Serialise release runs so a push and a manual dispatch can't race into two
# concurrent cargo publish attempts. Never cancel an in-flight publish.
concurrency:
  group: release-${{ github.ref }}
  cancel-in-progress: false

jobs:
  release-please:
    runs-on: cachekit
    outputs:
      release_created: ${{ steps.release.outputs.release_created }}
      tag_name: ${{ steps.release.outputs.tag_name }}
    steps:
      - uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2
        id: app-token
        with:
          app-id: ${{ secrets.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}

      - uses: googleapis/release-please-action@8b8fd2cc23b2e18957157a9d923d75aa0c6f6ad5 # v4
        id: release
        with:
          token: ${{ steps.app-token.outputs.token }}

      # release-please's config schema has no assignees option, so assign the
      # release PR here. outputs.pr is set whenever the release PR was created
      # OR updated, so this re-runs on every push to main while a release PR is
      # open — adding an already-present assignee is a no-op, so that's safe.
      # github-script, NOT `gh`: the self-hosted cachekit runner has no gh CLI
      # (LAB-899). outputs.pr is passed raw via env and parsed in JS — never
      # through template-position fromJson, which is evaluated even when if: is
      # false and crashes on '' for no-release pushes (LAB-865).
      - name: Assign release PR to 27Bslash6
        if: ${{ steps.release.outputs.pr }}
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          PR_JSON: ${{ steps.release.outputs.pr }}
        with:
          github-token: ${{ steps.app-token.outputs.token }}
          script: |
            try {
              const pr = JSON.parse(process.env.PR_JSON);
              await github.rest.issues.addAssignees({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: pr.number,
                assignees: ['27Bslash6'],
              });
              core.info(`Assigned 27Bslash6 to release PR #${pr.number}`);
            } catch (error) {
              core.setFailed(`Failed to assign release PR in ${context.repo.owner}/${context.repo.repo} (PR_JSON length ${process.env.PR_JSON.length}): ${error.message}`);
            }

  publish:
    needs: release-please
    # Run on an automated release, OR on a manual dispatch — but a manual dispatch
    # must target main (workflow_dispatch can be fired from any ref; restricting to
    # refs/heads/main stops a feature branch from publishing its own Cargo.toml).
    # Explicit == 'true' avoids relying on string-coercion of the action output.
    if: ${{ needs.release-please.outputs.release_created == 'true' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') }}
    # GitHub-hosted: the self-hosted ARC pods have unreliable DNS/egress to Sigstore
    # (Fulcio/Rekor), which intermittently fails build-provenance + SBOM attestation
    # and blocks publish. A hosted runner has reliable egress to Sigstore + crates.io,
    # and the publish job is infrequent + free on public repos.
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
      attestations: write
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

      - name: Cache cargo registry
        uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2

      - name: Run tests before publish
        run: cargo test --all-features

      - name: Package crate
        run: cargo package

      - name: Attest Build Provenance
        uses: actions/attest-build-provenance@96b4a1ef7235a096b17240c259729fdd70c83d45 # v2
        with:
          subject-path: target/package/*.crate

      - name: Install cargo-sbom
        # --force is required: the self-hosted runner's CARGO_HOME (/cache/cargo) is a
        # persistent volume, so the binary survives between runs and a plain install
        # exits 101 ("binary `cargo-sbom` already exists"). --force reinstalls the
        # --locked pinned version idempotently.
        run: cargo install cargo-sbom --locked --force

      - name: Generate SBOM
        run: |
          cargo sbom --output-format cyclone_dx_json_1_6 > sbom.cdx.json
          test -s sbom.cdx.json || { echo "::error::SBOM file is empty"; exit 1; }

      - name: Attest SBOM
        uses: actions/attest-sbom@10926c72720ffc3f7b666661c8e55b1344e2a365 # v2
        with:
          subject-path: target/package/*.crate
          sbom-path: sbom.cdx.json

      - name: Publish to crates.io
        run: cargo publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}