Repository navigation
Rebuild claude-code & ralphex-fe only on real tool updates (Renovate) - #116
Merged
Merged
Conversation
Replace the daily rebuild crons for claude-code and ralphex-fe with pinned tool versions managed by Renovate; rebuild only on a real version bump. Mend-hosted app, scope limited to rtk/ralphex/ claude-code/agent-browser, auto-merge gated on CI (which gains claude-code, both targets).
The cron landed on master in #113, after this branch was cut, so merging master back in reintroduced it. Git saw no conflict — this branch simply predates the cron — which is exactly how a no-op rebuild would have survived the PR unnoticed. Its own comment justified it by the Dockerfile resolving ralphex/rtk from GitHub "latest" at build time; those versions are now pinned ARGs that Renovate bumps, so the cron would rebuild byte-identical images daily.
This was referenced Aug 14, 2026
gatezh
added a commit
that referenced
this pull request
Aug 14, 2026
Installing the app with "All repositories" defaults the repo to Silent mode (dryRun=lookup): Renovate scans and lists updates in the developer portal but creates no PRs and no issues — including the Dependency Dashboard and any config-warning issue. A correct config then looks indistinguishable from a broken one, which is exactly how this repo presented after #116 landed. Record the portal fix and the reason a config change can't substitute for it: mode is overridden by dryRun, which is admin-level. Refs #119
gatezh
added a commit
that referenced
this pull request
Aug 14, 2026
#116 pinned rtk, ralphex, the Claude Code CLI and agent-browser as Renovate-managed ARGs but corrected only the rebuild-cadence wording, leaving the version-pinning claims stale. - ralphex-fe: replace the three "latest" rows with pinned versions and add an Automatic Rebuilds section — the image had no rebuild-trigger docs at all, which is why its stale rows never surfaced in a "daily" grep. Records that an agent-tool bump overwrites the bun-hugo tag rather than minting a new one, since the version tag derives from Bun and Hugo only. - claude-code: drop "Always-latest from GitHub Releases" and "pre-installed at latest"; fix AGENT_BROWSER_VERSION's default and document the three build args #116 added but never listed. - root: document the Renovate path above the manual dispatch path, state what stays manual (base images, Bun/Hugo), and record the Mend portal toggles — Silent mode off, Automated PRs on — that a correct config still depends on. Silent mode (dryRun=lookup) suppresses PRs and every issue including the Dependency Dashboard, making a working config indistinguishable from a broken one; `mode` in renovate.json5 cannot override it because dryRun takes precedence. Closes #119
gatezh
added a commit
that referenced
this pull request
Sep 9, 2026
`automerge: true` has never merged a PR since it was added in #116. #121 sat open, green and CLEAN for 3.5 weeks; #124 was on the same path. Root cause is a race created by `platformAutomerge: false`. That setting means only a Renovate run can merge, and a run merges when it observes an already-green branch. But @anthropic-ai/claude-code ships ~2 releases/day while Renovate runs every 2-9 days, so every run found a newer version, force-pushed the branch (resetting CI to pending) and ended seconds later. The last run is typical: pushed at 19:56:39, run ended 19:56:45, first check went green at 19:56:53, last at 19:59:40 -- nobody was watching. The run that could merge is always the run that just invalidated CI. Note this is not fixable with `minimumReleaseAge`: at any threshold there are still newly-eligible versions by the next run, so the force-push repeats. Switch to `platformAutomerge: true` so GitHub's native auto-merge merges on green with no Renovate run involved. This is also the freshest option -- no version-age delay at all. Native auto-merge needs something to wait for, i.e. branch protection with a required check, and a required check that never runs blocks a PR forever. CI is currently path-filtered at the `on:` level, so a docs-only PR (#123 touches only README.md and docs/*.md) triggers no CI at all and would deadlock. So drop the paths filter and add one `CI complete` job aggregating the others, passing on success-or-skipped so path-filtered builds still don't block. Per-image builds are still gated by detect-changes; the always-on jobs are lint-only. Verified with actionlint (exit 0, no findings).
gatezh
added a commit
that referenced
this pull request
Sep 9, 2026
* fix(ci): make Renovate auto-merge actually fire `automerge: true` has never merged a PR since it was added in #116. #121 sat open, green and CLEAN for 3.5 weeks; #124 was on the same path. Root cause is a race created by `platformAutomerge: false`. That setting means only a Renovate run can merge, and a run merges when it observes an already-green branch. But @anthropic-ai/claude-code ships ~2 releases/day while Renovate runs every 2-9 days, so every run found a newer version, force-pushed the branch (resetting CI to pending) and ended seconds later. The last run is typical: pushed at 19:56:39, run ended 19:56:45, first check went green at 19:56:53, last at 19:59:40 -- nobody was watching. The run that could merge is always the run that just invalidated CI. Note this is not fixable with `minimumReleaseAge`: at any threshold there are still newly-eligible versions by the next run, so the force-push repeats. Switch to `platformAutomerge: true` so GitHub's native auto-merge merges on green with no Renovate run involved. This is also the freshest option -- no version-age delay at all. Native auto-merge needs something to wait for, i.e. branch protection with a required check, and a required check that never runs blocks a PR forever. CI is currently path-filtered at the `on:` level, so a docs-only PR (#123 touches only README.md and docs/*.md) triggers no CI at all and would deadlock. So drop the paths filter and add one `CI complete` job aggregating the others, passing on success-or-skipped so path-filtered builds still don't block. Per-image builds are still gated by detect-changes; the always-on jobs are lint-only. Verified with actionlint (exit 0, no findings). * fix(ci): drop redundant platformAutomerge, soak non-claude bumps, guard the gate Review follow-ups on this branch. platformAutomerge:true is Renovate's own default (renovate-schema.json: platformAutomerge.default = true), so the explicit setting was noise. Deleted it and kept only the part of the comment that is still load-bearing: what the config depends on being configured on the GitHub side. These bumps merge unreviewed and publish to ghcr.io, so a compromised upstream release would reach the published images with no human in the loop. Added minimumReleaseAge: '3 days' as a soak period, with a second packageRule clearing it for @anthropic-ai/claude-code, which is tracked at latest on purpose. internalChecksFilter defaults to 'strict', so a too-young version is never offered and the group PR simply carries whichever tools are eligible. ci-complete is about to become the only required check on master, gating unattended merges, so two hardening changes: - A new job added to this workflow but omitted from `needs` would fail while the gate stayed green. The first step now derives the job list from the workflow file with yq and fails if `needs` has drifted. - The failure message named a bare result ('failure') with no job attached. Iterating toJSON(needs) instead of join(needs.*.result) keeps the job ids, so the error now says which job failed and how. Also recorded why this job uses always() rather than !cancelled(): GitHub counts a skipped required check as passing, so !cancelled() would turn a cancelled run into a green gate. Verified: actionlint exit 0; renovate-config-validator "Config validated successfully"; both jq filters and the yq job-list extraction exercised locally against success/skipped/failure/cancelled fixtures.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Rebuild the
claude-codeandralphex-feimages only when a tracked tool actually publishes a new release, instead of on a daily cron. Versions are pinned and kept current by Renovate.Why
Both images pinned four dev tools to "latest" at build time —
rtk,ralphex,@anthropic-ai/claude-code, andagent-browser— so a dailyschedule:cron existed purely to re-pull them. That rebuilt every morning even when nothing upstream had changed. Pinning the versions and letting Renovate open a bump PR on each real release (auto-merged on green CI, which then fires the existingpushbuild) eliminates the no-op rebuilds while keeping the images fresh — and gives a git-history audit trail of every bump.Changes
ARGs with# renovate:annotations inclaude-code/.devcontainer/Dockerfile(rtk, ralphex, claude-code CLI, agent-browser) andralphex-fe/Dockerfile(rtk, ralphex, claude-code CLI); drop the build-time GitHub-APIcurl | jq"latest" resolution (and the now-unusedjqfrom those alpine stages)..github/renovate.json5: acustom.regexmanager — set as the only enabled manager, so base images and action pins stay out of scope — reads those annotations;extractVersionstrips the leadingvfrom GitHub-release tags; the four tools are grouped into a single auto-merging PR (automerge: true,platformAutomerge: falseso the merge waits for green CI without requiring a branch-protection rule).ci.yml): addclaude-code(bothdefaultandsandboxtargets) to the hadolint and amd64 build-and-verify matrices — closing a pre-existing gap whereclaude-codehad no PR-time verification. A new optionaltargetmatrix field leaves the other single-target images unchanged.schedule:cron from bothbuild-claude-code.ymlandbuild-ralphex-fe.yml. Both keeppush(path-filtered) +workflow_dispatch.README.md,claude-code/README.md, and a stale.hadolint.yamlcomment.docs/superpowers/.Notes
.github/renovate.json5already onmaster, Renovate treats the repo as manually onboarded and goes straight to scanning — expect no "Configure Renovate" onboarding PR. Installing before the merge instead produces one that adds a rootrenovate.json, which sits ahead of.github/renovate.json5in Renovate's config lookup order (it stops at the first match), so merging it would silently shadow this scoped config with the all-managers default. Close it if it appears. Afterward, confirm the Dependency Dashboard lists exactly the four tools / seven annotations.masterin (312d62a) to remove the ralphex-fe cron. That cron reachedmasterin fix(ralphex-fe): rebuild daily for latest ralphex/rtk + repair update workflow #113 after this branch was cut, and this branch's copy ofbuild-ralphex-fe.ymlsimply predates it — so git merged cleanly in both directions and the cron would have survived this PR with no conflict and green CI (actionlint lints a cron fine). With the versions now pinned, it would have rebuilt byte-identical images daily. Removed in708ef80. (Supersedes the earlier note about coordinating with theralphex-image-rebuild-schedulebranch: fix(ralphex-fe): rebuild daily for latest ralphex/rtk + repair update workflow #113 already merged it, squashed, which is whymastercarried the cron.)custom.regexonly, so base images are out of scope and nothing rebuilds unless one of the four tools ships a release. If OS patch freshness starts to matter, add a weekly/monthly cron (no longer a daily no-op) or widen Renovate to thedockermanager for base images.BUN_VERSION/HUGO_VERSIONinralphex-fe/Dockerfileare deliberately unannotated and stay manual viaupdate-and-build-ralphex-fe.yml(dispatch-only, so it can't race Renovate).renovate-config-validator,hadolint(both Dockerfiles), andactionlint(all workflows) pass — actionlint re-run after themastermerge and cron removal; the config and Dockerfiles are untouched by that merge.