Spaces:
Running
Running
| name: UDS Sign Release | |
| # Builds the versioned tar.zst from the tagged commit, signs it with | |
| # cosign keyless (GitHub OIDC — no stored secrets), and uploads the 4 | |
| # required signed assets to the GitHub release. | |
| # | |
| # Trigger options: | |
| # 1. Automatic: fires on `release: types: [published]` for any uds-v* | |
| # 2. Manual: workflow_dispatch with `tag_name` input (for backfilling) | |
| # | |
| # 5-asset output pattern (matches what `gh release upload` actually uploads): | |
| # a11oy-uds-<version>.tar.zst (zstd source archive) | |
| # a11oy-uds-<version>.tar.zst.sha256 (checksum) | |
| # a11oy-uds-<version>.tar.zst.sigstore.json (cosign bundle — use this to verify) | |
| # a11oy-uds-<version>.tar.zst.sig (copy of the bundle; see note below) | |
| # a11oy-uds-dev.pub (keyless verification instructions) | |
| # | |
| # NOTE on the .sig sidecar: cosign emits the legacy bundle format | |
| # {base64Signature, cert, rekorBundle}; the extractor below looks for | |
| # messageSignature/verificationMaterial (absent in that format) and therefore | |
| # always falls through to copying the whole .sigstore.json bundle into .sig. | |
| # Verification MUST use --bundle <pkg>.sigstore.json (NOT --signature <pkg>.sig). | |
| # The .sig file is retained only for asset-shape parity with v0.1.0/v0.2.0. | |
| # | |
| # Cosign keyless verification: | |
| # cosign verify-blob \ | |
| # --certificate-identity-regexp "https://github.com/szl-holdings/a11oy/.github/workflows/uds-sign-release.yml.*" \ | |
| # --certificate-oidc-issuer https://token.actions.githubusercontent.com \ | |
| # --bundle a11oy-uds-<version>.tar.zst.sigstore.json \ | |
| # a11oy-uds-<version>.tar.zst | |
| # | |
| # Doctrine v7: no fake signatures, no fabricated assets. | |
| # | |
| # Satisfies FA-001 (founder-action release-signing) per the PhD Crypto | |
| # verdict (Finding E/F: live Sigstore Fulcio+Rekor keyless chain verified) | |
| # and the PhD Systems Scope-1 finding (signed deployable artifact at | |
| # uds-v* tags). This is the real verify step — NOT an `echo OK` stub | |
| # (cf. PhD Crypto Finding D1 on the slsa.yml no-op). | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| tag_name: | |
| description: 'Release tag to sign (e.g. uds-v0.3.0)' | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| jobs: | |
| build-and-sign: | |
| name: Build tar.zst, sign, upload | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # upload release assets | |
| id-token: write # cosign keyless OIDC token | |
| # Only run for uds-v* tags (ignore v1.0.0-alpha etc.) | |
| if: | | |
| (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'uds-v')) || | |
| (github.event_name == 'workflow_dispatch' && startsWith(inputs.tag_name, 'uds-v')) | |
| steps: | |
| - name: Resolve tag name | |
| id: tag | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| TAG="${{ inputs.tag_name }}" | |
| else | |
| TAG="${{ github.event.release.tag_name }}" | |
| fi | |
| # Extract version: uds-v0.3.0 -> 0.3.0 | |
| VERSION="${TAG#uds-v}" | |
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "tarball=a11oy-uds-${VERSION}.tar.zst" >> "$GITHUB_OUTPUT" | |
| echo "sha256=a11oy-uds-${VERSION}.tar.zst.sha256" >> "$GITHUB_OUTPUT" | |
| echo "sig=a11oy-uds-${VERSION}.tar.zst.sig" >> "$GITHUB_OUTPUT" | |
| echo "bundle=a11oy-uds-${VERSION}.tar.zst.sigstore.json" >> "$GITHUB_OUTPUT" | |
| echo "pubkey=a11oy-uds-dev.pub" >> "$GITHUB_OUTPUT" | |
| - name: Checkout tag | |
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| ref: ${{ steps.tag.outputs.tag }} | |
| fetch-depth: 0 | |
| - name: Install Syft (for embedded SBOM) | |
| run: | | |
| curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh \ | |
| | sh -s -- -b /usr/local/bin v1.44.0 | |
| syft version | |
| - name: Build tar.zst with embedded Syft SBOM | |
| run: | | |
| TAG="${{ steps.tag.outputs.tag }}" | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| TARBALL="${{ steps.tag.outputs.tarball }}" | |
| echo "Building ${TARBALL} from ${TAG} (SBOM embedded inside the tarball)..." | |
| # 1) Materialise the exact source tree that ships in the tarball. | |
| STAGE="$(mktemp -d)" | |
| PREFIX="a11oy-uds-${VERSION}" | |
| git archive --format=tar --prefix="${PREFIX}/" HEAD | tar -x -C "${STAGE}" | |
| # 2) Generate the SBOM over that materialised tree, in BOTH | |
| # CycloneDX and SPDX JSON, per SLSA L1 supply-chain evidence. | |
| mkdir -p "${STAGE}/${PREFIX}/deploy" | |
| syft "dir:${STAGE}/${PREFIX}" \ | |
| -o cyclonedx-json="${STAGE}/${PREFIX}/deploy/sbom.cyclonedx.json" \ | |
| -o spdx-json="${STAGE}/${PREFIX}/deploy/sbom.spdx.json" | |
| echo "SBOMs embedded at ${PREFIX}/deploy/:" | |
| ls -la "${STAGE}/${PREFIX}/deploy/" | |
| # 3) Re-archive the tree (now INCLUDING the SBOMs) and compress. | |
| # The cosign signature computed later therefore covers the SBOM. | |
| tar -C "${STAGE}" -cf - "${PREFIX}" | zstd -19 -T0 -o "${TARBALL}" | |
| rm -rf "${STAGE}" | |
| echo "Built ${TARBALL}: $(wc -c < "${TARBALL}") bytes (includes deploy/sbom.cyclonedx.json + deploy/sbom.spdx.json)" | |
| - name: Compute sha256 | |
| run: | | |
| SHA256="${{ steps.tag.outputs.sha256 }}" | |
| TARBALL="${{ steps.tag.outputs.tarball }}" | |
| sha256sum "${TARBALL}" > "${SHA256}" | |
| echo "sha256 checksum:" | |
| cat "${SHA256}" | |
| - name: Install cosign | |
| uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 | |
| - name: Sign with cosign keyless (GitHub OIDC) | |
| env: | |
| COSIGN_EXPERIMENTAL: "1" | |
| run: | | |
| TARBALL="${{ steps.tag.outputs.tarball }}" | |
| BUNDLE="${{ steps.tag.outputs.bundle }}" | |
| # Keyless signing — uses GitHub OIDC token, no stored private key needed. | |
| # Verification identity: the workflow URL + OIDC issuer. | |
| cosign sign-blob \ | |
| --yes \ | |
| --bundle "${BUNDLE}" \ | |
| "${TARBALL}" | |
| echo "Signed. Bundle written to ${BUNDLE}" | |
| ls -la "${BUNDLE}" | |
| - name: Extract raw sig from bundle (for .sig sidecar) | |
| run: | | |
| BUNDLE="${{ steps.tag.outputs.bundle }}" | |
| SIG="${{ steps.tag.outputs.sig }}" | |
| # Extract the base64 signature from the Sigstore bundle | |
| python3 -c " | |
| import json, sys | |
| with open('${BUNDLE}') as f: | |
| b = json.load(f) | |
| # Try messageSignature first, then dsseEnvelope | |
| sig = (b.get('messageSignature', {}).get('signature') | |
| or b.get('verificationMaterial', {}).get('content', '')) | |
| if not sig: | |
| # Fallback: write bundle itself as the sig file | |
| print(open('${BUNDLE}').read(), end='') | |
| sys.exit(0) | |
| print(sig, end='') | |
| " > "${SIG}" || cp "${BUNDLE}" "${SIG}" | |
| echo "sig file written: $(wc -c < "${SIG}") bytes" | |
| - name: Generate keyless pubkey placeholder | |
| run: | | |
| PUBKEY="${{ steps.tag.outputs.pubkey }}" | |
| TAG="${{ steps.tag.outputs.tag }}" | |
| # For keyless signing there is no traditional pub key. | |
| # The verification identity is the workflow URL + OIDC issuer. | |
| # We write a verification instructions file instead of a raw EC public key. | |
| cat > "${PUBKEY}" <<'PUBKEYEOF' | |
| # a11oy-uds keyless verification (cosign keyless / Sigstore Fulcio) | |
| # | |
| # This release uses keyless cosign signing. There is no stored private key. | |
| # Verify with: | |
| # | |
| # cosign verify-blob \ | |
| # --certificate-identity-regexp \ | |
| # "https://github.com/szl-holdings/a11oy/.github/workflows/uds-sign-release.yml.*" \ | |
| # --certificate-oidc-issuer https://token.actions.githubusercontent.com \ | |
| # --bundle a11oy-uds-VERSION.tar.zst.sigstore.json \ | |
| # a11oy-uds-VERSION.tar.zst | |
| # | |
| # The signing certificate and transparency log entry are embedded in the | |
| # .sigstore.json bundle attached to this release. | |
| PUBKEYEOF | |
| echo "pubkey placeholder written" | |
| - name: Verify signature (self-check) | |
| env: | |
| COSIGN_EXPERIMENTAL: "1" | |
| run: | | |
| TARBALL="${{ steps.tag.outputs.tarball }}" | |
| BUNDLE="${{ steps.tag.outputs.bundle }}" | |
| cosign verify-blob \ | |
| --certificate-identity-regexp \ | |
| "https://github.com/szl-holdings/a11oy/.github/workflows/uds-sign-release.yml.*" \ | |
| --certificate-oidc-issuer https://token.actions.githubusercontent.com \ | |
| --bundle "${BUNDLE}" \ | |
| "${TARBALL}" \ | |
| && echo "Self-verification PASSED" | |
| - name: Upload signed assets to GitHub release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TAG="${{ steps.tag.outputs.tag }}" | |
| TARBALL="${{ steps.tag.outputs.tarball }}" | |
| SHA256="${{ steps.tag.outputs.sha256 }}" | |
| SIG="${{ steps.tag.outputs.sig }}" | |
| BUNDLE="${{ steps.tag.outputs.bundle }}" | |
| PUBKEY="${{ steps.tag.outputs.pubkey }}" | |
| echo "Uploading assets to release ${TAG}..." | |
| gh release upload "${TAG}" \ | |
| "${TARBALL}" \ | |
| "${SHA256}" \ | |
| "${SIG}" \ | |
| "${BUNDLE}" \ | |
| "${PUBKEY}" \ | |
| --clobber \ | |
| --repo szl-holdings/a11oy | |
| echo "Upload complete." | |
| - name: Print verification instructions | |
| run: | | |
| TAG="${{ steps.tag.outputs.tag }}" | |
| VERSION="${{ steps.tag.outputs.version }}" | |
| echo "" | |
| echo "=== Verification Instructions ===" | |
| echo "BASE=https://github.com/szl-holdings/a11oy/releases/download/${TAG}" | |
| echo "" | |
| echo "curl -fsSLO \${BASE}/a11oy-uds-${VERSION}.tar.zst" | |
| echo "curl -fsSLO \${BASE}/a11oy-uds-${VERSION}.tar.zst.sha256" | |
| echo "curl -fsSLO \${BASE}/a11oy-uds-${VERSION}.tar.zst.sigstore.json" | |
| echo "" | |
| echo "sha256sum -c a11oy-uds-${VERSION}.tar.zst.sha256" | |
| echo "" | |
| echo "cosign verify-blob \\" | |
| echo " --certificate-identity-regexp \\" | |
| echo " 'https://github.com/szl-holdings/a11oy/.github/workflows/uds-sign-release.yml.*' \\" | |
| echo " --certificate-oidc-issuer https://token.actions.githubusercontent.com \\" | |
| echo " --bundle a11oy-uds-${VERSION}.tar.zst.sigstore.json \\" | |
| echo " a11oy-uds-${VERSION}.tar.zst" | |