Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/maintenance-implementation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ jobs:
permissions:
contents: write
pull-requests: write
checks: read
checks: write
actions: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/maintenance-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ permissions:
contents: write
issues: write
actions: write
checks: read
checks: write
pull-requests: write

concurrency:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/maintenance-watch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ jobs:
actions: write
artifact-metadata: write
attestations: write
checks: read
checks: write
contents: write
id-token: write
pull-requests: write
Expand Down
5 changes: 3 additions & 2 deletions docs/repository-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ Required repository state:
remains owner-only except for the deterministic evidence-state proof above.
- Because GitHub suppresses ordinary PR events created by `GITHUB_TOKEN`, each
deterministic PR coordinator explicitly dispatches `ci.yml` and
`protected-controls.yml` at the exact PR branch, then accepts only newly
created successful check runs for that head SHA.
`protected-controls.yml` at the exact PR branch, accepts only newly created
successful validator runs for that head SHA, and only then publishes the
Actions-owned required checks onto the PR rollup with the validator URLs.
- Allow GitHub-owned Actions plus only `openai/codex-action` and
`jdx/mise-action`, and require every Action reference to use a full commit
SHA.
Expand Down
38 changes: 37 additions & 1 deletion scripts/dispatch-pr-checks
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,46 @@ while ((SECONDS < deadline)); do
fi
if jq -e -s 'all(.[]; .status=="completed" and .conclusion=="success")' \
<<<"$primary"$'\n'"$protected" >/dev/null; then
publish_gate() {
local validator="$1"
local name details request
name="$(jq -r .name <<<"$validator")"
details="$(jq -r .details_url <<<"$validator")"
request="$(mktemp)"
jq -n \
--arg name "$name" \
--arg head "$head_sha" \
--arg details "$details" \
--arg summary "Exact-head validator passed: $details" \
'{name:$name,head_sha:$head,status:"completed",conclusion:"success",details_url:$details,output:{title:"Dispatched validator passed",summary:$summary}}' \
> "$request"
gh api --method POST -H "Accept: application/vnd.github+json" \
"repos/$repository/check-runs" --input "$request"
rm -f "$request"
}

# workflow_dispatch runs validate the exact commit but GitHub does not
# attach them to a PR rollup. Publish Actions-owned gate check runs only
# after both new validator runs pass, retaining their URLs as evidence.
primary_gate="$(publish_gate "$primary")"
protected_gate="$(publish_gate "$protected")"
for _ in {1..24}; do
rollup="$(gh pr checks "$pr_number" --repo "$repository" --required \
--json name,bucket,link 2>/dev/null || true)"
if jq -e --arg primary "$primary_check" \
'([.[] | select(.name==$primary) | .bucket] | last) == "pass" and ([.[] | select(.name=="Protected controls") | .bucket] | last) == "pass"' \
<<<"$rollup" >/dev/null 2>&1; then
break
fi
sleep 5
done
jq -e --arg primary "$primary_check" \
'([.[] | select(.name==$primary) | .bucket] | last) == "pass" and ([.[] | select(.name=="Protected controls") | .bucket] | last) == "pass"' \
<<<"${rollup:-[]}" >/dev/null
mkdir -p "$(dirname "$output")"
jq -s --arg head "$head_sha" \
'map({name, bucket:"pass", link:.details_url, headSha:$head, checkRunId:.id})' \
<<<"$primary"$'\n'"$protected" > "$output"
<<<"$primary_gate"$'\n'"$protected_gate" > "$output"
exit 0
fi
fi
Expand Down
3 changes: 3 additions & 0 deletions tests/test_maintenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ def test_token_created_prs_explicitly_dispatch_required_checks(self):
self.assertIn("pr_number:", protected)
self.assertIn("gh workflow run ci.yml", dispatcher)
self.assertIn("gh workflow run protected-controls.yml", dispatcher)
self.assertIn('"repos/$repository/check-runs"', dispatcher)
self.assertIn("Exact-head validator passed", dispatcher)
for workflow in (
"maintenance-watch.yml",
"maintenance-implementation.yml",
Expand All @@ -294,6 +296,7 @@ def test_token_created_prs_explicitly_dispatch_required_checks(self):
body = (root / ".github/workflows" / workflow).read_text()
self.assertIn("./scripts/dispatch-pr-checks", body)
self.assertNotIn("gh pr checks", body)
self.assertIn("checks: write", body)

def test_malformed_contract_shapes_fail_closed(self):
contract = self._contract()
Expand Down
Loading