@reddoorla/maintenance 0.94.1 → 0.95.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/{announce-PGT4SZZB.js → announce-CLQYX5I4.js} +4 -4
- package/dist/{blux-NXQLMS4W.js → blux-4KNXUP2K.js} +1 -1
- package/dist/{bump-deps-6OR7LL4E.js → bump-deps-C5G2EFBB.js} +6 -6
- package/dist/{chunk-LS6QPANO.js → chunk-2LYOAB2E.js} +2 -2
- package/dist/{chunk-KQB2BX4V.js → chunk-2ZAZ7ETT.js} +2 -2
- package/dist/{chunk-XTK5VIQB.js → chunk-AVEQUR44.js} +48 -1
- package/dist/chunk-AVEQUR44.js.map +1 -0
- package/dist/{chunk-B6NUZ2ZX.js → chunk-BCKEP5S6.js} +2 -2
- package/dist/{chunk-DMPHP7UP.js → chunk-BI2VI6EM.js} +2 -2
- package/dist/{chunk-5FCBYK6L.js → chunk-BVEDKMNG.js} +10 -10
- package/dist/{chunk-OCNSIBMP.js → chunk-C3IQQKXL.js} +26 -1
- package/dist/{chunk-OCNSIBMP.js.map → chunk-C3IQQKXL.js.map} +1 -1
- package/dist/{chunk-PPIZHDJL.js → chunk-CJ2RMTT7.js} +2 -2
- package/dist/{chunk-7CJ3DVZW.js → chunk-CWXTNPNZ.js} +2 -2
- package/dist/{chunk-TGUESYGF.js → chunk-DFKBTBO3.js} +2 -2
- package/dist/{chunk-WHWVYL7B.js → chunk-FSGICFXB.js} +7 -32
- package/dist/chunk-FSGICFXB.js.map +1 -0
- package/dist/{chunk-3HS6LPFX.js → chunk-FUWFLAWQ.js} +4 -4
- package/dist/{chunk-4AIYWWKV.js → chunk-KJRVMMM3.js} +2 -2
- package/dist/{chunk-TCF6S7AD.js → chunk-LM2NHRCJ.js} +2 -2
- package/dist/{chunk-AEUABPAJ.js → chunk-R5IMFAFR.js} +2 -2
- package/dist/{chunk-NTLSMJHW.js → chunk-S7RYFUIK.js} +2 -2
- package/dist/{chunk-MLT4QY5X.js → chunk-SLFG7Y76.js} +1 -1
- package/dist/chunk-SLFG7Y76.js.map +1 -0
- package/dist/{chunk-REJCSA4H.js → chunk-TR3HFH6J.js} +4 -3
- package/dist/chunk-TR3HFH6J.js.map +1 -0
- package/dist/{chunk-22GKJO5F.js → chunk-VSGMCCGQ.js} +2 -2
- package/dist/{chunk-4R3YAQNV.js → chunk-WSJ7RO54.js} +2 -2
- package/dist/chunk-XPJQXSC4.js +112 -0
- package/dist/chunk-XPJQXSC4.js.map +1 -0
- package/dist/chunk-ZCL7C5LH.js +2698 -0
- package/dist/chunk-ZCL7C5LH.js.map +1 -0
- package/dist/{chunk-ZTPPCHDB.js → chunk-ZQV7UQNE.js} +2 -2
- package/dist/cli/bin.js +39 -26
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.d.ts +1 -1
- package/dist/cli/commands/audit.js +8 -8
- package/dist/client-G2WGWPCA.js +11 -0
- package/dist/configs/eslint.js +27 -1
- package/dist/configs/eslint.js.map +1 -1
- package/dist/{convert-to-pnpm-3RVGUBBR.js → convert-to-pnpm-YTFREUJK.js} +6 -6
- package/dist/{db-FL7ZBF6H.js → db-XKZYDCKM.js} +9 -9
- package/dist/{digest-TZ3QQOSF.js → digest-7UBGUBRK.js} +14 -14
- package/dist/{digest-collectors-EHFY2OQC.js → digest-collectors-DJFN5ZDK.js} +8 -8
- package/dist/{ensure-site-WIDO6LGU.js → ensure-site-OAAE5YE2.js} +2 -2
- package/dist/{forms-notify-target-2LWF5C5B.js → forms-notify-target-UC6PF3AE.js} +2 -2
- package/dist/{github-signals-GAHNOAHS.js → github-signals-TX5FPYP6.js} +5 -5
- package/dist/{header-image-YN3AQRY3.js → header-image-PSPT7CUM.js} +2 -2
- package/dist/{health-endpoint-XLNBWVHK.js → health-endpoint-KVCJYWET.js} +6 -6
- package/dist/{health-mirror-EB76EERV.js → health-mirror-XJAKA3EY.js} +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +31 -29
- package/dist/index.js.map +1 -1
- package/dist/{init-YCLSGIEP.js → init-FFE4FCO2.js} +13 -13
- package/dist/{launch-FSHDTXSM.js → launch-UD36AHUN.js} +221 -7
- package/dist/launch-UD36AHUN.js.map +1 -0
- package/dist/match-harness-QRG6XZAR.js +70 -0
- package/dist/match-harness-QRG6XZAR.js.map +1 -0
- package/dist/migrate-Y7R4CESL.js +7 -0
- package/dist/{onboard-NKE6GZKL.js → onboard-TM2KESGF.js} +6 -6
- package/dist/{orchestrate-MWXENQOS.js → orchestrate-HYVQPRCE.js} +6 -6
- package/dist/{pipeline-MATFYQRC.js → pipeline-YROAY6DT.js} +2 -2
- package/dist/{preflight-4VQP7JJL.js → preflight-RYIP54NY.js} +7 -7
- package/dist/{prismic-ci-DM352L2Q.js → prismic-ci-6GKKZGB5.js} +13 -11
- package/dist/prismic-ci-6GKKZGB5.js.map +1 -0
- package/dist/{prismic-models-Q32ARSYS.js → prismic-models-P6FWWVRS.js} +5 -5
- package/dist/prospect/types.d.ts +1 -1
- package/dist/{prospect-audit-MT6RGMOY.js → prospect-audit-PRHU53HS.js} +5 -5
- package/dist/{prospect-audits-RVYEA4KH.js → prospect-audits-R3LWF6U7.js} +10 -4
- package/dist/recipes/sync-configs.d.ts +1 -1
- package/dist/recipes/sync-configs.js +4 -4
- package/dist/{render-JE5P26H5.js → render-YGNGTX3W.js} +2 -2
- package/dist/render-from-row-IFSBHWDH.js +1 -1
- package/dist/{report-6FFHGUUS.js → report-LQS4ECTM.js} +12 -12
- package/dist/{report-mirror-IP64R7B3.js → report-mirror-HDTOJE64.js} +3 -3
- package/dist/{schema-2V7BGYVT.js → schema-A4WCPIF4.js} +1 -1
- package/dist/{schema-2V7BGYVT.js.map → schema-A4WCPIF4.js.map} +1 -1
- package/dist/{self-updating-5DVDIEHB.js → self-updating-MWXIZLL7.js} +6 -6
- package/dist/{selftest-LJYXBIO5.js → selftest-VXTHRPRN.js} +9 -9
- package/dist/{site-mirror-UKHREAD6.js → site-mirror-WON4FTJ2.js} +3 -3
- package/dist/{smoke-suite-NUTC7IUK.js → smoke-suite-7DGITCTZ.js} +6 -6
- package/dist/{submissions-5GKJPCIH.js → submissions-OVZPVBYG.js} +2 -2
- package/dist/{svelte-codemods-U4NIT6CV.js → svelte-codemods-WSV2FGNG.js} +6 -6
- package/dist/{sync-configs-S2JYY6QU.js → sync-configs-RRIHNLIJ.js} +7 -7
- package/dist/{types-OIQZ5hWN.d.ts → types-D_bpy89i.d.ts} +1 -1
- package/dist/{upgrade-YFZCMUPE.js → upgrade-VTD6V4HS.js} +6 -6
- package/dist/util/git.d.ts +46 -1
- package/dist/util/git.js +7 -1
- package/package.json +1 -1
- package/dist/chunk-7MKT4I5T.js +0 -62
- package/dist/chunk-7MKT4I5T.js.map +0 -1
- package/dist/chunk-MLT4QY5X.js.map +0 -1
- package/dist/chunk-REJCSA4H.js.map +0 -1
- package/dist/chunk-WHWVYL7B.js.map +0 -1
- package/dist/chunk-XTK5VIQB.js.map +0 -1
- package/dist/client-M2V6FHH5.js +0 -11
- package/dist/launch-FSHDTXSM.js.map +0 -1
- package/dist/migrate-K4JETR36.js +0 -7
- package/dist/prismic-ci-DM352L2Q.js.map +0 -1
- /package/dist/{announce-PGT4SZZB.js.map → announce-CLQYX5I4.js.map} +0 -0
- /package/dist/{blux-NXQLMS4W.js.map → blux-4KNXUP2K.js.map} +0 -0
- /package/dist/{bump-deps-6OR7LL4E.js.map → bump-deps-C5G2EFBB.js.map} +0 -0
- /package/dist/{chunk-LS6QPANO.js.map → chunk-2LYOAB2E.js.map} +0 -0
- /package/dist/{chunk-KQB2BX4V.js.map → chunk-2ZAZ7ETT.js.map} +0 -0
- /package/dist/{chunk-B6NUZ2ZX.js.map → chunk-BCKEP5S6.js.map} +0 -0
- /package/dist/{chunk-DMPHP7UP.js.map → chunk-BI2VI6EM.js.map} +0 -0
- /package/dist/{chunk-5FCBYK6L.js.map → chunk-BVEDKMNG.js.map} +0 -0
- /package/dist/{chunk-PPIZHDJL.js.map → chunk-CJ2RMTT7.js.map} +0 -0
- /package/dist/{chunk-7CJ3DVZW.js.map → chunk-CWXTNPNZ.js.map} +0 -0
- /package/dist/{chunk-TGUESYGF.js.map → chunk-DFKBTBO3.js.map} +0 -0
- /package/dist/{chunk-3HS6LPFX.js.map → chunk-FUWFLAWQ.js.map} +0 -0
- /package/dist/{chunk-4AIYWWKV.js.map → chunk-KJRVMMM3.js.map} +0 -0
- /package/dist/{chunk-TCF6S7AD.js.map → chunk-LM2NHRCJ.js.map} +0 -0
- /package/dist/{chunk-AEUABPAJ.js.map → chunk-R5IMFAFR.js.map} +0 -0
- /package/dist/{chunk-NTLSMJHW.js.map → chunk-S7RYFUIK.js.map} +0 -0
- /package/dist/{chunk-22GKJO5F.js.map → chunk-VSGMCCGQ.js.map} +0 -0
- /package/dist/{chunk-4R3YAQNV.js.map → chunk-WSJ7RO54.js.map} +0 -0
- /package/dist/{chunk-ZTPPCHDB.js.map → chunk-ZQV7UQNE.js.map} +0 -0
- /package/dist/{client-M2V6FHH5.js.map → client-G2WGWPCA.js.map} +0 -0
- /package/dist/{convert-to-pnpm-3RVGUBBR.js.map → convert-to-pnpm-YTFREUJK.js.map} +0 -0
- /package/dist/{db-FL7ZBF6H.js.map → db-XKZYDCKM.js.map} +0 -0
- /package/dist/{digest-TZ3QQOSF.js.map → digest-7UBGUBRK.js.map} +0 -0
- /package/dist/{digest-collectors-EHFY2OQC.js.map → digest-collectors-DJFN5ZDK.js.map} +0 -0
- /package/dist/{ensure-site-WIDO6LGU.js.map → ensure-site-OAAE5YE2.js.map} +0 -0
- /package/dist/{forms-notify-target-2LWF5C5B.js.map → forms-notify-target-UC6PF3AE.js.map} +0 -0
- /package/dist/{github-signals-GAHNOAHS.js.map → github-signals-TX5FPYP6.js.map} +0 -0
- /package/dist/{header-image-YN3AQRY3.js.map → header-image-PSPT7CUM.js.map} +0 -0
- /package/dist/{health-endpoint-XLNBWVHK.js.map → health-endpoint-KVCJYWET.js.map} +0 -0
- /package/dist/{health-mirror-EB76EERV.js.map → health-mirror-XJAKA3EY.js.map} +0 -0
- /package/dist/{init-YCLSGIEP.js.map → init-FFE4FCO2.js.map} +0 -0
- /package/dist/{migrate-K4JETR36.js.map → migrate-Y7R4CESL.js.map} +0 -0
- /package/dist/{onboard-NKE6GZKL.js.map → onboard-TM2KESGF.js.map} +0 -0
- /package/dist/{orchestrate-MWXENQOS.js.map → orchestrate-HYVQPRCE.js.map} +0 -0
- /package/dist/{pipeline-MATFYQRC.js.map → pipeline-YROAY6DT.js.map} +0 -0
- /package/dist/{preflight-4VQP7JJL.js.map → preflight-RYIP54NY.js.map} +0 -0
- /package/dist/{prismic-models-Q32ARSYS.js.map → prismic-models-P6FWWVRS.js.map} +0 -0
- /package/dist/{prospect-audit-MT6RGMOY.js.map → prospect-audit-PRHU53HS.js.map} +0 -0
- /package/dist/{prospect-audits-RVYEA4KH.js.map → prospect-audits-R3LWF6U7.js.map} +0 -0
- /package/dist/{render-JE5P26H5.js.map → render-YGNGTX3W.js.map} +0 -0
- /package/dist/{report-6FFHGUUS.js.map → report-LQS4ECTM.js.map} +0 -0
- /package/dist/{report-mirror-IP64R7B3.js.map → report-mirror-HDTOJE64.js.map} +0 -0
- /package/dist/{self-updating-5DVDIEHB.js.map → self-updating-MWXIZLL7.js.map} +0 -0
- /package/dist/{selftest-LJYXBIO5.js.map → selftest-VXTHRPRN.js.map} +0 -0
- /package/dist/{site-mirror-UKHREAD6.js.map → site-mirror-WON4FTJ2.js.map} +0 -0
- /package/dist/{smoke-suite-NUTC7IUK.js.map → smoke-suite-7DGITCTZ.js.map} +0 -0
- /package/dist/{submissions-5GKJPCIH.js.map → submissions-OVZPVBYG.js.map} +0 -0
- /package/dist/{svelte-codemods-U4NIT6CV.js.map → svelte-codemods-WSV2FGNG.js.map} +0 -0
- /package/dist/{sync-configs-S2JYY6QU.js.map → sync-configs-RRIHNLIJ.js.map} +0 -0
- /package/dist/{upgrade-YFZCMUPE.js.map → upgrade-VTD6V4HS.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/recipes/self-updating/index.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport type { RecipeResult, Site } from \"../../types.js\";\nimport { templatesByName } from \"../sync-configs/templates.js\";\nimport {\n renovateActionGaps,\n withRenovatePinsFrom,\n RENOVATE_ACTION_CONFIG,\n} from \"../sync-configs/renovate-action.js\";\nimport {\n resolveOwnerRepo,\n push as gitPush,\n branchName,\n checkoutBranch,\n createBranch,\n currentBranch,\n commit as gitCommit,\n isWorkingTreeClean,\n} from \"../../util/git.js\";\nimport { siteLabel } from \"../../util/site.js\";\nimport { readGitHubConfig } from \"../../github/config.js\";\nimport { makeGitHub, type GitHub } from \"../../github/gh.js\";\nimport {\n FLEET_RULESET_NAME,\n desiredRuleset,\n healRuleset,\n rulesetGaps,\n} from \"../../github/rulesets.js\";\n\n// Deliberately NOT ci.yml: that file is per-site parameterized (netlify-site,\n// node-version, permissions), so an exact-match heal against a static template\n// would strip those values in a green auto-mergeable PR — the armed clobber\n// the 2026-08-02 architecture review found. The starter clone owns ci.yml's\n// shape. Its pinned reusable-workflow ref is sometimes bumped by Renovate\n// (v1.2.0 -> v1.3.0 across 17 repos, Jul-Aug 2026) but was not for v1.4.0 or\n// v1.4.1 — the latter was swept by hand on 2026-09-01 — so propagation is a\n// manual step to verify, never a guarantee.\nconst SELF_UPDATING_CONFIGS = [\"renovate-action\", \"renovate-config\"] as const;\n\n// Reusable-workflow jobs report their check as \"<caller-job> / <reusable-job>\".\n// The thin `ci` caller (job `ci`) calls reddoorla/.github's reusable workflow (job `ci`),\n// so the required context is \"ci / ci\", NOT \"ci\". (Provisional — verified empirically on the\n// starter during M7.1 rollout; correct here if the live checks API reports a different string.)\nconst REQUIRED_CHECK = \"ci / ci\";\n\nexport type SelfUpdatingDeps = {\n github?: GitHub;\n pushBranch?: (cwd: string, branch: string) => Promise<void>;\n};\n\nfunction resultOf(\n site: Site,\n status: RecipeResult[\"status\"],\n notes: string,\n commits: string[] = [],\n): RecipeResult {\n return { recipe: \"self-updating\", site: siteLabel(site), status, commits, notes };\n}\n\n/** True when a file's current content matches the canonical template for drift\n * purposes. Normalizes line endings + trailing whitespace so a stray trailing\n * newline (or CRLF) can't read as drift and open a needless PR every nightly run;\n * any real content change still differs. */\nfunction sameConfigContents(current: string, canonical: string): boolean {\n const norm = (s: string) => s.replace(/\\r\\n/g, \"\\n\").replace(/\\s+$/, \"\");\n return norm(current) === norm(canonical);\n}\n\nexport async function selfUpdating(site: Site, deps: SelfUpdatingDeps = {}): Promise<RecipeResult> {\n const templates = templatesByName([...SELF_UPDATING_CONFIGS]);\n\n let repo: string | null;\n try {\n repo = await resolveOwnerRepo(site);\n } catch (err) {\n // A malformed repo identity must abort before any `gh` write — surface it\n // as a recipe failure rather than letting the token reach an unintended repo.\n return resultOf(site, \"failed\", err instanceof Error ? err.message : String(err));\n }\n if (!repo) {\n return resultOf(\n site,\n \"failed\",\n \"could not determine a GitHub repo for this site — set Airtable 'Git repo', or give \" +\n \"the checkout an origin remote whose URL is a GitHub owner/repo\",\n );\n }\n\n const cfg = readGitHubConfig();\n if (!deps.github && !cfg) return resultOf(site, \"failed\", \"GITHUB_TOKEN not set\");\n const github = deps.github ?? makeGitHub({ token: cfg!.token });\n\n const base = await github.defaultBranch(repo).catch(() => \"main\");\n const actions: string[] = [];\n const commits: string[] = [];\n // Hoisted so the `finally` can restore the operator's branch even when a\n // failure (push/PR error) aborts AFTER we created + checked out the maint\n // branch. `maintBranch` stays null until createBranch succeeds, so the\n // restore is a no-op on every path that never left the operator's branch.\n let original: string | null = null;\n let maintBranch: string | null = null;\n\n try {\n // A. Renovate config on the default branch — compare CONTENT, not just\n // existence, so a present-but-STALE config (a pre-App workflow, a drifted\n // Renovate schedule window) is corrected rather than left silently\n // forever. The prior existence-only gate reported \"already self-updating\" for any\n // repo that merely HAD the files, however out of date — the very drift this\n // recipe exists to repair (e.g. the Renovate schedule-window regression).\n //\n // renovate.yml specifically is compliance-checked rather than byte-matched\n // (see renovateActionGaps / withRenovatePinsFrom in\n // ../sync-configs/renovate-action.js): Renovate legitimately bumps its own\n // digest pins forward, and a site's prettier may legitimately quote the\n // cron / RENOVATE_* scalars differently — neither is real drift (issue\n // #651; a byte-match recipe opened reddoor-starter-blux#1, a PR that\n // DOWNGRADED Renovate's own pin).\n const drifted: string[] = [];\n // Captured so the write loop below can carry a drifted renovate.yml's own\n // pins forward instead of re-fetching (and so a compliant-but-non-byte-\n // identical file's contents are available for that carry-forward too,\n // though it won't need healing).\n const currentContents = new Map<string, string | null>();\n for (const t of templates) {\n const current = await github.fileContentsOnBranch(repo, base, t.path);\n currentContents.set(t.path, current);\n if (current === null) {\n drifted.push(t.path);\n continue;\n }\n if (sameConfigContents(current, t.contents)) continue;\n if (t.config === RENOVATE_ACTION_CONFIG && renovateActionGaps(current).length === 0) {\n continue;\n }\n drifted.push(t.path);\n }\n if (drifted.length > 0) {\n const existingPR = await github.findOpenSelfUpdatingPR(repo);\n if (existingPR) {\n actions.push(`self-updating PR already open: ${existingPR}`);\n } else {\n if (!(await isWorkingTreeClean(site.path))) {\n return resultOf(site, \"failed\", \"working tree not clean — commit or stash first\");\n }\n // Capture the operator's branch BEFORE creating the maint branch so the\n // `finally` can return them to it (#2). Best-effort capture: if it fails\n // we just skip the restore rather than guess.\n try {\n original = await currentBranch(site.path);\n } catch {\n original = null;\n }\n maintBranch = branchName(\"self-updating\");\n await createBranch(site.path, maintBranch);\n // Write only the paths that actually drifted — a repo whose\n // renovate.json is stale but whose renovate.yml already passes\n // `renovateActionGaps` must not have that compliant renovate.yml\n // silently rewritten (e.g. un-quoting its scalars) in the same PR.\n // On a fresh repo every template path is missing, so `drifted`\n // already contains all of them and bootstrap is unaffected.\n for (const t of templates) {\n if (!drifted.includes(t.path)) continue;\n const dest = join(site.path, t.path);\n await mkdir(dirname(dest), { recursive: true });\n // For a genuinely non-compliant renovate.yml, carry the site's own\n // (still-digest-pinned) action refs forward onto the template before\n // writing it — writing the template verbatim would put its OLDER\n // pins over the site's newer ones, re-introducing the downgrade\n // this compliance check exists to prevent (issue #651).\n const contents =\n t.config === RENOVATE_ACTION_CONFIG\n ? withRenovatePinsFrom(t.contents, currentContents.get(t.path) ?? null)\n : t.contents;\n await writeFile(dest, contents, \"utf-8\");\n }\n const sha = await gitCommit(site.path, \"ci: enable self-updating (Renovate auto-merge)\");\n if (sha) commits.push(sha);\n await (deps.pushBranch ?? gitPush)(site.path, maintBranch);\n const pr = await github.openPullRequest(repo, {\n head: maintBranch,\n base,\n title: \"Enable self-updating (Renovate)\",\n body: \"Adds scheduled Renovate and Renovate-owned merges for patch/minor updates. (ci.yml is deliberately not managed here — its shape comes from the starter and its reusable-workflow pin from Renovate.)\",\n });\n actions.push(`opened PR ${pr.url}`);\n // (Branch restore happens in the `finally` below — on success AND on a\n // failure that aborts after createBranch.)\n }\n }\n\n // B. Repo settings — check-then-ensure, each independent (self-healing).\n //\n // Platform auto-merge is DISABLED fleet-wide, and this block self-heals toward\n // OFF (it used to drive repos ON — do not \"fix\" it back).\n //\n // Why: GitHub's platform auto-merge is a per-PR flag that anyone with write\n // access can arm; the PR then merges itself later, unattended, once checks go\n // green. The org Renovate preset forbids auto-merging majors and was working\n // correctly — Renovate never armed them — but the preset has no authority over\n // a flag someone else set. On 2026-07-26 a bulk `gh pr merge --auto` sweep\n // armed it across 8 fleet repos, and the next morning two `actions/checkout`\n // MAJOR bumps merged with zero reviews (reddoor-starter#77,\n // reddoor-website#111) as soon as Renovate's own rebase cleared their\n // conflicts. Realized damage, not a hypothetical.\n //\n // The fix is to keep merges INSIDE Renovate's own run, where its `packageRules`\n // actually govern the decision. Renovate can only merge while it is running, so\n // the workflow cron (see sync-configs templates) is the merge cadence.\n //\n // Leaving the self-heal pointed at OFF makes this a drift ALARM: if someone\n // re-enables platform auto-merge, the next `self-updating` / `launch` run turns\n // it back off and records the action. `enableRepoAutoMerge` is retained in\n // `src/github/gh.ts` as the documented rollback path.\n if (await github.autoMergeEnabled(repo)) {\n await github.disableRepoAutoMerge(repo);\n actions.push(\"disabled auto-merge (platform auto-merge is not permitted fleet-wide)\");\n }\n const existingContexts = await github.branchProtectionContexts(repo, base);\n if (!existingContexts.includes(REQUIRED_CHECK)) {\n // protectBranch issues a full PUT that REPLACES required-status-check contexts.\n // Send the UNION of the branch's existing required contexts + our REQUIRED_CHECK\n // so we ADD the CI gate without silently dropping any other checks the repo\n // already requires. (When the branch has no protection yet, existingContexts is\n // [] and this is just [REQUIRED_CHECK] — the original behavior.) Dedupe defends\n // against REQUIRED_CHECK already appearing among the existing contexts.\n const contexts = [...new Set([...existingContexts, REQUIRED_CHECK])];\n await github.protectBranch(repo, base, contexts);\n actions.push(`required \"${REQUIRED_CHECK}\" check on ${base}`);\n }\n // Ruleset ensure — the layer that binds ADMINS. Classic protection above\n // keeps `enforce_admins: false`, which is exactly the hole the 2026-07-26\n // bulk `gh pr merge --auto` sweep walked through (two unreviewed MAJORS\n // merged). The 2026-08-01 audit then found the six NEWEST repos with no\n // ruleset at all — every hand-applied sweep regrows its gap at the next\n // onboard, so the recipe owns the invariant now. Spec + invariants:\n // docs/superpowers/specs/2026-08-02-ruleset-self-healing-design.md.\n //\n // Private repos are skipped BEFORE any attempt: rulesets there need a paid\n // plan, so trying would either fail every run or spam a phantom action\n // that flips this recipe to \"applied\" forever. Classic protection remains\n // their floor.\n if ((await github.repoVisibility(repo)) === \"public\") {\n // Evidence gate: only require \"ci / ci\" once that check-run has actually\n // been OBSERVED on the default branch. With an empty bypass list, a\n // required context that never fires would make the repo permanently\n // unmergeable by EVERYONE — e.g. a workflow_call-only ci.yml emits no\n // check runs. Unobserved ⇒ refs rules only; the check upgrades on a\n // later pass once real evidence exists.\n //\n // Residual risk, accepted: this proves the check fires on default-branch\n // PUSHES, not on pull_request events — a hand-edited push-only ci.yml\n // would pass the gate and then block every PR. ci.yml is deliberately\n // NOT healed by block A (per-site parameterized; see\n // SELF_UPDATING_CONFIGS), so this recipe cannot correct that drift —\n // the exposure is one bricked-PR repo, immediately visible on its next\n // PR. Un-brick path: edit the ruleset via API (ruleset ADMIN is not\n // ref-gated), then fix the ci.yml triggers by push.\n const check = (await github.checkContextObserved(repo, base, REQUIRED_CHECK))\n ? REQUIRED_CHECK\n : null;\n const existingRuleset = (await github.listRepoRulesets(repo)).find(\n (r) => r.name === FLEET_RULESET_NAME,\n );\n if (!existingRuleset) {\n await github.createRuleset(repo, desiredRuleset(check));\n actions.push(\n check === null\n ? `created ruleset \"${FLEET_RULESET_NAME}\" (refs rules only — \"${REQUIRED_CHECK}\" not yet observed on ${base})`\n : `created ruleset \"${FLEET_RULESET_NAME}\"`,\n );\n } else {\n const full = await github.getRuleset(repo, existingRuleset.id);\n const gaps = rulesetGaps(full, check);\n if (gaps.length > 0) {\n // PUT the healed payload (merge TOWARD the floor, never away — see\n // healRuleset). Recording the specific gaps makes this the drift\n // ALARM, same as the auto-merge-off action above.\n await github.updateRuleset(repo, existingRuleset.id, healRuleset(full, check));\n actions.push(`healed ruleset drift: ${gaps.join(\"; \")}`);\n }\n }\n }\n // (No per-repo RENOVATE_TOKEN planting anymore: Renovate authenticates as\n // the reddoor-renovate GitHub App, whose RENOVATE_APP_ID / _PRIVATE_KEY are\n // org-level with all-repos visibility — a new repo needs NOTHING set. The\n // old step was already redundant for new sites (the PAT was an org secret\n // too); existing per-repo copies are inert and left to rot harmlessly.\n // docs/runbooks/renovate-app-identity.md covers the migration + rollback.)\n } catch (err) {\n const done = actions.length ? ` (completed: ${actions.join(\"; \")})` : \"\";\n const message = err instanceof Error ? err.message : String(err);\n return resultOf(site, \"failed\", `${message}${done}`, commits);\n } finally {\n // Restore the operator's branch whenever we created + checked out a maint\n // branch — on SUCCESS and, critically, on FAILURE. Without this, a push/PR\n // error after createBranch strands the checkout on the maint branch with an\n // unpushed commit, and the retry then fails at createBranch (\"branch already\n // exists\"). Best-effort: a restore failure must not mask the real outcome.\n if (maintBranch !== null && original !== null && original !== maintBranch) {\n try {\n await checkoutBranch(site.path, original);\n } catch (err) {\n console.warn(\n `warning: could not restore branch ${original} after self-updating: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n }\n\n return actions.length\n ? resultOf(site, \"applied\", actions.join(\"; \"), commits)\n : resultOf(site, \"noop\", \"already self-updating\", commits);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
withRecipe
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZQV7UQNE.js";
|
|
4
4
|
import {
|
|
5
5
|
ALL_TEMPLATES,
|
|
6
6
|
RENOVATE_ACTION_CONFIG,
|
|
7
7
|
renovateActionGaps,
|
|
8
8
|
templatesByName,
|
|
9
9
|
withRenovatePinsFrom
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-SLFG7Y76.js";
|
|
11
11
|
import {
|
|
12
12
|
listTrackedFiles,
|
|
13
13
|
removeFromIndex
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AVEQUR44.js";
|
|
15
15
|
|
|
16
16
|
// src/recipes/sync-configs.ts
|
|
17
17
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -229,4 +229,4 @@ export {
|
|
|
229
229
|
planTemplateDiffs,
|
|
230
230
|
syncConfigs
|
|
231
231
|
};
|
|
232
|
-
//# sourceMappingURL=chunk-
|
|
232
|
+
//# sourceMappingURL=chunk-FUWFLAWQ.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
approveBlockers
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-R5IMFAFR.js";
|
|
4
4
|
import {
|
|
5
5
|
ACTIVE_STATUSES,
|
|
6
6
|
allActionableVulnsTransitive,
|
|
@@ -306,4 +306,4 @@ export {
|
|
|
306
306
|
prismicAckIsLive,
|
|
307
307
|
collectPrismicDriftAlerts
|
|
308
308
|
};
|
|
309
|
-
//# sourceMappingURL=chunk-
|
|
309
|
+
//# sourceMappingURL=chunk-KJRVMMM3.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-LBYOLBW7.js";
|
|
4
4
|
import {
|
|
5
5
|
sameOwnerRepo
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-AVEQUR44.js";
|
|
7
7
|
|
|
8
8
|
// src/cli/fleet/clone-if-needed.ts
|
|
9
9
|
import { stat, readdir, mkdir } from "fs/promises";
|
|
@@ -150,4 +150,4 @@ export {
|
|
|
150
150
|
appendSkipNotice,
|
|
151
151
|
fleetWorkdir
|
|
152
152
|
};
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-LM2NHRCJ.js.map
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
isProbablyEmail,
|
|
7
7
|
parseAddresses
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-2ZAZ7ETT.js";
|
|
9
9
|
import {
|
|
10
10
|
listAllReports
|
|
11
11
|
} from "./chunk-F3JWOPTG.js";
|
|
@@ -341,4 +341,4 @@ export {
|
|
|
341
341
|
preflight,
|
|
342
342
|
approveBlockers
|
|
343
343
|
};
|
|
344
|
-
//# sourceMappingURL=chunk-
|
|
344
|
+
//# sourceMappingURL=chunk-R5IMFAFR.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
withRecipe
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZQV7UQNE.js";
|
|
4
4
|
import {
|
|
5
5
|
bumpDep,
|
|
6
6
|
readPackageJson,
|
|
@@ -150,4 +150,4 @@ export {
|
|
|
150
150
|
selfCaretRange,
|
|
151
151
|
onboard
|
|
152
152
|
};
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-S7RYFUIK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/recipes/sync-configs/templates.ts","../src/recipes/sync-configs/renovate-action.ts"],"sourcesContent":["import type { ConfigName } from \"../../types.js\";\n\nexport type ConfigTemplate = {\n config: ConfigName;\n path: string;\n contents: string;\n};\n\nconst eslint: ConfigTemplate = {\n config: \"eslint\",\n path: \"eslint.config.js\",\n contents: `import { createEslintConfig } from \"@reddoorla/maintenance/configs/eslint\";\nimport svelteConfig from \"./svelte.config.js\";\n\nexport default createEslintConfig({ svelteConfig });\n`,\n};\n\nconst prettier: ConfigTemplate = {\n config: \"prettier\",\n path: \".prettierrc.json\",\n contents: `{\n \"trailingComma\": \"all\",\n \"singleQuote\": false,\n \"printWidth\": 100,\n \"plugins\": [\"prettier-plugin-svelte\"]\n}\n`,\n};\n\nconst prettierIgnore: ConfigTemplate = {\n config: \"prettier-ignore\",\n path: \".prettierignore\",\n contents: `pnpm-lock.yaml\n.svelte-kit/\nbuild/\n.netlify/\ndist/\n`,\n};\n\nconst lighthouse: ConfigTemplate = {\n config: \"lighthouse\",\n path: \"lighthouserc.json\",\n contents: `${JSON.stringify(\n {\n $note:\n \"Generated by @reddoorla/maintenance sync-configs; edit src/configs/lighthouse.ts in the package instead.\",\n extends: \"@reddoorla/maintenance/configs/lighthouse\",\n },\n null,\n 2,\n )}\n`,\n};\n\nconst playwrightA11y: ConfigTemplate = {\n config: \"playwright-a11y\",\n path: \"playwright.config.ts\",\n contents: `export { default } from \"@reddoorla/maintenance/configs/playwright-a11y\";\n`,\n};\n\nconst svelte: ConfigTemplate = {\n config: \"svelte\",\n path: \"svelte.config.js\",\n contents: `import { createSvelteConfig } from \"@reddoorla/maintenance/configs/svelte\";\nimport adapter from \"@sveltejs/adapter-netlify\";\n\n/** @type {import('@sveltejs/kit').Config} */\nexport default createSvelteConfig({\n kit: { adapter: adapter({ edge: false, split: false }) },\n});\n`,\n};\n\n// There is deliberately NO ci.yml template. Every live fleet ci.yml carries\n// per-site values a static template cannot own (`netlify-site:`, plus\n// `permissions:` and `node-version:` inputs) — an exact-match heal against a\n// canonical byte template would strip them fleet-wide in green auto-mergeable\n// PRs (the \"armed clobber\" the 2026-08-02 architecture review flagged; it was\n// this file's `ci` entry). Ownership is split instead: the STARTER clone\n// provides each site's ci.yml shape at bootstrap, and the pinned\n// reusable-workflow ref is not reliably bumped for you — Renovate moved it\n// v1.2.0 -> v1.3.0 across 17 repos in Jul-Aug 2026, then proposed neither\n// v1.4.0 nor v1.4.1, and v1.4.1 was swept by hand across 21 repos on\n// 2026-09-01. The caller job name `ci` + the\n// reusable workflow's job `ci` produce the check context \"ci / ci\" —\n// REQUIRED_CHECK in src/recipes/self-updating/index.ts depends on that naming.\n\nconst renovateAction: ConfigTemplate = {\n config: \"renovate-action\",\n path: \".github/workflows/renovate.yml\",\n contents: `name: renovate\non:\n schedule:\n # Twice daily. GitHub's platform auto-merge is disabled fleet-wide, so Renovate\n # performs its own merges from inside this run — which means this cron is the\n # MERGE cadence, not just the PR-creation cadence.\n - cron: 0 */12 * * *\n workflow_dispatch:\npermissions:\n contents: read\njobs:\n renovate:\n runs-on: ubuntu-latest\n steps:\n # Renovate authenticates as the reddoor-renovate GitHub App (installed\n # org-wide, \"All repositories\" — future repos are auto-covered), NOT as an\n # operator PAT. Why: the App is a distinct identity, so the audit trail\n # finally distinguishes bot from human (the 2026-07-26 unreviewed-major\n # forensics took hours precisely because it couldn't); the credential is\n # permission-scoped and centrally revocable instead of a fleet-write PAT;\n # and the installation gets its own API-quota pool instead of sharing the\n # operator's. The minted token is scoped to THIS repository only\n # (create-github-app-token's default when owner/repositories are omitted).\n # RENOVATE_APP_ID / RENOVATE_APP_PRIVATE_KEY are org-level (all-repos\n # visibility) — nothing to configure per repo.\n # Digest-pinned: this workflow mints a repo-write token, so a mutable tag\n # ref here is a supply-chain hole — a retagged \\`@v3\\`/\\`@v46\\` would run\n # attacker code with that token. Pin to the commit; Renovate bumps these.\n - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0\n id: app-token\n with:\n app-id: \\${{ vars.RENOVATE_APP_ID }}\n private-key: \\${{ secrets.RENOVATE_APP_PRIVATE_KEY }}\n - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7\n - uses: renovatebot/github-action@1a96852b0384df1837619d04c60b2d10d1f9ff08 # v46.1.21\n with:\n token: \\${{ steps.app-token.outputs.token }}\n env:\n RENOVATE_REPOSITORIES: \\${{ github.repository }}\n # Commit/PR identity = the App's bot user, so gitAuthor matches the\n # token identity (mismatched authors read as \"operator pushed this\").\n # 312185038 = the bot's user id (gh api 'users/reddoor-renovate[bot]' --jq .id).\n RENOVATE_USERNAME: reddoor-renovate[bot]\n RENOVATE_GIT_AUTHOR: reddoor-renovate[bot] <312185038+reddoor-renovate[bot]@users.noreply.github.com>\n`,\n};\n\nconst renovateConfig: ConfigTemplate = {\n config: \"renovate-config\",\n path: \"renovate.json\",\n contents: `{\n \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n \"extends\": [\"github>reddoorla/.github:renovate-config\"]\n}\n`,\n};\n\nconst netlify: ConfigTemplate = {\n config: \"netlify\",\n path: \"netlify.toml\",\n contents: `[build]\n command = \"pnpm build\"\n publish = \"build/\"\n functions = \"functions/\"\n\n[build.environment]\n NODE_VERSION = \"22\"\n COREPACK_INTEGRITY_KEYS = \"0\"\n\n# Baseline security headers for all responses. CSP is emitted per-response by\n# SvelteKit (see \\`kit.csp\\` in svelte.config.js) so it is intentionally omitted\n# here to avoid conflicting duplicates.\n[[headers]]\n for = \"/*\"\n [headers.values]\n Strict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n X-Content-Type-Options = \"nosniff\"\n X-Frame-Options = \"SAMEORIGIN\"\n Referrer-Policy = \"strict-origin-when-cross-origin\"\n Permissions-Policy = \"camera=(), microphone=(), geolocation=(), interest-cohort=()\"\n Cross-Origin-Opener-Policy = \"same-origin\"\n\n[[headers]]\n for = \"/favicon.png\"\n [headers.values]\n Cache-Control = \"public, max-age=31536000, immutable\"\n\n[[headers]]\n for = \"/_app/immutable/*\"\n [headers.values]\n Cache-Control = \"public, max-age=31536000, immutable\"\n`,\n};\n\nexport const ALL_TEMPLATES: ConfigTemplate[] = [\n eslint,\n prettier,\n prettierIgnore,\n lighthouse,\n playwrightA11y,\n svelte,\n renovateAction,\n renovateConfig,\n netlify,\n];\n\nexport function templatesByName(which: ConfigName[]): ConfigTemplate[] {\n return ALL_TEMPLATES.filter((t) => which.includes(t.config));\n}\n","import type { ConfigName } from \"../../types.js\";\n\n/**\n * `.github/workflows/renovate.yml` (the `renovate-action` template) is\n * compliance-checked, not byte-matched, for the same reason `svelte.config.js`\n * and `netlify.toml` already are (see `isSvelteConfigCompliant` /\n * `isNetlifyConfigCompliant` in `sync-configs.ts`): an exact overwrite is the\n * wrong operation whenever a file legitimately diverges from the template\n * without being wrong. For renovate.yml there are TWO such legitimate\n * divergences, both observed live on 2026-08-31 (issue #651):\n *\n * 1. Renovate bumps its own digest pins. The template pins\n * `renovatebot/github-action@1a96852b...` (# v46.1.21); a site Renovate\n * maintains moves that pin forward on its own (reddoor-starter was already\n * at `@e09d604f...` / v46.2.2). A byte-match recipe reads that as drift and\n * opens a PR that DOWNGRADES the pin — reverting Renovate's own\n * supply-chain update, the exact thing the template's own comment says\n * Renovate is supposed to do. Observed live: it opened\n * `reddoorla/reddoor-starter-blux#1`, closed unmerged.\n * 2. YAML scalar quoting. The template writes an unquoted twice-daily cron and\n * `RENOVATE_USERNAME: reddoor-renovate[bot]` unquoted; a site's prettier\n * may render both quoted instead. Both forms are prettier-clean (verified\n * against the repo's own prettier), so prettier never normalizes one\n * toward the other — once a site's copy diverges in quoting, a byte-match\n * recipe reports drift FOREVER.\n *\n * The fix, following the svelte.config/netlify.toml precedent exactly: stop\n * asking \"is this file byte-identical to the template?\" and ask \"does it\n * still satisfy the invariants we actually own?\". `renovateActionGaps`\n * returns that — a `string[]` of human-readable gaps, empty meaning\n * compliant and left alone, the same shape as `rulesetGaps` (src/github/rulesets.ts)\n * and `renovateGaps` (src/audits/protection-coverage.ts).\n *\n * When a file genuinely IS non-compliant (e.g. its cron changed) the recipe\n * heals it by writing the template — but writing the template verbatim would\n * itself re-introduce the downgrade bug above, overwriting the site's newer\n * digest pins with the template's older ones. `withRenovatePinsFrom` carries\n * the site's own (still-digest-pinned) refs forward onto the template before\n * it is written — see that function's own doc comment for exactly what\n * guarantee this is (and, as importantly, isn't).\n *\n * Every check below runs against a COMMENT-STRIPPED copy of the file (see\n * `stripComments`). A raw-text `.match()`/`.includes()` takes the FIRST hit\n * anywhere in the file, comments included — a canonical pin, cron, or job\n * body sitting in a `#` comment above the real (non-compliant) line reads as\n * compliant, and worse, `withRenovatePinsFrom` would scrape a sha out of a\n * comment like `# old: uses: actions/checkout@1111…1111 # v4` and carry a\n * *commented* pin forward into the healed file — a pin downgrade delivered by\n * the very recipe that exists to prevent pin downgrades.\n */\n\nconst DIGEST_RE = /^[0-9a-f]{40}$/;\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Strip full-line `#` comments before any compliance check or ref extraction\n * runs, so a canonical-looking line that exists only in a comment can never\n * read as the real thing.\n *\n * Full-line only: a trailing `# vX.Y.Z` after a real `uses:` line is\n * meaningful (it's the human-readable version tag) and must survive — only\n * strip a line whose first non-whitespace character is `#`. This is safe\n * specifically because this workflow's YAML has no block scalars (`|`/`>`),\n * so there is no line in a compliant or non-compliant renovate.yml where a\n * leading `#` is literal string content rather than a comment.\n */\nfunction stripComments(contents: string): string {\n return contents.replace(/^[ \\t]*#[^\\n]*$/gm, \"\");\n}\n\n/** A `uses:` line, anchored to the start of a line (allowing only leading\n * indentation and an optional `- ` list marker before `uses:`) so a match\n * can only ever land on a real step, never on prose inside a comment that\n * happens to contain the word `uses:`. Always run against comment-stripped\n * text (see `stripComments`) — the anchor alone does not exclude comments,\n * since a full-line comment's content can itself start with `- uses:` at\n * matching indentation. */\nconst USES_LINE_RE = /^([ \\t]*(?:- )?)uses:\\s*([\\w.-]+\\/[\\w.-]+)@(\\S+)(?:[ \\t]*#[^\\r\\n]*)?/gm;\n\n/** The ref (and trailing `# vX.Y.Z` comment, if any) a `uses: <slug>@<ref>`\n * line carries in `contents`, or null if that action isn't referenced.\n * Callers must pass comment-stripped text — see `stripComments`. */\nfunction usesRefAndComment(\n contents: string,\n slug: string,\n): { ref: string; comment: string } | null {\n const re = new RegExp(\n `^[ \\\\t]*(?:- )?uses:\\\\s*${escapeRegExp(slug)}@(\\\\S+)([ \\\\t]*#[^\\\\r\\\\n]*)?`,\n \"m\",\n );\n const m = contents.match(re);\n if (!m) return null;\n return { ref: m[1]!, comment: m[2] ?? \"\" };\n}\n\n/** Gap text for one `uses:` action that must be present AND digest-pinned.\n * This workflow mints a repo-write token (the App-token step), so a mutable\n * tag ref on ANY step here is a supply-chain hole — a retagged `@v3`/`@v46`\n * would run attacker code with that token. (The three actions checked here\n * are the ones this workflow ships with by default; `renovateActionGaps`\n * separately widens this to ANY `uses:` line, so an action added later is\n * covered too.) `contents` must already be comment-stripped. */\nfunction pinGap(slug: string, contents: string): string | null {\n const found = usesRefAndComment(contents, slug);\n if (!found || !DIGEST_RE.test(found.ref)) {\n return `${slug} is missing or not digest-pinned (needs @<40-hex-sha>) — this workflow mints a repo-write token, so a mutable tag ref is a supply-chain hole`;\n }\n return null;\n}\n\n/** The three actions this workflow ships with by default and always requires,\n * present and digest-pinned. Checked individually (via `pinGap`, above) so a\n * gap can say which one of the three is the problem; the widen loop in\n * `renovateActionGaps` then covers every OTHER `uses:` line so a step added\n * later can't slip in on a mutable ref unnoticed. */\nconst KNOWN_PINNED_ACTIONS = [\n \"renovatebot/github-action\",\n \"actions/create-github-app-token\",\n \"actions/checkout\",\n];\n\nconst APP_AUTH_MARKERS = [\n \"vars.RENOVATE_APP_ID\",\n \"secrets.RENOVATE_APP_PRIVATE_KEY\",\n \"steps.app-token.outputs.token\",\n];\n\n/** Gaps in a site's `.github/workflows/renovate.yml`. Empty = compliant,\n * leave it alone — see the module doc comment above for why this is\n * compliance-checked rather than byte-matched. */\nexport function renovateActionGaps(contents: string): string[] {\n const gaps: string[] = [];\n const withoutComments = stripComments(contents);\n\n for (const slug of KNOWN_PINNED_ACTIONS) {\n const gap = pinGap(slug, withoutComments);\n if (gap) gaps.push(gap);\n }\n\n // Widen the pin check to every OTHER `uses:` line in the file — the doc\n // comment on `pinGap` says a mutable ref on ANY step is a supply-chain\n // hole; without this, a step added later (e.g. `pnpm/action-setup@v4`)\n // would pass clean just because it isn't one of the three known actions.\n // A local `uses: ./.github/actions/foo` path ref never matches — the regex\n // requires a literal `@<ref>`, which a bare path reference doesn't have.\n for (const m of withoutComments.matchAll(USES_LINE_RE)) {\n const slug = m[2]!;\n const ref = m[3]!;\n if (KNOWN_PINNED_ACTIONS.includes(slug)) continue; // already covered above\n if (!DIGEST_RE.test(ref)) {\n gaps.push(\n `${slug}@${ref} is not digest-pinned (needs @<40-hex-sha>) — this workflow mints a repo-write token, so every action reference must be digest-pinned`,\n );\n }\n }\n\n const missingAuth = APP_AUTH_MARKERS.filter((m) => !withoutComments.includes(m));\n if (missingAuth.length > 0) {\n gaps.push(\n `App auth is not fully wired — missing ${missingAuth.join(\", \")} (Renovate authenticates as the reddoor-renovate GitHub App, not an operator PAT)`,\n );\n }\n\n if (withoutComments.includes(\"RENOVATE_TOKEN\")) {\n gaps.push(\n \"contains RENOVATE_TOKEN — that is the retired operator-PAT identity the App migration replaced\",\n );\n }\n\n // Checked (and reported) as two independent gaps, not one folded string, so\n // a caller can tell which field actually failed.\n if (!/RENOVATE_USERNAME:\\s*[\"']?reddoor-renovate\\[bot\\]/.test(withoutComments)) {\n gaps.push(\"RENOVATE_USERNAME does not identify reddoor-renovate[bot]\");\n }\n if (!/RENOVATE_GIT_AUTHOR:\\s*[\"']?reddoor-renovate\\[bot\\]/.test(withoutComments)) {\n gaps.push(\"RENOVATE_GIT_AUTHOR does not identify reddoor-renovate[bot]\");\n }\n\n if (\n !/RENOVATE_REPOSITORIES:\\s*[\"']?\\$\\{\\{\\s*github\\.repository\\s*\\}\\}[\"']?/.test(withoutComments)\n ) {\n gaps.push(\n \"RENOVATE_REPOSITORIES is not scoped to ${{ github.repository }} — this workflow must only ever touch its own repo\",\n );\n }\n\n if (!/cron:\\s*[\"']?0 \\*\\/12 \\* \\* \\*[\"']?/.test(withoutComments)) {\n gaps.push(\n 'cron is not the twice-daily \"0 */12 * * *\" schedule — this cron IS the fleet\\'s merge cadence (platform auto-merge is disabled fleet-wide, so Renovate merges from inside this run)',\n );\n }\n\n if (!withoutComments.includes(\"workflow_dispatch\")) {\n gaps.push(\"missing workflow_dispatch trigger\");\n }\n\n // A `permissions:` key is present somewhere AND a `contents: read` line\n // (optionally quoted) exists somewhere — not anchored to top-level-first-key,\n // since this predicate doesn't actually need to assert that shape (a\n // job-level permissions block satisfies the same invariant), and requiring\n // `contents: read` to be the first entry under `permissions:` false-positived\n // on both quoting (`contents: \"read\"`) and ordering (`contents: read` listed\n // after another key, e.g. `pull-requests: write`).\n const hasPermissionsKey = withoutComments.includes(\"permissions:\");\n const hasContentsRead = /^[ \\t]*contents:\\s*[\"']?read[\"']?[ \\t]*$/m.test(withoutComments);\n if (!hasPermissionsKey || !hasContentsRead) {\n gaps.push(\"permissions: does not grant contents: read\");\n }\n\n return gaps;\n}\n\n/**\n * The canonical template with each action's `uses:` ref replaced by the ref\n * the site already has, when the site's is digest-pinned.\n *\n * Why: when a file IS genuinely non-compliant (say its cron changed) the\n * recipe heals it by writing the template — and writing the template verbatim\n * would put the template's OLDER pins over the site's newer ones, re-\n * introducing the downgrade this fix removes. So carry the site's pins\n * forward onto the (otherwise-canonical) healed file.\n *\n * This is PIN-NEUTRAL, not downgrade-avoiding: it substitutes the site's ref\n * whenever it is 40 hex characters, with no notion of \"newer\" — digests are\n * unorderable, so there is no way to tell from the strings alone. It never\n * changes a digest-pinned ref in either direction; some fleet sites carry a\n * pin OLDER than the template's and will heal onto their own older pin\n * (defensible — Renovate re-advances it on its own), so \"pin-neutral\" is the\n * accurate description, not \"downgrade-avoiding\".\n *\n * `current === null` (no existing file) returns the template unchanged — there\n * is nothing to carry forward. A non-digest ref on the site (a mutable tag) is\n * never substituted in, since that would carry the mutable-ref hole forward\n * instead of healing it. Likewise, a ref that appears only inside a `#`\n * comment in `current` is never carried forward (see `stripComments`) — a\n * commented sha must never be scraped and pinned into the healed file.\n */\nexport function withRenovatePinsFrom(template: string, current: string | null): string {\n if (current === null) return template;\n const currentWithoutComments = stripComments(current);\n return template.replace(USES_LINE_RE, (full, prefix: string, slug: string) => {\n const found = usesRefAndComment(currentWithoutComments, slug);\n if (!found || !DIGEST_RE.test(found.ref)) return full;\n return `${prefix}uses: ${slug}@${found.ref}${found.comment}`;\n });\n}\n\n/** The `ConfigName` for `.github/workflows/renovate.yml`. Exported once here\n * (rather than re-declared as a local constant in both `sync-configs.ts` and\n * `self-updating/index.ts`) so the two call sites can't drift apart. */\nexport const RENOVATE_ACTION_CONFIG: ConfigName = \"renovate-action\";\n"],"mappings":"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","names":[]}
|
|
@@ -1421,7 +1421,7 @@ function distinctive(terms) {
|
|
|
1421
1421
|
return multiWord || hasNumber || properNoun;
|
|
1422
1422
|
});
|
|
1423
1423
|
}
|
|
1424
|
-
var MIN_TOKEN =
|
|
1424
|
+
var MIN_TOKEN = 3;
|
|
1425
1425
|
function findTerm(haystack, term) {
|
|
1426
1426
|
if (haystack.includes(normalize(term))) return "exact";
|
|
1427
1427
|
const tokens = normalize(term).split(/[^a-z0-9]+/).filter((t) => t.length >= MIN_TOKEN);
|
|
@@ -1448,14 +1448,15 @@ function backstopAbsent(assertions, siteText, searchTermsByClaim, siteFullyRead)
|
|
|
1448
1448
|
}
|
|
1449
1449
|
scattered ??= term.trim();
|
|
1450
1450
|
}
|
|
1451
|
-
if (scattered !== null) return { ...a, nearbyMention: scattered };
|
|
1452
1451
|
if (!siteFullyRead) {
|
|
1453
1452
|
return {
|
|
1454
1453
|
...a,
|
|
1455
1454
|
verdict: "unverified",
|
|
1455
|
+
nearbyMention: scattered,
|
|
1456
1456
|
unverifiedReason: "The site was larger than we read in one pass, so we cannot say this is absent from it."
|
|
1457
1457
|
};
|
|
1458
1458
|
}
|
|
1459
|
+
if (scattered !== null) return { ...a, nearbyMention: scattered };
|
|
1459
1460
|
return a;
|
|
1460
1461
|
});
|
|
1461
1462
|
}
|
|
@@ -2956,4 +2957,4 @@ export {
|
|
|
2956
2957
|
mergeFixes,
|
|
2957
2958
|
runProspectAudit
|
|
2958
2959
|
};
|
|
2959
|
-
//# sourceMappingURL=chunk-
|
|
2960
|
+
//# sourceMappingURL=chunk-TR3HFH6J.js.map
|