Repository navigation
ci(security): regenerate the approved publish-workflow revisions on a schedule - #3568
Conversation
… schedule Add a daily / on-demand workflow that runs the approved-revision generator against prod with a least-privilege App token and, when the committed set has moved, re-reads it through the approved-revisions guard and opens (or updates) ONE draft pull request from a fixed branch carrying only the regenerated data file. A consumer that cannot be resolved is a red run with nothing committed, never a pull request that dropped it. A contract test pins the workflow's shape — schedule + dispatch, empty ambient grant, queue: single, environment prod, the generator pinned to admin@prod on the App token, exactly actions=read/contents=write/ pull-requests=write minted, persist-credentials off, a fixed draft branch with an API-signed commit limited to the data file, no continue-on-error, and generator -> guard -> pull request ordering — with 20 ablations that each trip exactly their own assertion. It is wired into the validate job, and both this pair and the generator pair join the k8s change filter so a PR editing either half alone still runs the proof. Part of #3308 (third child, #3552). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
I repaired the current PR head after its required CI failed on three ShellCheck SC2015 findings in Fresh CI is running on |
Automated user evaluation on exact head @coderabbitai review |
|
|
|
Warning Review limit reachedNext included review available in 10 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Team Run ID: 📒 Files selected for processing (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CodeRabbit explicitly refused the exact-head request as rate limited. Automated user evaluation remains unchanged on @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1e67c46b3e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
…own base Four review findings, each a way the daily run would have gone red or opened the wrong pull request, with an ablated contract assertion pinning every one. - App token: create-github-app-token given neither `owner` nor `repositories` mints a token scoped to the repository the job runs in. The generator reads each consumer's cd.yaml and CD runs on the consumer's own repository, so every read would 404 and the generator would refuse on the pin half. Scope the token to this repository plus the consumers the approved set names. - Concurrency and base: the pushed branch is fixed, so a dispatch from another ref and the scheduled run contend for the same pull request; a ref-keyed group put them in different groups. Make the group ref-independent and pin the pull request's base so a dispatch cannot target its own ref. - Disclosure: the action supplies the complete body, so every pull request it opened lacked the attribution line AGENTS.md requires. - Stable API endpoint: KUBE_CONFIG can name a control-plane node that `cluster update` later rolls, so reading OCIRepositories through it fails against a healthy cluster. Resolve the floating IP before any API call, the same ordering the deploy-prod composite uses. The contract test gains A10-A13 with ten ablations. A10 derives the expected token scope from the generated set, so CI's path filter now also watches that file: without it, adding a consumer — the exact change that would strand the token — would not run the check that catches it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI's shellcheck runs at its default severity and refused the `for c in $(…)` form (SC2013), which my local `-S warning` pass had suppressed. The obvious rewrite — a `while read` inside a command substitution — is worse than the lint: the `case` pattern's own `)` closes the substitution early, so the loop's source text was captured as the result and A10 failed the control naming shell code as a missing consumer. Measured, not predicted. Reads the consumers one per line and keeps the loop in the current shell via a here-string, so nothing is split on whitespace, nothing runs in a subshell, and no `case` sits inside a substitution. All 30 ablations still pass, including the one that drops a consumer from the token scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai review |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Pull request metadata =='
gh pr view 3568 --repo devantler-tech/platform \
--json number,title,headRefName,headRefOid,baseRefName,files \
--jq '{number, title, headRefName, headRefOid, baseRefName, files: [.files[].path]}'
printf '%s\n' '== Checked-out commit =='
git rev-parse HEAD
printf '%s\n' '== Changed files relative to merge base =='
BASE="$(git merge-base HEAD origin/main)"
printf 'merge-base=%s\n' "$BASE"
git diff --name-status "$BASE"...HEAD
printf '%s\n' '== Structure of changed shell and workflow files =='
while IFS= read -r file; do
case "$file" in
*.sh) ast-grep outline "$file" --items all || true ;;
*.yml|*.yaml) sed -n '1,260p' "$file" ;;
esac
done < <(git diff --name-only "$BASE"...HEAD)Length of output: 1974 🏁 Script executed: #!/bin/bash
set -euo pipefail
BASE="$(git merge-base HEAD origin/main)"
printf '%s\n' '== Complete merge-base diff =='
git diff --find-renames --find-copies --no-ext-diff "$BASE"...HEAD -- \
.github/workflows/ci.yaml \
.github/workflows/regenerate-publish-workflow-approved-revisions.yaml \
scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh
printf '%s\n' '== Shell test outline =='
ast-grep outline scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh --items all || true
printf '%s\n' '== Related workflow/data references =='
rg -n -C 4 --hidden \
-g '*.yaml' -g '*.yml' -g '*.sh' -g '!**/.git/**' \
'approved[-_ ]revisions|publish-workflow|cosign|workflow_ref' \
.github scriptsLength of output: 206706 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Existing queue: single usage =='
rg -n -C 8 --hidden -g '*.yaml' -g '*.yml' -g '!**/.git/**' \
'^[[:space:]]*queue:[[:space:]]*single[[:space:]]*$|^[[:space:]]*concurrency:' \
.github
printf '%s\n' '== Workflow validation-related configuration =='
fd -HI -t f 'actionlint|workflow|github' . \
| sed -n '1,160p'
printf '%s\n' '== create-pull-request references and workflow pins =='
rg -n -C 3 --hidden -g '*.yaml' -g '*.yml' -g '!**/.git/**' \
'peter-evans/create-pull-request@|sign-commits:|branch-token:' \
.githubLength of output: 23984 🤖 web_search executed:
Length of output: 18060 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Requested head and whitespace =='
test "$(git rev-parse HEAD)" = '7e9c56473633c5fdb9412f2ce8a44388820563fe'
git diff --check "$(git merge-base HEAD origin/main)"...HEAD
printf '%s\n' '== Parse workflow structure =='
yq -e '
.name == "Regenerate Approved Publish Revisions" and
.concurrency.group == "${{ github.workflow }}" and
.concurrency.queue == "single" and
.jobs.regenerate.environment == "prod" and
(.jobs.regenerate.steps | length) > 0
' .github/workflows/regenerate-publish-workflow-approved-revisions.yaml
printf '%s\n' '== Local actionlint configuration =='
cat .github/actionlint.yaml
printf '%s\n' '== Static lint of changed shell test, if tool is available =='
if command -v shellcheck >/dev/null 2>&1; then
shellcheck scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh
else
printf '%s\n' 'shellcheck is unavailable in the inspection environment'
fi
printf '%s\n' '== Pull request check summary =='
gh pr checks 3568 --repo devantler-tech/platform --json name,state,workflow,link \
--jq '.[] | [.workflow, .name, .state, .link] | `@tsv`' || trueLength of output: 1557 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '== Local actionlint configuration =='
cat .github/actionlint.yaml
printf '%s\n' '== Available static linters =='
command -v shellcheck || true
command -v actionlint || true
printf '%s\n' '== Static lint of changed shell test =='
if command -v shellcheck >/dev/null 2>&1; then
shellcheck scripts/tests/test-regenerate-publish-workflow-approved-revisions.sh
else
printf '%s\n' 'shellcheck is unavailable in the inspection environment'
fi
printf '%s\n' '== Static lint of changed workflow =='
if command -v actionlint >/dev/null 2>&1; then
actionlint .github/workflows/regenerate-publish-workflow-approved-revisions.yaml
else
printf '%s\n' 'actionlint is unavailable in the inspection environment'
fi
printf '%s\n' '== Pull request check summary =='
gh pr checks 3568 --repo devantler-tech/platform --json name,state,workflow,link \
--jq '.[] | [.workflow, .name, .state, .link] | `@tsv`' || trueLength of output: 5791
I found no blocking findings. The workflow uses a fixed pull-request branch and The contract test has one control and 30 ablations. ShellCheck and Actionlint passed in the inspection environment. You are interacting with an AI system. |
CodeRabbit answered this head as chat, not a review — the reply carries no Advancing the lane rather than spending a deeper CodeRabbit pass here: Codex already reviewed this PR at @codex review |
Both sides added to the same two places in ci.yaml, so the resolution keeps both: #3565's OCIRepository verify step and its path-filter pair alongside this branch's regeneration-workflow validation step and its filter entries. Nothing from either side is dropped — verified by grepping for each step name and each filter path after resolving. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7e9c564736
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The tenants guide said the App token is minted from an org-level `APP_ID`
variable. Every workflow in the org that mints one passes
`client-id: ${{ vars.APP_CLIENT_ID }}` with `secrets.APP_PRIVATE_KEY` —
devantler-tech/actions' own template-sync, agent-plugins' update-agent-skills,
and ksail's cd — and that path demonstrably works: agent-plugins commit
744f0e06 was authored by the resulting App identity today.
The stale name is not merely untidy. Review on this PR read it as authoritative
and raised the workflow's correct `APP_CLIENT_ID` as a P1 that would break every
run, so the doc was actively generating false findings against correct code.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The step did not fail; it was killed by the 10-minute action limit mid-render, with every file it had reached logged
This PR touches a workflow file, a path filter, a shell contract test and one documentation line — none of which can reach Helm rendering. Re-running the failed job once. If it times out again, that is a different finding: the job would then be genuinely near its ceiling and the fix is the job's own budget, not this PR. |
The timeout was a transient, as diagnosed: the re-run is 21/21 green with no change to the tree, which is the falsifier I named coming out on the "transient" side rather than the "job near its ceiling" side. Re-requesting at the current head. Two pushes have landed since the review at @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d94f8cc708
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ted set Three review findings, each with an ablated assertion. - ONE token became TWO. An installation token carries one permission set across every repository it names, so scoping a single token to this repository plus the four consumers granted contents/pull-requests WRITE over `.github`, `ascoachingogvaner`, `aws` and `wedding-app` — two of them private — when the generator only ever reads them. That widening arrived with the previous commit and is undone here: a read-only consumer token (contents+actions read) for the generator, and a platform-only write token for the checkout and the pull request. A10 now also refuses a consumer appearing in the WRITE list. - The checkout is pinned to `main`. `base:` chooses the pull request's target but does not remove the dispatched ref's commits from the fixed regeneration branch, so a manual dispatch from a topic branch would carry them into the automation's pull request. - The pull-request step is no longer gated on `moved`. A reverted consumer rollout returns the set to the committed one, so a gated step skipped and left the earlier run's pull request standing with the superseded set, still mergeable. devantler-tech/actions' sync-cluster-policies drives this same action at this same pinned revision ungated, for the same reason. A failure still stops it — there is no `if: always()`, which A9 pins. Nine new ablations; 39 in total. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All three findings from the last round are fixed and their threads resolved; CI is 21/21 green at this head and Staying on this lane rather than restarting at CodeRabbit, on measured evidence rather than preference: on this repository it acked a full review at @codex review |
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Readiness at
|
Why
Every consumer's pin on a shared publish workflow moves with each
devantler-tech/actionsbump, several times a month. The approved-revision set the cosign matchers will be narrowed from is stale the moment that happens, and regenerating it by hand would turn every dependency bump into a two-repository ordered rollout. Until the set regenerates itself, the narrowing in #3308 is not safe to switch on.What
A daily and on-demand workflow regenerates the set against prod and, when it has moved, opens or updates a single draft pull request carrying only the regenerated file. A consumer it cannot resolve is a red run and never a pull request that dropped that consumer. A contract test pins the workflow's safety properties, each with an ablation, and runs on every PR that touches the workflow, the test, the generator or its test.
The matchers themselves are not rewritten here: that is the narrowing switch, which follows only after this workflow has run green at least once (feature-flag-first, generated file first).
Part of #3308 · Fixes #3552