Skip to content

Multi-Account Renovate CE Rollout — Implementation Plan

Multi-Account Renovate CE Rollout — Implementation Plan

Section titled “Multi-Account Renovate CE Rollout — Implementation Plan”

For agentic workers: REQUIRED SUB-SKILL: Use dev-flow:subagent-driven-development (recommended) or dev-flow:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Expand the single in-cluster Mend Renovate CE to manage the seanb4t and holomush accounts alongside fzymgc-house, via a global config.js baseline + a widened autodiscover filter, with forks excluded and seanb4t curated by App-install scope.

Architecture: One config-bearing PR to this cluster repo (Task 1–2) followed by two out-of-band GitHub App installs with live verification (Task 3–4). No new infrastructure, secrets, or Vault policy — the App-ID/JWT credential is account-agnostic, so the same pod/App/secret serve all three accounts. The global config.js carries only the generic, account-agnostic baseline; cluster-specific rules stay in selfhosted-cluster/.github/renovate.json (which overrides global for that repo).

Tech Stack: Mend Renovate CE (Helm chart mend-renovate-ce 14.6.2), ArgoCD multi-source Application, helm/yq/yamllint/node for local validation, GitHub App installations, gh CLI for verification.

Design spec: docs/engineering/specs/2026-06-15-renovate-multi-account-design.md (design-reviewer READY round 2).


FileActionResponsibility
argocd/cluster-app/templates/renovate.yamlModifyWiden mendRnvAutoDiscoverFilter; add the renovate.config (global config.js) block to helm.valuesObject.

No other files change. config.js ships inline through the chart’s renovate.config value (rendered to ConfigMap renovate-ce-config-js, mounted /usr/src/app/config.js); there is no standalone manifest. selfhosted-cluster/.github/renovate.json is intentionally left untouched (its cluster-specific rules override the global baseline for this repo). Tasks 3–4 are GitHub App installs (GitHub UI / API), not repo files.

Helm-templating caveat: argocd/cluster-app/ is itself a Helm chart, so renovate.yaml is Helm-rendered. The config.js block MUST NOT contain a {{ sequence (Helm would try to interpret it). The baseline below contains none — preserve that property in any edit.


Task 1: Add global config.js baseline + widen autodiscover filter

Section titled “Task 1: Add global config.js baseline + widen autodiscover filter”

Files:

  • Modify: argocd/cluster-app/templates/renovate.yaml (the renovate: block of helm.valuesObject, currently lines ~27–36)

  • Step 1: Widen the autodiscover filter

In argocd/cluster-app/templates/renovate.yaml, change the filter line (currently):

mendRnvAutoDiscoverFilter: "fzymgc-house/*" # scope; widen per-org at rollout

to:

# Comma = filter delimiter in env-var form → three independent filters.
# Outer fence: nothing outside these three accounts is ever discovered.
mendRnvAutoDiscoverFilter: "fzymgc-house/*,seanb4t/*,holomush/*"
  • Step 2: Add the renovate.config global baseline

In the same renovate: block, immediately after the logFormat: json line, add a config: | block. The content is the global config.js (a module.exports = {...} JS file). Add it indented to match the other renovate.* keys (one level under renovate:):

# Global self-hosted config.js → ConfigMap renovate-ce-config-js,
# mounted /usr/src/app/config.js. Carries the GENERIC, account-agnostic
# baseline applied to every discovered repo across all 3 accounts.
# Cluster-specific rules (component groups, manager file patterns) stay
# in selfhosted-cluster/.github/renovate.json (repo config overrides global).
# MUST NOT contain a '{{' sequence (this file is Helm-rendered).
config: |
module.exports = {
// ---- Self-hosted / global controls ----
onboarding: true, // a repo is unmanaged until its onboarding PR merges
onboardingConfig: {
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
extends: ['config:recommended'],
},
forkProcessing: 'disabled', // skip forks (strong default; a repo's own
// renovate.json can still opt back in)
// ---- Shared baseline (flows down as defaults to every discovered repo) ----
extends: [
'config:recommended',
':dependencyDashboard',
':semanticCommits',
':automergeDigest',
':automergePatch',
':automergeMinor',
'group:monorepos',
'group:recommended',
'workarounds:all',
],
timezone: 'America/New_York',
dependencyDashboard: true,
dependencyDashboardTitle: '🤖 Dependency Dashboard',
configMigration: true,
osvVulnerabilityAlerts: true,
prConcurrentLimit: 10,
prHourlyLimit: 4,
labels: ['dependencies', 'renovate'],
automerge: true,
automergeType: 'pr',
platformAutomerge: true,
rebaseWhen: 'auto',
rangeStrategy: 'auto',
semanticCommits: 'enabled',
semanticCommitType: 'chore',
semanticCommitScope: 'deps',
vulnerabilityAlerts: {
enabled: true,
schedule: ['at any time'],
automerge: true,
minimumReleaseAge: null,
commitMessageSuffix: '[SECURITY]',
},
lockFileMaintenance: {
enabled: true,
schedule: ['before 4am on monday'],
automerge: true,
platformAutomerge: true,
},
packageRules: [
{
description: 'Major updates require manual review (no automerge)',
matchUpdateTypes: ['major'],
automerge: false,
minimumReleaseAge: '7 days',
prPriority: -1,
},
{
description: 'Auto-merge grouped minor and patch updates',
matchUpdateTypes: ['minor', 'patch'],
groupName: 'all non-major dependencies',
groupSlug: 'all-minor-patch',
automerge: true,
automergeType: 'pr',
platformAutomerge: true,
minimumReleaseAge: '3 days',
},
],
customManagers: [
{
customType: 'regex',
description: 'Update _VERSION variables in shell scripts and Makefiles',
managerFilePatterns: ['/\\.sh$/', '/Makefile$/', '/\\.mk$/'],
matchStrings: [
'# renovate: datasource=(?<datasource>\\S+) depName=(?<depName>\\S+)\\s+\\w+_VERSION\\s*=\\s*["\']?(?<currentValue>\\S+)["\']?',
],
},
],
ignorePaths: [
'**/node_modules/**',
'**/bower_components/**',
'**/vendor/**',
'**/examples/**',
'**/test/**',
'**/tests/**',
'**/__tests__/**',
],
};
  • Step 3: Lint the YAML

Run: yamllint -c .yamllint.yaml argocd/cluster-app/templates/renovate.yaml Expected: no errors (exit 0). If line-length warnings fire on long preset lines, they are inside a block scalar (config: |) — yamllint does not enforce length inside block scalars, so this should pass clean.

  • Step 4: Render the chart and confirm the Application + config.js render

Run:

Terminal window
helm template argocd/cluster-app | yq 'select(.kind == "Application" and .metadata.name == "renovate")' -

Expected: the renovate Application renders without error, and under .spec.sources[0].helm.valuesObject.renovate you see both mendRnvAutoDiscoverFilter: fzymgc-house/*,seanb4t/*,holomush/* and the multi-line config string beginning module.exports = {.

  • Step 5: Validate the embedded config.js is syntactically valid JavaScript

Extract the rendered config and check it with Node:

Terminal window
helm template argocd/cluster-app \
| yq 'select(.metadata.name == "renovate") | .spec.sources[0].helm.valuesObject.renovate.config' - \
> /tmp/renovate-config.js
node --check /tmp/renovate-config.js && echo "config.js OK"

Expected: config.js OK (exit 0). If node --check reports a syntax error, fix the config: | block in renovate.yaml and re-run Steps 4–5.

  • Step 6: Confirm no {{ crept into the config block

Run: rg -n '\{\{' argocd/cluster-app/templates/renovate.yaml Expected: no matches inside the config: | block (a match there would be Helm-interpreted and break rendering — Step 4 would already have failed, this is a fast guard).

  • Step 7: Commit

Commit using VCS-appropriate commands per references/vcs-preamble.md. In this jj repo:

Terminal window
jj commit -m "feat(renovate): global config.js baseline + multi-account autodiscover filter [hl-rn4i]
Add the generic account-agnostic Renovate baseline as global config.js
(forkProcessing disabled, onboarding on, automerge tiers, vuln alerts,
dependency dashboard, _VERSION regex manager) and widen the autodiscover
filter to fzymgc-house/seanb4t/holomush. No new infra/secrets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"

Task 2: Land the PR and verify fzymgc-house is unchanged

Section titled “Task 2: Land the PR and verify fzymgc-house is unchanged”

Files: none (deployment + verification task)

  • Step 1: Push the bookmark and open the PR
Terminal window
jj bookmark set worktree-renovate-multi-account -r @-
jj git push -b worktree-renovate-multi-account
gh pr create --head worktree-renovate-multi-account \
--title "feat(renovate): multi-account CE — global config.js baseline + widened autodiscover [hl-rn4i]" \
--body "Implements docs/engineering/plans/2026-06-15-renovate-multi-account.md. Config-only: global config.js baseline + autodiscover filter widened to fzymgc-house/seanb4t/holomush. No new infra/secrets/Vault policy. Per-account App installs follow in separate rollout steps (holomush, then seanb4t)."

Expected: PR created. Wait for merge to main (branch protection requires it).

  • Step 2: After merge, confirm ArgoCD synced the renovate Application

Use the Kubernetes MCP (read-only) or kubectl context fzymgc-house:

Terminal window
kubectl --context fzymgc-house get application renovate -n argocd -o jsonpath='{.status.sync.status} {.status.health.status}{"\n"}'

Expected: Synced Healthy.

  • Step 3: Confirm the ConfigMap now carries the baseline
Terminal window
kubectl --context fzymgc-house get configmap renovate-ce-config-js -n renovate -o jsonpath='{.data.config\.js}' | head -5

Expected: output begins module.exports = { and contains forkProcessing: 'disabled'.

  • Step 4: Confirm CE restarted cleanly with the new config
Terminal window
kubectl --context fzymgc-house get pods -n renovate
kubectl --context fzymgc-house logs -n renovate deploy/renovate-ce --tail=50 | rg -i 'error|fatal|config' | head -20

Expected: pod 1/1 Running; logs show config loaded with no fatal/config errors. (If CE crashloops, the config.js is semantically invalid — roll back by reverting the PR; ArgoCD re-syncs the prior ConfigMap.)

  • Step 5: Regression check — fzymgc-house behavior unchanged

The global baseline now merges under every fzymgc-house repo’s own config. Verify the richest repo (selfhosted-cluster, which has its own .github/renovate.json) is unaffected:

  • Trigger a debug run or inspect the next scheduled run’s logs for selfhosted-cluster in ClickStack/Loki (query service.name for renovate, filter the repo), OR check the repo’s open Renovate PRs/Dependency Dashboard before vs. after.
  • Confirm: no change in group names, no newly-automerged majors, the 🤖 Dependency Dashboard issue is intact, no duplicate/conflicting PRs.

Expected: fzymgc-house PR cadence and grouping are visually unchanged. If the global baseline alters effective config (e.g. an unexpected new group), narrow the global packageRules and repeat from Task 1.

  • Step 6: No commit (verification only). Record findings as a bd note on this task’s bead.

Task 3: Roll out holomush (org-wide install)

Section titled “Task 3: Roll out holomush (org-wide install)”

Files: none (GitHub App install + verification)

  • Step 1: Pre-flight — cut over from the Mend cloud Renovate app

holomush already has the Mend-hosted cloud Renovate app installed org-wide (app_slug: renovate, repository_selection: all). Running CE alongside it causes double-management (duplicate PRs, competing bots) — the same situation the fzymgc-house epic resolved by uninstalling the cloud app.

Confirm the pre-existing cloud install:

Terminal window
gh api /orgs/holomush/installations --jq '.installations[] | select(.app_slug=="renovate") | "\(.app_slug) selection=\(.repository_selection)"'

Expected: renovate selection=all. Uninstall (or suspend) the Mend cloud Renovate app from holomush via GitHub → Settings → Installed GitHub Apps, before installing CE. (Expect the cloud bot’s old artifacts — Dependency Dashboard, open PRs — to go stale, as in the fzymgc-house cutover; close them after CE takes over.)

  • Step 2: Install the fzymgc-renovate App on holomush

In GitHub: install the fzymgc-renovate App (App ID 4059017) on the holomush org with scope All repositories (holomush has 1 repo; org-wide is fine).

Verify the CE App install replaced the cloud one:

Terminal window
gh api /orgs/holomush/installations --jq '.installations[].app_slug'

Expected: the list now includes fzymgc-renovate and no longer includes the cloud renovate.

  • Step 3: Confirm CE discovers holomush within the fence

After the next autodiscover cycle, check CE logs:

Terminal window
kubectl --context fzymgc-house logs -n renovate deploy/renovate-ce --since=15m | rg -i 'holomush'

Expected: log lines showing holomush/holomush discovered/onboarding (matched by the holomush/* filter).

  • Step 4: Verify the onboarding PR
Terminal window
gh pr list --repo holomush/holomush --author 'app/fzymgc-renovate' --json number,title

Expected: a “Configure Renovate” onboarding PR exists. Open it and confirm the proposed renovate.json (extends: ['config:recommended'] per onboardingConfig) is correct for a Go repo.

  • Step 5: Merge the onboarding PR and confirm management begins

Merge the onboarding PR. After the next cycle, confirm Renovate opens dependency PRs (e.g. Go module updates) and the Dependency Dashboard issue appears.

Expected: dependency PRs follow the baseline (grouped minor/patch automerge, majors held for review).

  • Step 6: No commit (rollout/verification). Record outcome as a bd note.

Task 4: Roll out seanb4t (selected-repos install, forks excluded)

Section titled “Task 4: Roll out seanb4t (selected-repos install, forks excluded)”

Files: none (GitHub App install + verification)

  • Step 1: Install the App on a first batch of seanb4t source repos

In GitHub: install fzymgc-renovate on the seanb4t account with scope Only select repositories, choosing an initial small batch of source repos (e.g. start with the 5 that already have a renovate config: engram, weft, image-organizer-cli, example-movie-service, bootstrap-graphql-model-mutate). Do not select any fork.

Verify the selected set. The account-level installation endpoints require a GitHub-App JWT (they 401/403 with an ordinary gh token), so verify per repo instead — /repos/{owner}/{repo}/installation works with a normal token:

Terminal window
for r in engram weft image-organizer-cli example-movie-service bootstrap-graphql-model-mutate; do
printf '%s -> ' "$r"
gh api "/repos/seanb4t/$r/installation" --jq '.app_slug' 2>/dev/null || echo "NOT installed"
done

Expected: each selected repo prints fzymgc-renovate. The authoritative selected-repos list is also visible in the GitHub UI (Settings → Installed GitHub Apps → fzymgc-renovate → Configure).

  • Step 2: Confirm CE discovers only the selected source repos
Terminal window
kubectl --context fzymgc-house logs -n renovate deploy/renovate-ce --since=15m | rg -i 'seanb4t/'

Expected: only the selected repos appear. No fork appears. Cross-check that none of the selected names is a fork:

Terminal window
gh repo list seanb4t --json name,isFork --jq '.[] | select(.isFork==true) | .name' > /tmp/seanb4t-forks.txt
# none of these names should show up in the CE discovery logs above
  • Step 3: Explicitly verify the fork guard

Pick one seanb4t fork and confirm CE never processes it:

Terminal window
kubectl --context fzymgc-house logs -n renovate deploy/renovate-ce --since=1h | rg -i "$(head -1 /tmp/seanb4t-forks.txt)"

Expected: either no match, or a log line indicating the fork was skipped (forkProcessing disabled). No PR is opened on any fork.

  • Step 4: Verify onboarding for the selected repos
Terminal window
for r in engram weft image-organizer-cli example-movie-service bootstrap-graphql-model-mutate; do
echo "== $r =="; gh pr list --repo seanb4t/$r --author 'app/fzymgc-renovate' --json number,title --jq '.[].title'
done

Expected: repos without a prior config get an onboarding PR; repos that already have a renovate.json start managing directly (no onboarding PR needed). Confirm baseline behavior (grouped automerge, majors held).

  • Step 5: Expand the install incrementally

Add further seanb4t source repos to the App’s selected set in batches, repeating Steps 2–4 per batch until the desired set is covered. Stop when membership matches intent.

  • Step 6: No commit (rollout/verification). Record the final managed-repo set and any excluded repos as a bd note (no-silent-caps: list what was deliberately left out).

Single rollback lever for the config change: revert the Task 1/2 PR on main; ArgoCD re-syncs the prior (empty) ConfigMap and CE returns to fzymgc-house-only behavior. App installs (Tasks 3–4) are reverted independently by uninstalling the App from the affected account — no repo change needed.