Skip to content

Root-home the hugo-extended pin, adopt a two-step update flow, open Renovate managers - #2747

Merged
chalin merged 34 commits into
google:mainfrom
chalin:chalin-m24-hugo-update-flow-2026-0826
Aug 27, 2026
Merged

Root-home the hugo-extended pin, adopt a two-step update flow, open Renovate managers#2747
chalin merged 34 commits into
google:mainfrom
chalin:chalin-m24-hugo-update-flow-2026-0826

Conversation

@chalin

@chalin chalin commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

@chalin chalin added this to the 0.17.0 milestone Aug 27, 2026
@chalin chalin changed the title Adopt the two-step hugo-extended update flow (update:hugo + approve:hugo) Root-home the hugo-extended pin and adopt a two-step update flow Aug 27, 2026
@chalin chalin changed the title Root-home the hugo-extended pin and adopt a two-step update flow Root-home the hugo-extended pin, adopt a two-step update flow, open Renovate managers Aug 27, 2026
chalin added 27 commits August 27, 2026 15:31
…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).
chalin added 6 commits August 27, 2026 15:31
- 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
chalin force-pushed the chalin-m24-hugo-update-flow-2026-0826 branch from cbbb8ec to ef4f1cd Compare August 27, 2026 19:35
'-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).
@chalin
chalin marked this pull request as ready for review August 27, 2026 20:00
@chalin
chalin merged commit 2c13109 into google:main Aug 27, 2026
18 checks passed
@chalin
chalin deleted the chalin-m24-hugo-update-flow-2026-0826 branch August 27, 2026 20:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant