Commit Graph
13 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 4.7 f286ce54dd fix(update/open_pr): scope dedup to changed files
The previous tree-hash dedup compared whole-commit trees, so unrelated
drift on main (e.g. a manual edit to any roles/* file) caused the same
auto-update delta to appear "new" each daily cron and a fresh PR to open.

Now: fingerprint only the files that this run actually committed
(git diff --name-only HEAD~1 HEAD), both locally and on each candidate
open PR with the same UPDATE_BRANCH_PREFIX. If any open PR carries the
identical content for those files, skip push + PR creation entirely.

Branch name now carries the fingerprint's short hash so parallel PRs
of the same class can coexist (the close-superseded loop is dropped).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 10:37:21 +02:00
kevinveenbirkenbachandClaude Opus 4.7 af628847bb fix(update/open_pr): delete head branch when closing superseded PRs
The previous gh pr close call left the bot's daily update branch
behind on the remote, so stale update/<prefix>-YYYYMMDD refs
accumulated indefinitely. --delete-branch removes the head ref on
close. Scope-safe because the loop only iterates branches matching
${UPDATE_BRANCH_PREFIX}-.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 21:06:34 +02:00
kevinveenbirkenbachandClaude Opus 4.7 dd7e84257f fix(update/open_pr): skip non-existent credentials paths via truncate -c
actions/checkout records two includeif targets per repo: one under
the host path (/home/runner/work/_temp/...) and one under the
container-internal path (/github/runner_temp/...). Only the host path
exists on the runner; the container path is a placeholder for
docker-based actions.

`truncate` (without -c) aborts on the missing container path with
exit 123. Add `-c` (--no-create) so missing paths are skipped
silently and the existing credentials file still gets blanked.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:42:59 +02:00
kevinveenbirkenbachandClaude Opus 4.7 6dc402e2b7 fix(update/open_pr): blank checkout's external credentials file
actions/checkout v6 doesn't put `http.https://github.com/.extraheader`
inside .git/config; it writes that header to an external file at
/home/runner/work/_temp/git-credentials-*.config and pulls it in via
local `includeif.gitdir:<repo>/.git.path = <credfile>` directives.
That's why every earlier `git config --local --unset-all` /
`--remove-section` round was a silent no-op (verified by the previous
debug commit's dump: the extraheader survives because the source-of-
truth lives outside .git/config).

Read every includeif `.path` value from local config, dedupe, and
`truncate -s 0` the targets so the included header chunk becomes
empty. The App installation token then enters the wire via the
URL-embedded `x-access-token:${GH_TOKEN}` credentials with no
competing Authorization header.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:37:08 +02:00
kevinveenbirkenbachandClaude Opus 4.7 86a998159b debug(update/open_pr): dump every config scope + probe token identity
The previous strip targeted only `--local` config and the resulting
CI log showed local was already empty, yet the push still authed
as github-actions[bot]. The auth has to be coming from --global,
--system, --worktree, or GH_TOKEN itself is GITHUB_TOKEN.

Dump every scope's http/credential/url entries before and after the
strip, and probe /installation/repositories with GH_TOKEN so the
HTTP status alone (no secret in logs) tells us whether it's an App
installation token or the workflow GITHUB_TOKEN.

Will replace this with the real fix once the next CI run surfaces
where the rogue Authorization actually originates.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:30:02 +02:00
kevinveenbirkenbachandClaude Opus 4.7 3dffbb8493 fix(update/open_pr): strip every extraheader via regex + log what was hit
Previous attempts targeted a specific config key path
(`http.https://github.com/.extraheader`). If actions/checkout v6
stores it under a different subsection naming, our unset/remove-section
no-ops silently and the GITHUB_TOKEN-bearing header keeps overriding
the App-token URL credentials.

Walk every `http.*.extraheader` entry the local config holds, pipe
the matched key names through xargs to unset-all each one, and dump
the remaining `http.*` config before pushing so the next CI run
proves what was stripped. The push itself still uses an
x-access-token URL so the App's installation token is the only
Authorization that reaches the wire.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:23:27 +02:00
kevinveenbirkenbachandClaude Opus 4.7 d950770385 fix(update/open_pr): nuke checkout's http section and push via tokenised URL
`--replace-all` on http.https://github.com/.extraheader still left the
wire with two Authorization headers ("remote: Duplicate header" → 400).
Drop the existing extraheader **and** the whole `http.https://github.com/`
subsection from local config, then push to a URL that embeds
`x-access-token:${GH_TOKEN}` directly so only one Authorization header
(the App's installation token) reaches the server.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:16:10 +02:00
kevinveenbirkenbachandClaude Opus 4.7 34f0c1d6c8 fix(update/open_pr): replace checkout extraheader instead of unsetting
Last attempt (unset + URL-embedded token) still pushed as
github-actions[bot] because actions/checkout's
http.https://github.com/.extraheader Authorization survived the
unset (likely silenced by `|| true`) and outranked URL-embedded
credentials on the wire.

Force-replace the header value with the App installation token's
HTTP Basic equivalent (`x-access-token:<token>` base64'd, same shape
checkout itself uses for GITHUB_TOKEN). Drops the URL rewrite, drops
the unset-then-rewrite dance, and guarantees a single Authorization
header that GitHub honours as the App.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:08:35 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ee9800200e fix(update/open_pr): suppress url-reachability lint on extraheader unset
`git config --unset-all "http.https://github.com/.extraheader"` is a
git config key path, not a fetchable URL, but
tests/external/repository/test_urls_reachable.py extracts the URL-like
substring and probes it, which returns 404 and fails the suite.

Tag the line with `# nocheck: url` so the URL test skips it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 13:01:36 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ce30948ed4 fix(update): push update branches with the App installation token
`actions/checkout` configures git's http.extraheader to authenticate
as GITHUB_TOKEN, which update.yml scopes to `contents: read`. The
subsequent `git push --force` in open_pr.sh therefore tripped 403
("Permission to infinito-nexus/core.git denied to github-actions[bot]")
even though the token-minting step had already produced an App
installation token with `contents: write` in GH_TOKEN.

Drop checkout's extraheader and rewrite origin's URL to embed
GH_TOKEN as x-access-token before pushing, so the App's permissions
actually reach the wire.

Also pin two actionlint `-ignore` patterns in scripts/lint/action.sh
to silence false positives for the `client-id` input on
actions/create-github-app-token@v3 (the action's action.yml carries
the input and marks `app-id` deprecated; actionlint 1.7.12's bundled
schema lags behind the upstream release).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 12:47:59 +02:00
kevinveenbirkenbachandClaude Opus 4.7 a988c12606 fix(update): tree-hash dedup + shellcheck SC1091 suppression
open_pr.sh's PR-dedup loop relied on `gh pr diff <num>` which the
GitHub API refuses with HTTP 406 once a PR exceeds 300 changed files.
Under `set -euo pipefail` that killed the workflow whenever any
unrelated large PR was open against the same base branch.

Replace the diff-hash check with a tree-SHA comparison: fetch each
PR's head commit shallowly, compare `git log -1 --format=%T` against
HEAD's tree. Same-tree means same end-state regardless of base SHA,
whitespace, file order, or patch context. Unfetchable heads (deleted
branches, lost permissions) fall through as non-duplicates instead of
aborting the run.

image_versions.sh / repository_refs.sh also pick up an explicit
`# shellcheck disable=SC1091` above their `. .env` source since the
dotenv file is generated at runtime and never present during
shellcheck's static parse.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 12:23:15 +02:00
kevinveenbirkenbachandClaude Opus 4.7 da77dd063b feat(update/open-pr): dedupe across open PRs and supersede stale class siblings
Two new steps wrapped around the existing push/create flow:

1. Dedupe before push: hash the local diff vs origin/<base-branch> and
   compare against gh pr diff of every open PR (any author, any branch)
   targeting the same base. On a match, exit 0 without pushing and
   without creating a PR. This is what stops the same image-version
   bump from being opened repeatedly.

2. Supersede after create: close every other open PR whose branch
   starts with UPDATE_BRANCH_PREFIX with a "Superseded by #N." comment.
   The prefix filter keeps the bot from ever closing repository-refs,
   skills, or human-authored PRs, even though dedupe itself sees them.

CRLF normalization on both sides of the hash compare so a runner that
checks out the patch with native-CRLF translation still produces a
stable hash.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 00:50:26 +02:00
kevinveenbirkenbachandClaude Opus 4.7 f9147e4486 refactor(scripts/github): split helpers into responsibility subfolders
Group the shell helpers under scripts/github/ into five subfolders
that match how the workflow files consume them:

  runner/   host-runner hygiene (disk, swap, Docker daemon, diagnostics)
  cancel/   run cancellation on PR-close and branch-delete events
  resolve/  derivation of workflow inputs and outputs from repo state
  update/   scheduled Update: Versions pipeline
  release/  release-time gates

Drop the now-redundant subfolder prefix from each filename, e.g.
dump_runner_state.sh becomes dump_state.sh and cancel_pull_request_runs.sh
becomes pull_request_runs.sh. Each subfolder carries a README that
describes its scope per documentation.md rules; the top-level README
points at the subfolders without inventorying files.

Propagate the new paths through ten workflow files, three docs
(workflow.md, pipeline.md, secrets.md), and two tests. Fix a real
runtime bug in enlarge_swap.sh, which still referenced the
pre-rename dump_runner_state.sh path. Extend the inline-block-size
lint hint to describe the new subfolder convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 00:50:26 +02:00