Root-home the hugo-extended pin, adopt a two-step update flow, open Renovate managers - #2747
Merged
chalin merged 34 commits intoAug 27, 2026
Merged
Conversation
…ugo) Back-ports docsy-example#487's flow to the family reference repo: - update:hugo goes script-free (--ignore-scripts) and gets a root entry point, making the renovate.json5 comment's command real; approve:hugo approves the pinned script and rebuilds so the hugo binary lands. - docsy.dev update:packages default-excludes hugo-extended (the approval-desync false clean lived there, the dep's manifest); root update:packages:not-hugo retired (filtered a manifest without the dep). - Renovate hugo-extended rule scoped to version updates only, with the security-updates path documented (vulnerabilityAlerts bypasses enabled:false; Dependabot security updates run config-free); dormant while the npm manager is off. - Maintainer notes document the two-step flow (don't build between the steps). - Drive-by: last ref shortcode converted to a path link (adding-content.md; ref links render from the dev baseURL and are excluded from link checking -- the docsy#2743 blind spot).
- Comment shrinks to the docs link (flow + security paths homed at maintainer notes); keeps only the config-local fact: the rule is dormant and why it stays. - The specific-version install alternative gets --ignore-scripts: without it, step 1 runs the very script the flow defers.
- Docs match the real gate (ported from docsy-example's honest wording): the approval gates the install script only, the hugo binary self-installs at first use; builds between the steps would fetch the unreviewed binary, not fail. - Automation paragraph states what runs here: Renovate excludes version bumps; config-free Dependabot security updates can still bump (evidence: google#2701), failing CI until approved. - update:hugo invokes npm via npm_execpath (the npm that launched the script), so PATH shims can't intercept the inner install; validated by dry-run.
- update:hugo installs via -w from the root project and chains the supply-chain audit: npm applies root overrides only while re-resolving (the audit test's documented invariant), so a bump that drops the adm-zip override now goes red at the terminal instead of first in CI (red-first proven: the chained audit caught the real downgrade during validation). - approve:hugo syncs the tree to the lock (npm ci --ignore-scripts) before approving, closing the stale-tree false-approve on Dependabot checkouts; --allow-scripts-pin passed explicitly so user npm config can't widen the approval to all versions; rebuild + theme deps via the existing _install:safe:post. - Root update:packages carries -x hugo-extended too, covering forwarded --workspaces/-w args. - docsy.dev's update:hugo delegates to root (the workspace-local install form escaped the root overrides project-wide). - Maintainer notes match: audit step named, approve semantics, -w specific-version form. Validated: update:hugo dry-run + red-first audit; approve:hugo live end-to-end (binary lands).
The dep was the last hugo-governance piece still declared in docsy.dev: the lock, the adm-zip override it justifies, the allowScripts pin, the approve/update scripts, and the rebuild helper are all root-scoped, and root's own test suites need hugo too. With the declaration at root: - update:hugo drops the -w indirection; root overrides apply natively (dry-run: no adm-zip drift, audit green). - docsy.dev sheds its delegate scripts and the now-vestigial ncu exclusion. - hugo-versions test reads the pin from root; docs cites retargeted, unused link defs dropped.
…safe halves - update:hugo requires the target version (npm run update:hugo -- X.Y.Z): the maintainer reviews the release before anything lands, instead of floating on latest; plain npm (repo idiom; deliberate installs use standard npm invocations); audit chain via the named _test:supply-chain. - install:safe splits into _install:safe:pre + _install:safe:post; approve:hugo is now visibly install:safe with the approval inserted between the halves (no duplicated ci string, no flag drift). - Audit pins updated red-first (install:safe pin verified firing); approve:hugo's reviewed form pinned too (it wields the approval authority). - Validated live: no-arg usage error; no-op run at the current version (pin+lock untouched, audit green); approve:hugo end-to-end (binary lands, theme deps restored).
- Opens the managers (enabledManagers allowlist dropped): npm, GitHub Actions, and Docker updates start flowing, with targeted opt-outs -- the docsy-example model. - Homes the settings rationale at maintainer-notes' new Dependency-updates section; config comments shrink to a pointer. - Leaner than the reference where docsy differs: timezone (UTC Sunday suffices weekly), gomod (Renovate defaults off go-directive bumps; bare theme/go.mod has nothing else), internalChecksFilter (strict is the default). - Adds the bootstrap/Font-Awesome opt-out (update:dep pairs the ScrollSpy-patch refresh); spell words promoted to the project dictionary.
Owner doctrine settled in review: a major-update PR is the GA notification -- constrain reactively, at decision time, not ahead. The cap pre-silenced the 3.0.0 GA signal and forced a quarterly manual re-check; the maintainer notes keep the migration facts.
- Package-rules bullet restructured as a sublist (owner edit). - Script-versions' Renovate pointer targets the Dependency-updates section (the settings' home) instead of the raw config. - Release helper scripts lists approve:hugo beside update:hugo. Verified untouched claims against the repo: test-suite table vs package.json scripts, CI's test:full coverage note, no stale update-script references.
…ated - update:hugo chained the full audit, whose allowScripts assertion keys on the locked version -- red on every real bump until approve:hugo runs (reviewer repro'd; confirmed). The audit now closes approve:hugo, when the flow's invariants all hold; CI still guards the gap between steps. - The version argument must be exact X.Y.Z: tags and ranges (latest, ^0.164) previously passed the bare non-empty check and -E laundered them into a resolved pin, defeating the review-first contract. - Validated live: tag/range/no-arg rejected pre-npm; real 0.163.0 round-trip through both steps, audit green both directions, tree restored byte-identical.
…chain - update:dep installed bootstrap/FA@latest into the root manifest (theme owns them; broken since the theme/ move, and this PR's Renovate opt-outs made it the load-bearing route). Replaced by guarded update:bootstrap / update:fontawesome: exact stable version, -w theme install, theme-lock sync, ScrollSpy reminder on the bootstrap route; theme's update:dep retires, its bulk update now excludes both deps. - Audit byte-pins the whole chain (update:hugo, the new bump scripts, _sync:theme-lock, _test:supply-chain), extends the hook-absence inventory to every script in it, and keeps bootstrap/FA out of the root manifest (r3's mutation probe showed the unpinned bodies were CI-invisible). - Version guards and the hugo-versions assertion reject leading-zero components (npm semver.valid parity); prereleases documented as deliberately rejected. - Renovate schedule comment names the UTC semantics. Validated live: leading-zero/tag/prerelease rejected pre-npm; no-op bootstrap round-trip at the current pin (locks untouched, reminder fires); audits green.
A -w theme install prunes theme/node_modules (lock-external to the workspace tree); both bump scripts now close with install:theme-deps, so the build stays healthy without a separate repair step. Caught by test:full after the r3 validation round-trip.
The three bump scripts shared the guard and install shape verbatim; _update:pin cases on the dep, the update:DEP aliases delegate. Audit pins the dispatcher once plus the three one-line aliases. Validated: usage errors name the invoked alias; unknown dep rejected; no-op hugo + bootstrap round-trips clean (locks untouched, theme tree intact, ScrollSpy reminder fires).
The case dispatcher served three deps that are really two flows: update:hugo (root devDep) and update:theme-dep (generic guarded theme-workspace bump -- any theme dep, nothing per-dep to maintain). Per-dep aliases die; npm appends invocation args after an alias chain's last command, which is what had forced the dispatcher shape. Validated: guard matrix (no-arg, tag, leading-zero) rejects pre-npm; no-op bootstrap round-trip clean (theme tree intact, reminder fires); audit green.
The audit reads only static inputs (manifests, locks, npmrc, workflows), all final once the script-free ci and approval complete -- so it can gate the newly approved installer instead of detecting problems after it has executed (adm-zip, the override's subject, is the installer's own dep). Chain: sync, approve, audit, rebuild. Validated live: audit passes before the rebuild lands the binary; failure now stops the chain pre-execution.
npm install adds an absent name, so a typo'd or unrelated package would silently expand @docsy/theme's published dependency surface; the script now rejects names missing from theme dependencies before npm runs. Validated: nosuchdep and lodash rejected pre-npm; bootstrap no-op round-trip clean.
The inline bash outgrew its home: two validations, an install, and a follow-up chain in a JSON string, unreadable and untestable, byte-pinned twice per fix. The helper follows the rebuild-hugo-extended.mjs idiom (npm's JS CLI via npm_execpath, no command shell, isMain entry) and carries unit tests for the guards, plans, and fail-stop; the npm scripts collapse to one-line entries, which is all the audit now pins. Same interfaces and semantics: stable X.Y.Z only, theme bumps for existing deps only, install -> lock sync -> tree restore -> reminder. Validated live: guard matrix rejects pre-npm; no-op hugo + bootstrap round-trips clean.
- One update:dep -- PKG X.Y.Z entry point replaces update:hugo/update:theme-dep: the helper plans by which manifest declares PKG (root devDeps -> root install; theme deps -> theme chain; else rejected), so there are no mode tokens or reserved names to collide (r4: update:theme-dep -- hugo reached the hugo-extended path), and argv arity is enforced (extra args were silently dropped). - Unit tests pin the execution contract (process.execPath + npm_execpath + repo-root cwd + env passthrough, the rebuild-hugo-extended.test.mjs idiom), the full theme plan (last-token-only assertions could false-clean), the missing-execpath fail-close, and both real manifests' declarations. - Maintainer notes: redoc 3.x wording matches the dropped Renovate cap (the 3.x PR will arrive; the shortcode URL change is part of that review). Validated live: guard matrix (no-arg, undeclared, tag, extra-arg) rejects pre-npm; no-op hugo-extended + bootstrap round-trips clean.
…ants Replace the dispatch-by-declaration update:dep with per-target scripts: - update:hugo passes hugo-extended -D; update:theme-dep passes PKG -w theme plus the theme follow-ups (lock sync, tree restore, ScrollSpy reminder) as an explicit npm-script chain. - The helper (scripts/update-dep.mjs) now plans a single install and owns only the cross-target invariants: stable X.Y.Z pin, declared-dependency check against the targeted manifest, -E --ignore-scripts, npm-cli spawn contract. Flags pass through verbatim. - Tests and audit pins updated for the new contract; maintainer notes and the Renovate comment retargeted.
The byte-exact script assertions are goldens, not behavior tests: name the pattern once, home them in a SCRIPT_PINS table with their own test, and derive the implicit-hook screen from the table so a new pin cannot miss its hook guard.
…e comment - Move the bootstrap/FA root-absence asserts out of the spec-form test into their own ownership test: they guard where a dependency is declared, not what shape its spec takes. - Open scripts/update-dep.mjs with its role and the scripts/helper division of responsibility (target policy vs cross-target invariants).
Keep the reviewed forms of the CI install path, approve:hugo, and the audit entry point -- the surfaces where drift detection is mechanical value (tree gate at approval time, no behavioral test possible). Drop the update:hugo/update:theme-dep/_sync:theme-lock entries: maintainer-run and helper-guarded, their per-change re-baseline cost outweighs drift detection; enforcement belongs to review. Rename pins -> reviewed forms (pin is version vocabulary).
- Audit comment: colon-introduction wanted a semicolon, not a dash. - maintainer-notes and changelog descriptions restated their titles; replace with scope fragments per the descriptions standard.
- hugo-versions.test.mjs imports STABLE_SEMVER instead of carrying a second copy of the regex (this branch had silently synced them). - Changelog: drop the inline katex ignore now that the project dictionary carries it. - Maintainer notes: the security-bump paragraph points at step 2 instead of restating the approval mechanism; the update:theme-dep bullet gains the chain's why (workspace installs prune the theme tree).
- REVIEWED_SCRIPTS header: cut the threat-actor narrative and the obvious remedy clause; keep membership, deliberate duplication, and the why of byte equality. - approve:hugo table comment: keep only the flag rationale homed here; the maintainer notes own the audit-before-installer ordering. - Flow step 1: 'required … rejected' said the same thing twice.
Same-session comments re-reviewed with inverted suspicion: the helper header dropped its invariants inventory (each is homed at its implementation line), the STABLE_SEMVER comment its rejected-forms enumeration, the ownership-check comment its redundant clause.
npm appends caller args to the script's own, so any extra token reached npm as an install argument -- under the old update:dep muscle memory (update:hugo -- hugo-extended X.Y.Z), a bare latest-floating package arg. Unknown tokens now fail to usage; scripts keep owning which flags to pass.
chalin
force-pushed
the
chalin-m24-hugo-update-flow-2026-0826
branch
from
August 27, 2026 19:35
cbbb8ec to
ef4f1cd
Compare
'-w theme' put a bare word in the allowed-flag vocabulary, and a bare token clears npm's parser as a package argument -- 'theme' is a real (dormant) registry name, so update:hugo could be handed an unreviewed install. With --workspace=theme every allowed token is flag-shaped: no vocabulary recombination can smuggle a package. Also: the version is appended by the caller, not npm, in update:theme-dep (JSDoc).
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.
hugo-extendedpin to the root manifest: the lock, theadm-zipoverride it justifies, and theallowScriptsentry are all root-scoped, so rootoverridesnow apply natively to Hugo bumps.update:hugobumps the pin script-free, at an explicit, reviewed stable version.approve:hugoapproves the pinned version and audits the tree before the installer runs: override drift fails at the terminal, not first in CI.update:theme-depreplacesupdate:dep, which installed@latestinto the wrong manifest. Both scripts run a unit-tested helper that rejects unknown flags and undeclared packages; the supply-chain audit byte-pins the install- and approval-authority script entries.enabledManagersallowlist: the npm and GitHub Actions managers activate, with opt-outs for the manually routed deps above.<3cap: a major-update PR is the GA notification; constraints are added reactively at decision time.refshortcode to a path link (refoutput renders from the devbaseURL, inside the link checker's localhost exclusion -- the blind spot found on Blog+CL 0.17.0: cover the agent llms.txt directive; fix nav-page anchor #2743).