fix(ci): reuse product CI provenance across metadata descendants #46
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: SpecSync trusted lifecycle policy | |
| on: | |
| pull_request_target: | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: specsync-trusted-policy-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| policy: | |
| name: Inspect trusted lifecycle policy | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Check out the immutable base workflow revision | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| ref: ${{ github.workflow_sha }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Verify the candidate without checking it out | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| TRUSTED_WORKFLOW_SHA: ${{ github.workflow_sha }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [[ ! "$BASE_SHA" =~ ^[0-9a-f]{40}$ \ | |
| || ! "$HEAD_SHA" =~ ^[0-9a-f]{40}$ \ | |
| || ! "$TRUSTED_WORKFLOW_SHA" =~ ^[0-9a-f]{40}$ \ | |
| || ! "$PR_NUMBER" =~ ^[0-9]+$ ]]; then | |
| echo "The pull-request target payload contains an invalid immutable identity." >&2 | |
| exit 1 | |
| fi | |
| if [[ "$(git rev-parse HEAD)" != "$TRUSTED_WORKFLOW_SHA" ]]; then | |
| echo "The trusted checkout does not match github.workflow_sha." >&2 | |
| exit 1 | |
| fi | |
| if [[ "$BASE_SHA" != "$TRUSTED_WORKFLOW_SHA" ]]; then | |
| echo "The PR base and trusted workflow revision are not the same commit." >&2 | |
| exit 1 | |
| fi | |
| git fetch --no-tags --no-recurse-submodules origin \ | |
| "+refs/pull/${PR_NUMBER}/head:refs/specsync/candidate" | |
| if [[ "$(git rev-parse refs/specsync/candidate)" != "$HEAD_SHA" ]]; then | |
| echo "Fetched candidate object does not match the event head SHA." >&2 | |
| exit 1 | |
| fi | |
| changed="${RUNNER_TEMP}/specsync-trusted-policy-paths.txt" | |
| git diff --name-only --no-renames -z "$BASE_SHA" "$HEAD_SHA" > "$changed" | |
| protected="${RUNNER_TEMP}/specsync-protected-policy-paths.txt" | |
| python3 - "$changed" "$protected" <<'PY' | |
| import pathlib | |
| import re | |
| import sys | |
| pattern = re.compile(rb'^(fledge\.toml|action\.(yml|yaml)|\.github/workflows/[^/]*\.(yml|yaml)|\.github/actions(/[^/]*)*|\.github/scripts/(classify-ci-paths|test-classify-ci-paths|test-lifecycle-workflows)\.sh|\.github/scripts/(lifecycle-validation-limits\.json|(test-)?verify-(archive-introduction|trusted-policy-check)\.py|(test-)?validate-release-candidate\.py)|\.specsync/workflow-v2-baseline\.json)$') | |
| paths = pathlib.Path(sys.argv[1]).read_bytes().split(b"\0") | |
| protected = [path for path in paths if path and pattern.fullmatch(path)] | |
| payload = b"\0".join(protected) + (b"\0" if protected else b"") | |
| pathlib.Path(sys.argv[2]).write_bytes(payload) | |
| PY | |
| if [[ -s "$protected" ]]; then | |
| echo "This PR changes the SHA-pinned SpecSync trust policy:" >&2 | |
| python3 - "$protected" <<'PY' >&2 | |
| import pathlib | |
| import sys | |
| for path in pathlib.Path(sys.argv[1]).read_bytes().split(b"\0"): | |
| if path: | |
| print(f" - {path!r}") | |
| PY | |
| echo "Policy changes require a separately pinned GitHub required-workflow update; optimized lifecycle reuse is forbidden." >&2 | |
| exit 1 | |
| fi | |
| echo "Trusted lifecycle policy revision: ${TRUSTED_WORKFLOW_SHA}" | |
| echo "Candidate inspected as Git objects only: ${HEAD_SHA}" | |
| echo "No protected lifecycle policy file changed." | |
| publish: | |
| name: Publish trusted lifecycle policy result | |
| needs: policy | |
| if: always() | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: read | |
| checks: write | |
| steps: | |
| - name: Publish the exact revision-bound check | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPOSITORY: ${{ github.repository }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| POLICY_RESULT: ${{ needs.policy.result }} | |
| TRUSTED_WORKFLOW_SHA: ${{ github.workflow_sha }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| conclusion=failure | |
| title="Trusted lifecycle policy blocked" | |
| if [[ "$POLICY_RESULT" == "success" ]]; then | |
| conclusion=success | |
| title="Trusted lifecycle policy unchanged" | |
| fi | |
| summary="$( | |
| printf '%s\n' \ | |
| "PR #${PR_NUMBER} candidate: \`${HEAD_SHA}\`" \ | |
| "Base commit: \`${BASE_SHA}\`" \ | |
| "Trusted workflow revision: \`${TRUSTED_WORKFLOW_SHA}\`" \ | |
| "Policy inspection: \`${POLICY_RESULT}\`" | |
| )" | |
| payload="${RUNNER_TEMP}/specsync-trusted-policy-check.json" | |
| jq -n \ | |
| --arg name "SpecSync trusted policy" \ | |
| --arg head_sha "$HEAD_SHA" \ | |
| --arg external_id "specsync-trusted-policy:${TRUSTED_WORKFLOW_SHA}:${HEAD_SHA}" \ | |
| --arg details_url "$RUN_URL" \ | |
| --arg conclusion "$conclusion" \ | |
| --arg title "$title" \ | |
| --arg summary "$summary" \ | |
| '{ | |
| name: $name, | |
| head_sha: $head_sha, | |
| status: "completed", | |
| conclusion: $conclusion, | |
| external_id: $external_id, | |
| details_url: $details_url, | |
| output: {title: $title, summary: $summary} | |
| }' > "$payload" | |
| gh api --method POST \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| "repos/${REPOSITORY}/check-runs" \ | |
| --input "$payload" | |
| if [[ "$conclusion" != "success" ]]; then | |
| exit 1 | |
| fi |