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
14 changes: 7 additions & 7 deletions .castiron.stats.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
schema_version: 1
generation_id: 158c74da-f640-42bc-9051-dc5c15f35612
openapi_spec_hash: e81fa0fd2133715dabef9f00a42557a9
openapi_transformed_spec_hash: bea7a30d23deed3fc8f1c6413a38d354
config_hash: c9c8b98008c6a5069191c1f38273d8ca
codegen_sha: 9144bd2d5b14c91d677edca7a1757c32af1e1baa
codegen_hash: ad31b796363b7ccd1828db887233e9f5090e1cd768c6b743a41b208ad4a447cb
public_codegen_sha: c22f6d6207a8f4b7a456392c48c283ed71e39164
generation_id: 7c22cf25-abcc-4a38-a881-75ab8f35f4f3
openapi_spec_hash: 437dce4de9f089780656c33ebc51630a
openapi_transformed_spec_hash: 3cf328f9ae72ee682ef1a9fd336c38c3
config_hash: 2f0a7ff931fec8c1d94b9556ddf66fdd
codegen_sha: 5597f5eb9ca6f53bddcf09ef58bd7073b44eeb42
codegen_hash: b596892647a3dd6ae9474fda30191de9138d733a7a94b65adf5e36935c7a5da0
public_codegen_sha: d61f6590fce730f6a08c710f15f706e6778486f5
61 changes: 51 additions & 10 deletions .github/workflows/castiron-custom-code-comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,22 @@ jobs:
number: ${{ steps.report.outputs.number }}
artifact-id: ${{ steps.artifact.outputs.artifact-id }}
artifact-run-attempt: ${{ github.run_attempt }}
target-branch: ${{ steps.budget.outputs.target_branch }}
policy-sha: ${{ steps.budget.outputs.policy_sha }}
base-sha: ${{ steps.budget.outputs.base_sha }}
head-sha: ${{ steps.budget.outputs.head_sha }}
isolation: ${{ steps.budget.outputs.isolation }}
budget: ${{ steps.budget.outputs.budget }}
steps:
# Selecting main here pins both the executable checker and PR policy base.
# Subsequent steps use this checkout's SHA even if main advances.
# Selecting main here pins executable checker code and budget policy.
# The reporter separately captures the live main/next target baseline.
- name: Check out the trusted reporter
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
persist-credentials: false

- name: Compute from the selected main and pull request Git objects
- name: Compute from trusted main and target branch Git objects
id: report
if: github.event.workflow_run.event == 'pull_request'
env:
Expand Down Expand Up @@ -133,6 +135,8 @@ jobs:
- name: Publish exact-head statuses after checking freshness
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
TARGET_BRANCH: ${{ needs.compute.outputs.target-branch }}
POLICY_SHA: ${{ needs.compute.outputs.policy-sha }}
BASE_SHA: ${{ needs.compute.outputs.base-sha }}
HEAD_SHA: ${{ needs.compute.outputs.head-sha }}
ISOLATION_RESULT: ${{ needs.compute.outputs.isolation }}
Expand All @@ -148,7 +152,10 @@ jobs:
const head = run.head_sha;
if (!/^[0-9a-f]{40}$/.test(head)) throw new Error('Invalid candidate SHA');
const base = process.env.BASE_SHA;
let fresh = /^[0-9a-f]{40}$/.test(base) && head === process.env.HEAD_SHA;
const policy = process.env.POLICY_SHA;
const evaluatedBranch = process.env.TARGET_BRANCH;
let branch, number;
let fresh = /^[0-9a-f]{40}$/.test(base) && /^[0-9a-f]{40}$/.test(policy) && head === process.env.HEAD_SHA;
if (run.event === 'pull_request') {
let pulls = run.pull_requests.length ? run.pull_requests : await github.paginate(
github.rest.repos.listPullRequestsAssociatedWithCommit, {...context.repo, commit_sha: head});
Expand All @@ -159,15 +166,20 @@ jobs:
for (const pull of pulls) {
const {data: pr} = await github.rest.pulls.get({...context.repo, pull_number: pull.number});
if (pr.state === 'open' && pr.head.sha === head &&
pr.base.ref === 'main' && pr.base.repo.full_name === `${context.repo.owner}/${context.repo.repo}`) current.push(pr);
['main', 'next'].includes(pr.base.ref) && pr.base.repo.full_name === `${context.repo.owner}/${context.repo.repo}`) current.push(pr);
}
if (current.length !== 1) return;
} else if (run.event !== 'merge_group' || !run.head_branch.startsWith('gh-readonly-queue/main/')) {
return;
branch = current[0].base.ref;
number = current[0].number;
} else {
const {data: main} = await github.rest.git.getRef({...context.repo, ref: 'heads/main'});
fresh = fresh && base === main.object.sha;
const match = run.head_branch.match(/^gh-readonly-queue\/(main|next)\//);
if (run.event !== 'merge_group' || !match) return;
branch = match[1];
}
// These required contexts belong to main. Next reports are advisory:
// GitHub does not rebuild its queue when main's policy changes.
if (branch !== 'main') return;
fresh = fresh && evaluatedBranch === branch;
const url = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
// Concurrency serializes writes, but does not order runs or retries.
// Keep an older evaluation from overwriting a newer result for this head.
Expand All @@ -183,13 +195,42 @@ jobs:
const different = previous.findIndex((value, index) => value !== order[index]);
if (different !== -1 && previous[different] > order[different]) return;
}
// Recheck after pagination, immediately before attaching statuses.
if (run.event === 'pull_request') {
const {data: pr} = await github.rest.pulls.get({...context.repo, pull_number: number});
if (pr.state !== 'open' || pr.head.sha !== head || pr.base.ref !== branch ||
pr.base.repo.full_name !== `${context.repo.owner}/${context.repo.repo}`) return;
} else {
// A removed/rebuilt queue entry must not receive a passing result.
let cursor = null, member = false;
do {
const response = await github.graphql(`query($owner:String!,$name:String!,$branch:String!,$cursor:String) {
repository(owner:$owner,name:$name) { mergeQueue(branch:$branch) {
entries(first:100,after:$cursor) {
nodes { headCommit { oid } }
pageInfo { hasNextPage endCursor }
}
} }
}`, {owner: context.repo.owner, name: context.repo.repo, branch, cursor});
const entries = response.repository.mergeQueue?.entries;
if (!entries) break;
member = entries.nodes.some(entry => entry.headCommit?.oid === head);
if (member || !entries.pageInfo.hasNextPage) break;
const next = entries.pageInfo.endCursor;
if (!next || next === cursor) throw new Error('Invalid queue pagination');
cursor = next;
} while (true);
const {data: target} = await github.rest.git.getRef({...context.repo, ref: `heads/${branch}`});
fresh = fresh && base === target.object.sha && policy === target.object.sha;
fresh = fresh && member;
}
for (const [name, result] of [
['Castiron / budget-only change', process.env.ISOLATION_RESULT],
['Castiron / custom-code budget', process.env.BUDGET_RESULT],
]) {
const state = fresh && result === 'success' ? 'success' : 'failure';
const description = !fresh ? 'Evaluation unavailable or queue base changed; inspect the trusted run and rerun.'
: `${state === 'success' ? 'Passed' : 'Failed'} against main ${base.slice(0, 12)}. See the trusted run summary.`;
: `${state === 'success' ? 'Passed' : 'Failed'}: ${branch} ${base.slice(0, 12)}, policy ${policy.slice(0, 12)}.`;
await github.rest.repos.createCommitStatus({...context.repo, sha: head, context: name,
state, description: `${description} ${marker}`, target_url: url});
}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/castiron-custom-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ concurrency:
cancel-in-progress: false

env:
REPORTER_SHA256: 249f5d2f90f1c66d9ac143c875a3ceac4b0edbce4e67fd0e84524f239d50315c
REPORTER_SHA256: 157ed30ac9d54d20fe5f7e625dc8ee5958beb91cc7e30aaa97cfba8fb3e06e21

jobs:
queue-signal:
Expand Down
Loading
Loading