From ff241ac9dc042dcc2f0ed70a0cfa199c9bbd8a9a Mon Sep 17 00:00:00 2001 From: Nathan Gillett Date: Sat, 30 May 2026 18:36:25 -0500 Subject: [PATCH] Restore spec release workflow for v0.1 tags Tag pushes run conformance tests, regenerate the integrity manifest, Cosign-sign, and publish the spec release bundle. --- .github/workflows/release-spec.yml | 252 +++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 .github/workflows/release-spec.yml diff --git a/.github/workflows/release-spec.yml b/.github/workflows/release-spec.yml new file mode 100644 index 0000000..ab2e03d --- /dev/null +++ b/.github/workflows/release-spec.yml @@ -0,0 +1,252 @@ +name: release spec + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + release_ref: + description: Git ref to build the release from. + required: true + default: refs/heads/main + type: string + release_version: + description: SemVer version for release metadata (without v prefix). + required: true + default: 0.0.0-dryrun + type: string + attest_to_rekor: + description: Upload Cosign signatures to the public Rekor log. + required: true + default: false + type: boolean + +permissions: + contents: write + id-token: write + +jobs: + test: + name: "IntentProof Release: Test Spec" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_ref || github.ref }} + + - uses: actions/setup-node@v6 + with: + node-version: '22' + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run spec verification + run: npm test + + generate: + name: "IntentProof Release: Generate Integrity Manifest" + needs: test + runs-on: ubuntu-latest + outputs: + release_ref: ${{ steps.release.outputs.release_ref }} + release_version: ${{ steps.release.outputs.release_version }} + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_ref || github.ref }} + + - uses: actions/setup-node@v6 + with: + node-version: '22' + cache: npm + + - name: Resolve release metadata + id: release + env: + INPUT_RELEASE_REF: ${{ inputs.release_ref }} + INPUT_RELEASE_VERSION: ${{ inputs.release_version }} + run: | + set -euo pipefail + semver_tag='^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$' + + if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then + release_ref="$INPUT_RELEASE_REF" + release_version="$INPUT_RELEASE_VERSION" + else + release_ref="${GITHUB_REF}" + release_version="${GITHUB_REF_NAME#v}" + if [[ ! "$release_ref" =~ $semver_tag ]]; then + echo "tag ref must match vMAJOR.MINOR.PATCH: ${release_ref}" >&2 + exit 1 + fi + fi + + { + echo "release_ref=${release_ref}" + echo "release_version=${release_version}" + } >> "$GITHUB_OUTPUT" + + - name: Install dependencies + run: npm ci + + - name: Generate and verify Ed25519-signed manifest + env: + SPEC_INTEGRITY_PRIVATE_KEY: ${{ secrets.SPEC_INTEGRITY_PRIVATE_KEY }} + run: | + set -euo pipefail + test -n "${SPEC_INTEGRITY_PRIVATE_KEY:-}" + npx ts-node integrity/generate_manifest.ts + npx ts-node integrity/verify_manifest.ts + + - uses: actions/upload-artifact@v7 + with: + name: spec-integrity-manifest + path: | + integrity/manifest.v1.json + integrity/manifest.v1.json.sig + well-known-keys/spec-integrity.pem + + cosign: + name: "IntentProof Release: Cosign Integrity Manifest" + needs: generate + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ needs.generate.outputs.release_ref }} + + - uses: actions/download-artifact@v8 + with: + name: spec-integrity-manifest + path: . + + - uses: sigstore/cosign-installer@v3 + + - name: Cosign-sign manifest + env: + ATTEST_TO_REKOR: ${{ github.event_name != 'workflow_dispatch' || inputs.attest_to_rekor }} + run: | + set -euo pipefail + manifest="integrity/manifest.v1.json" + test -f "$manifest" + tlog_args=() + if [[ "$ATTEST_TO_REKOR" != "true" ]]; then + tlog_args+=(--tlog-upload=false) + fi + cosign sign-blob --yes \ + "${tlog_args[@]}" \ + --bundle "${manifest}.cosign.sigstore.json" \ + --output-signature "${manifest}.cosign.sig" \ + "$manifest" + + - name: Verify Cosign signature + if: github.event_name != 'workflow_dispatch' || inputs.attest_to_rekor + run: | + set -euo pipefail + manifest="integrity/manifest.v1.json" + cosign verify-blob \ + --certificate-identity-regexp 'https://github.com/IntentProof/intentproof-spec/' \ + --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ + --bundle "${manifest}.cosign.sigstore.json" \ + --signature "${manifest}.cosign.sig" \ + "$manifest" + + - uses: actions/upload-artifact@v7 + with: + name: spec-integrity-cosign + path: | + integrity/manifest.v1.json.cosign.sig + integrity/manifest.v1.json.cosign.sigstore.json + + bundle: + name: "IntentProof Release: Build Spec Bundle" + needs: + - generate + - cosign + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ needs.generate.outputs.release_ref }} + + - uses: actions/download-artifact@v8 + with: + name: spec-integrity-manifest + path: . + + - uses: actions/download-artifact@v8 + with: + name: spec-integrity-cosign + path: integrity + + - name: Build reproducible spec tarball + env: + RELEASE_VERSION: ${{ needs.generate.outputs.release_version }} + run: | + set -euo pipefail + mkdir -p dist + tar \ + --sort=name \ + --mtime='UTC 1970-01-01' \ + --owner=0 \ + --group=0 \ + --numeric-owner \ + -czf "dist/intentproof-spec-${RELEASE_VERSION}.tar.gz" \ + schema \ + golden \ + compatibility \ + integrity/manifest.v1.json \ + integrity/manifest.v1.json.sig \ + integrity/manifest.v1.json.cosign.sig \ + integrity/manifest.v1.json.cosign.sigstore.json \ + well-known-keys/spec-integrity.pem + + - uses: actions/upload-artifact@v7 + with: + name: spec-release-bundle + path: dist/*.tar.gz + + upload-release: + name: "IntentProof Release: Publish Spec Release Artifacts" + needs: + - generate + - cosign + - bundle + if: github.event_name != 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/download-artifact@v8 + with: + name: spec-integrity-manifest + path: release-artifacts + + - uses: actions/download-artifact@v8 + with: + name: spec-integrity-cosign + path: release-artifacts/integrity + + - uses: actions/download-artifact@v8 + with: + name: spec-release-bundle + path: release-artifacts + + - name: Upload artifacts to GitHub Release + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + mapfile -t files < <(find release-artifacts -type f | sort) + test "${#files[@]}" -gt 0 + if ! gh release view "$RELEASE_TAG" >/dev/null 2>&1; then + gh release create "$RELEASE_TAG" --generate-notes + fi + gh release upload "$RELEASE_TAG" "${files[@]}" --clobber