From 90bdf1f0941e557097378354ec8a361852404f5d Mon Sep 17 00:00:00 2001 From: Omry Yadan Date: Sun, 18 Oct 2026 23:54:20 +0800 Subject: [PATCH] Clarify Node maintenance setup and reference documentation Make the installation path usable for standalone Node applications, libraries, and websites using either npm or pnpm. Separate the basic weekly-security/manual-upgrades setup from the weekly-security/quarterly-upgrades shared defaults, and show complete local validation and caller setup. Consolidate scheduling, inheritance, maintenance behavior, and migration guidance in their reference pages. Correct omitted-list semantics, remove stale installation-specific notes, and move the existing shared-schedule example check to the scheduling guide. Runtime behavior, workflow permissions, and policy defaults are unchanged. Validation: 1,026 public tests; Ruff lint and format; runtime type checks; 44 local links and anchors; 15 parsed JSON/YAML examples; and npm/pnpm basic/shared preflight examples with modeled installed tool versions. --- README.md | 122 ++++-------- docs/audit-schedules.md | 191 +++++++++--------- docs/node-maintenance.md | 299 +++++++++++++++++------------ docs/node-migration.md | 98 +++++----- docs/shared-defaults.md | 213 ++++++++------------ node/maintenance.md | 225 ++++++++++------------ node/tests/test_shared_defaults.py | 8 +- 7 files changed, 555 insertions(+), 601 deletions(-) diff --git a/README.md b/README.md index d42dddb..94b15e8 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,48 @@ # Shared GitHub Actions Reusable GitHub workflows for organization-owned and personal repositories. -Shared scripts stay here; each repository keeps its configuration and pins -the workflow to a reviewed commit. +Each repository keeps its project configuration and pins the shared workflows +to a reviewed commit. -## Available workflows +## Node dependency maintenance -| Workflow | What it does | Get started | -| --- | --- | --- | -| Node maintenance | Fix security advisories, propose dependency upgrades, validate the project, and open one PR for review. | [Setup guide](docs/node-maintenance.md) | - -## One maintenance PR per project - -**Security fixes and routine upgrades, together in one PR.** Choose security-only -maintenance or limit routine upgrades to patch, minor, or major versions. Reruns -update the same PR, so each audit does not add another PR to review. - -- **Combined review:** security fixes and routine upgrades in one maintenance PR. [Comparison](docs/node-maintenance.md#supported-projects). -- **Validation evidence:** a frozen install and selected project checks before review. -- **Clear decisions:** see what was fixed, which pins need review, and what is blocked. - -The Node workflow follows your declared Node and npm/pnpm versions within its -[tested combinations](docs/node-maintenance.md#toolchain-selection). It requires a -standalone Node project. Project pins and deployment remain under your control. -The built-in GitHub token works; a GitHub App is optional. -Version 6 replaces the Docusaurus interface. Existing installations must follow -the [migration checklist and rollout order](docs/node-migration.md). -The [setup guide](docs/node-maintenance.md#set-it-up) includes a pinned caller workflow, -local validation checks, and steps for the first audit. - -## Shared defaults for multiple installations - -Integration version 6 lets many projects follow one authoritative JSON defaults -file in any public GitHub repository. Each project keeps local overrides and its -own validation/deployment setup. Conflicting declarations fail preflight and update -one reusable configuration issue; nothing is migrated automatically. - -See [shared defaults setup and fleet updates](docs/shared-defaults.md). The -[recommended defaults](defaults/node-maintenance.json) select Node 24, pnpm 11.21.0 or npm 11.9.0, -and a minimum ten-day cooldown. +Maintain dependencies for a standalone Node application, library, or website +using npm or pnpm. The workflow attempts compatible security fixes, optionally +proposes routine upgrades, and validates the result with a frozen install and +your project's checks. -## Default schedule and overrides +All audit kinds maintain **one open dependency PR per repository**. Reruns update +that PR rather than opening a PR for each dependency. Reports show the proposed +versions, validation results, and any blocked findings. Nothing merges automatically. -With **integration version 6**, projects that follow the -[recommended defaults](defaults/node-maintenance.json) inherit this schedule: +**[Set up Node maintenance](docs/node-maintenance.md)** for supported versions, +complete npm and pnpm instructions, and your first audit. -```json -{ - "maintenance": {"mode": "security-and-maintenance"}, - "schedule": { - "security": ["0 2 * * 1"], - "upgrades": ["0 2 1 */3 *"] - } -} -``` +## Choose your maintenance policy -Security checks run every Monday at 02:00 UTC. Routine upgrades run at 02:00 UTC -on the first day of January, April, July, and October. Upgrade runs also check -security. All runs maintain the same dependency PR. - -To change the default for your projects, edit `schedule` in your shared defaults -file. To override one project, set `schedule.security` or `schedule.upgrades` in -that project's `.github/node-maintenance.json`. Each local list replaces the inherited -list of the same name; omitted lists continue to inherit. - -For example, these local fields keep inherited security checks and make routine -upgrades manual-only: - -```json -{ - "maintenance": {"mode": "security-only"}, - "schedule": {"upgrades": []} -} -``` - -Keep the caller workflow's `on.schedule` cron entries equal to the combined -resolved lists. A shared-defaults edit does not rewrite installed workflow files; -preflight reports a mismatch until their triggers are updated. The -[scheduling guide](docs/audit-schedules.md) covers caller setup, other cadences, -manual runs, and cron-list shorthand. Existing explicit local schedules keep -overriding shared defaults. - -## Reference - -- [Setup, first run, and updates](docs/node-maintenance.md) -- [Maintenance policy](node/maintenance.md) -- [Caller template](node/caller.yml) -- [Integration contract](node/contract.json) - -Currently supports GitHub.com. The Node implementation retains its -BSD 3-Clause license. +| Setup | Automatic security checks | Automatic routine upgrades | +| --- | --- | --- | +| Basic setup in the guide | Weekly | Disabled; run upgrades manually | +| Follow this repository's shared defaults | Weekly | Every three months | + +Both setups use a ten-day release cooldown. The basic setup follows your +supported project toolchain. The [shared defaults](defaults/node-maintenance.json) +require Node 24 and pnpm 11.21.0 or npm 11.9.0. Local settings can override +inherited defaults; conflicts with project declarations fail preflight and +update one reusable configuration issue. + +For several installations, [share defaults from a public GitHub JSON file](docs/shared-defaults.md). +To change cadence, disable automatic upgrades, or run an audit on demand, use +the [scheduling guide](docs/audit-schedules.md). GitHub cron entries remain in +each repository's caller workflow and must match its resolved configuration. + +## Documentation + +- [Setup, first run, updates, and troubleshooting](docs/node-maintenance.md) +- [Schedules and manual audits](docs/audit-schedules.md) +- [Shared defaults for multiple installations](docs/shared-defaults.md) +- [Maintenance behavior, reports, and permissions](node/maintenance.md) +- [Migrate an earlier Docusaurus installation](docs/node-migration.md) +- [Caller template](node/caller.yml) and [integration contract](node/contract.json) + +Supports GitHub.com. The Node implementation retains its BSD 3-Clause license. diff --git a/docs/audit-schedules.md b/docs/audit-schedules.md index 9f976ad..58bf8b2 100644 --- a/docs/audit-schedules.md +++ b/docs/audit-schedules.md @@ -1,21 +1,21 @@ -# Security and upgrade schedules +# Schedules and manual audits -Integration contract **version 6** supports separate automatic cadences and a -manual choice of audit work. The [recommended shared defaults](../defaults/node-maintenance.json) -check security weekly and propose routine upgrades quarterly. A security-only -run proposes dependency changes only -when before/after audit evidence confirms a security fix. Blocked findings and -reports remain available in the Actions run summary and artifacts. +Each installation keeps its policy in `.github/node-maintenance.json` and its +GitHub triggers in `.github/workflows/dependency-audit.yml`. **Update both when +you change cadence.** Shared defaults do not rewrite workflow files. + +The [basic setup](node-maintenance.md#set-it-up) checks security weekly and leaves +routine upgrades manual. The [recommended shared defaults](../defaults/node-maintenance.json) +check security weekly and propose routine upgrades every three months. Both use +integration version 6 and maintain the same dependency PR. ## Weekly security, quarterly upgrades -Set these fields in the installation's `.github/node-maintenance.json`, keeping its -directory, toolchain, shared defaults source, and other settings: +For local policy, set these fields in `.github/node-maintenance.json`. Keep your +existing directory, validation script, and other project settings: ```json { - "directory": "project", - "toolchain": {}, "maintenance": { "mode": "security-and-maintenance", "upgradeScope": "major" @@ -27,26 +27,15 @@ directory, toolchain, shared defaults source, and other settings: } ``` -This checks security every Monday at 02:00 UTC. Routine upgrades run at 02:00 UTC -on the first day of January, April, July, and October. An upgrade run also checks -security. The ten-day release delay and selected upgrade scope still apply. - -For weekly security and **manual-only upgrades**, use -`"maintenance": {"mode": "security-only"}` and `"upgrades": []`. To run both -kinds of audit manually only, use `"schedule": {"security": [], "upgrades": []}`. -An omitted list in the schedule object is empty. Routine scheduled upgrades need -both an upgrades cadence and `maintenance.mode=security-and-maintenance`. +If you inherit the recommended shared defaults, these fields are already supplied; +leave them out locally unless you want to override them. -## Update the caller too +Security checks run every Monday at 02:00 UTC. Upgrade runs check security and +routine upgrades at 02:00 UTC on the first day of January, April, July, and +October. Both kinds obey the release cooldown and project constraints. Routine +upgrades also obey `maintenance.upgradeScope`. -Choose a reviewed revision whose `node/contract.json` declares version 6 or -later. Start from that revision's [caller template](../node/caller.yml). -Replace both repository and revision markers with the shared repository name and -the same full commit SHA. Earlier Docusaurus workflow paths do not implement the Node interface; -follow the [migration checklist](node-migration.md). - -Replace the template's schedule marker with the unique cron entries from both -lists. For the weekly/quarterly example: +In the [setup guide's caller](node-maintenance.md#3-add-the-caller-workflow), use: ```yaml 'on': @@ -64,78 +53,102 @@ lists. For the weekly/quarterly example: - upgrades ``` -Keep the template's audit input forwarding: +Keep the remaining caller jobs, permissions, SHA pins, and audit input forwarding: ```yaml with: audit-kind: ${{ inputs.audit-kind || 'auto' }} ``` -GitHub reads cron entries from the caller workflow. Keep its entries identical -to the resolved JSON lists, including any inherited schedule; preflight rejects -a mismatch before dependency changes. If -both lists contain the same cron expression, include it once in the workflow; -that event runs one combined audit. Different expressions that happen to fire -at the same time can enqueue separate runs. They still update the same PR. -When both lists are empty, omit `on.schedule` from the workflow. +## Change or disable a cadence -GitHub uses the exact triggering cron expression to select the work. Scheduling -can be delayed by GitHub; this policy does not infer cadence from the current -clock or require an external scheduler. See [GitHub's schedule and manual input -reference](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax). +Each list accepts one or more five-field cron expressions. Replace a list to +change that cadence. For example, `"upgrades": ["0 2 1 * *"]` selects monthly +upgrades. Add the same expression to the caller's `on.schedule`. -## Run upgrades on demand +To keep weekly security and make upgrades **manual-only**, merge these fields +into the local configuration: -Open **Actions → Node dependency audit → Run workflow** on the default -branch. Choose **security** for security fixes only, or **upgrades** for security -plus routine upgrades. The choice applies to this run and does not change your -automatic policy. It still obeys `maintenance.upgradeScope`, cooldown, project -constraints, frozen install, and the selected project checks. +```json +{ + "maintenance": {"mode": "security-only"}, + "schedule": { + "security": ["0 2 * * 1"], + "upgrades": [] + } +} +``` + +Remove the quarterly cron entry from the caller. Setting the mode alone is not +enough: a nonempty upgrades list requires `security-and-maintenance` mode. + +For **manual-only audits of both kinds**, set: + +```json +{"schedule": {"security": [], "upgrades": []}} +``` -From the CLI: +Remove `on.schedule` from the caller and keep `workflow_dispatch`. + +When shared defaults are involved, local lists replace inherited lists per kind: + +| Local setting | With an inherited cadence | Without an inherited cadence | +| --- | --- | --- | +| List omitted | Keeps the inherited list | Uses an empty list | +| `[]` | Disables that cadence | Disables that cadence | +| Nonempty list | Replaces that cadence | Defines that cadence | + +An empty schedule object inherits both lists when available. Use explicit empty +lists to disable inherited cadences. These rules apply to the schedule object; +the older cron-list form is described below. + +## Keep GitHub triggers aligned + +The caller's `on.schedule` must contain the unique cron entries from **both +resolved lists**, including inherited values. Preflight checks this even for +manual dispatch. A mismatch fails before dependency changes and updates the +single configuration issue. Fix the caller or local overrides, then rerun. + +If both lists contain the same expression, include it once in the caller; that +event runs a combined security and upgrade audit. Different expressions that +happen to fire at the same time can enqueue separate runs. They still maintain +the same PR. + +GitHub selects work using the exact triggering cron expression. Runs can be +delayed; selection does not depend on the time the runner starts. See +[GitHub's scheduling reference](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#onschedule). + +## Run an audit on demand + +Open **Actions → Node dependency audit → Run workflow** on your repository's +default branch. Choose **security** for security fixes only, or **upgrades** for +security plus routine upgrades. The choice applies only to this run and does not +change automatic policy. The cooldown, upgrade scope, and validation still apply. + +From a checkout of the repository, you can also use: ```sh +gh workflow run dependency-audit.yml --ref main -f audit-kind=security gh workflow run dependency-audit.yml --ref main -f audit-kind=upgrades ``` -Use your repository's default branch if it is not `main`. - -## One maintained PR - -All audit kinds use the same `maintenance/dependency-audit` branch and open PR. -An outdated proposal updates that PR. If the files, report, base branch, and -validation result are unchanged, a rerun creates no commit, push, or PR edit. -Run timestamps and workflow links alone do not count as changes. New report -findings update the same PR body without another dependency commit. -If validation now fails for the same files, the workflow -updates the report and marks the existing PR as a draft without another push. -A run with no qualifying changes creates no PR and leaves an -existing proposal intact; read that run's summary for current findings. After a -PR is merged or closed, a later qualifying proposal can open the next PR. - -The report leads with proposed dependency changes and installation and project validation. -Routine-only headlines show the update count and validation status; security proposals -also show confirmed fix counts. Existing blocked, unchanged findings appear as -neutral background, with severities and constraints in expandable details. -Newly reported findings remain visible. Passing project checks alone does not establish -that a security finding was fixed. See [reports and PRs](../node/maintenance.md#reports-and-prs). - -## Shared defaults and existing installations - -`maintenance.mode`, `maintenance.upgradeScope` and schedule lists can come from -[shared defaults](shared-defaults.md); local fields override them. Each local -`schedule.security` or `schedule.upgrades` list replaces that inherited list; -an omitted list inherits, and `[]` disables that cadence. An empty object -inherits both lists. The recommended file selects weekly security and quarterly -upgrades. GitHub still requires matching cron entries in each consumer workflow. -A live schedule change can therefore require updates to those local triggers; -until then, preflight reports the mismatch in the reusable configuration issue. - -A cron-list shorthand (`"schedule": ["..."]`) is also accepted. It uses one -configured maintenance mode rather than selecting separate kinds per cron. -Without an explicit mode it uses `security-and-maintenance`. Use the schedule -object shown above for separate cadences and the quieter security-only default. -Omitted schedules do not add GitHub triggers; define the schedule explicitly. - -Public installation and execution use only the checked-in caller, actions, -and runtime. No private tooling is required. +Run the command for the kind you want. Replace `main` if your default branch has +another name. Other dispatch branches are rejected. + +All kinds maintain the same `maintenance/dependency-audit` branch and open PR. +A run without qualifying dependency changes leaves an existing proposal untouched; +read the current run summary for its findings. After that PR is merged or closed, +a later qualifying proposal can open the next PR. See +[reports and PR behavior](../node/maintenance.md#reports-and-prs). + +## Existing cron-list configurations + +The runtime also accepts `"schedule": ["0 2 * * 1"]`. This form runs the same +configured maintenance mode on every cron event; it does not select a kind per +expression. Without an explicit or inherited mode, it uses +`security-and-maintenance`. A local cron list replaces an inherited schedule +object entirely. + +Omitting `schedule` also retains that mode default and an internal January 30 +cron fallback. It does not create a GitHub trigger. For new installations, use +the explicit schedule object above so cadence and manual-only policy are clear. diff --git a/docs/node-maintenance.md b/docs/node-maintenance.md index 1e20165..bf7e16f 100644 --- a/docs/node-maintenance.md +++ b/docs/node-maintenance.md @@ -1,34 +1,41 @@ -# Node dependency maintenance +# Set up Node dependency maintenance -Maintain dependencies for a standalone Node application, library, or website. -Each audit attempts compatible security fixes, optionally proposes routine -upgrades, and runs a frozen installation and your selected package script. -Reruns update one maintenance PR instead of opening a PR for each dependency. +Use these workflows for a standalone Node application, library, or website. +They attempt compatible security fixes, optionally propose routine upgrades, +and validate the result before opening or updating one maintenance PR. + +Already using the earlier Docusaurus workflows? Follow the +[migration checklist](node-migration.md) instead. ## Supported projects - One project directory, at the repository root (`"directory": "."`) or in a - subdirectory. It owns one `package.json` and one lockfile. -- Node 22 (at least 22.13.0) or Node 24, declared in `.nvmrc`, `.node-version`, - or explicitly in the audit configuration. -- **npm 11.9.0**, pinned as `"packageManager": "npm@11.9.0"`, with a version-3 - `package-lock.json`. + subdirectory, with one `package.json` and one lockfile. +- Node 22 (at least 22.13.0) or Node 24. +- **npm 11.9.0**, pinned in `packageManager`, with a version-3 `package-lock.json`. - **pnpm 11.20.0 or 11.21.0**, pinned in `packageManager`, with lockfile version 9.0 and a standalone `pnpm-workspace.yaml` containing `packages: ["."]`. -- An existing, nonempty package script for validation. The default name is - `build`. It can run tests, lint, a build, or several checks. +- A package script that runs the checks needed to validate dependency changes. + It can run tests, lint, a build, or several checks. -Yarn, multi-package workspaces, and multiple independent projects per installation -are outside this version's scope. The workflow does not migrate package managers -or change deployment. Both package-manager branches use the same audit engine. -The toolchain versions listed above are the supported test matrix, not a promise -that every npm or pnpm version works. +Yarn, `npm-shrinkwrap.json`, multi-package workspaces (including a selected +package inside an ancestor workspace), and multiple projects per installation +are not supported. Use an existing supported package manager; these workflows +do not migrate it or change deployment. GitHub.com is supported; GitHub +Enterprise Server is outside the current scope. ## Set it up +The steps below install **weekly security checks with manual-only routine +upgrades**. For weekly security and quarterly upgrades across several projects, +use the [shared-defaults setup](shared-defaults.md) when completing step 2. +Both choices use the same workflows and ten-day release cooldown. + ### 1. Verify the project locally The examples use `app` as the project directory. Use `.` for a root project. +Run only the instructions for your existing package manager. + Declare Node in `app/.nvmrc`, for example: ```text @@ -36,20 +43,35 @@ Declare Node in `app/.nvmrc`, for example: ``` Keep an existing supported declaration instead of adding a conflicting one. -The nearest declaration up to the repository root wins. When `.nvmrc` and -`.node-version` are present in that directory, they must agree. +The nearest `.nvmrc` or `.node-version` up to the repository root wins. If both +exist in that directory, they must agree. Activate that Node version with your +version manager before continuing. -For **npm**, merge these fields into the existing `app/package.json`: +Choose a validation script in `app/package.json`. Both package-manager examples +below use `check`. For example, a project using Node's test runner could use: ```json { - "packageManager": "npm@11.9.0", "scripts": {"check": "node --test"} } ``` -The script is an example. Use the checks your project actually needs. Activate -Node with your version manager, then verify from the project directory: +Merge this field into the existing manifest; do not replace other scripts. +Use the checks your project actually needs. To use an existing `build` or `test` +script instead, substitute that name in the local commands and in +`validationScript` in step 2. Put required setup and checks in that script. +A Docusaurus website can keep its existing `build` script; Docusaurus is not +required for other projects. + +#### npm + +Keep or add this exact supported pin in `app/package.json`: + +```json +{"packageManager": "npm@11.9.0"} +``` + +From the project directory, verify the pinned tools and validation script: ```sh cd app @@ -60,20 +82,57 @@ npm ci npm run check ``` -For **pnpm**, retain your supported exact `packageManager` pin, for example -`pnpm@11.21.0`. Merge the [standalone workspace policy](../node/runtime/templates/pnpm-workspace.yaml) -into `app/pnpm-workspace.yaml`. Preserve existing overrides, release-age -exceptions and dependency script approvals. Then verify: +If your manifest edits require a lockfile update, run +`npm install --package-lock-only --ignore-scripts` with the pinned npm version, +review the diff, then repeat `npm ci` and the validation script. The resulting +`package-lock.json` must use `lockfileVersion: 3`. + +#### pnpm + +Keep your existing supported exact pin, including any Corepack integrity suffix. +For a project using pnpm 11.21.0, the field is: + +```json +{"packageManager": "pnpm@11.21.0"} +``` + +Merge the [standalone workspace policy](../node/runtime/templates/pnpm-workspace.yaml) +into `app/pnpm-workspace.yaml`: + +```yaml +packages: + - "." +minimumReleaseAge: 14400 +blockExoticSubdeps: true +strictDepBuilds: true +allowBuilds: + core-js: false + core-js-pure: false + fsevents: false +``` + +`14400` minutes is ten days. Preserve stricter cooldowns, existing overrides, +exact-version release-age exceptions, and dependency-script approval decisions. +Review any new script approval required by your project. + +From the project directory, verify the pinned tools and validation script: ```sh cd app corepack enable +node --version corepack pnpm --version corepack pnpm install --frozen-lockfile corepack pnpm run check ``` -Commit the working declarations, manifest, lockfile, and pnpm policy, if used. +If your manifest or workspace edits require a lockfile update, run +`corepack pnpm install --lockfile-only --ignore-scripts`, review the diff, then +repeat the frozen install and validation script. The resulting `pnpm-lock.yaml` +must use `lockfileVersion: '9.0'`. + +Commit the working Node declaration, manifest, lockfile, and pnpm policy if used. +Return to the repository root for the remaining steps. ### 2. Add the project configuration @@ -90,25 +149,28 @@ Create `.github/node-maintenance.json` at the repository root: } ``` -This checks security every Monday at 02:00 UTC and leaves routine upgrades -manual. `toolchain: {}` follows the declared Node and package manager. Explicit -constraints, for example `{"node": "24", "npm": "11.9.0"}`, must agree with those -declarations. `toolchain.pnpm` applies to pnpm projects; `toolchain.npm` applies -to npm projects. Omitting `toolchain` also follows the project. +This basic setup follows the project's supported toolchain, checks security +every Monday at 02:00 UTC, and leaves routine upgrades manual. Change `directory` +and `validationScript` to match step 1. `validationScript` names a package script, +not a shell command; it defaults to `build` when omitted. -`validationScript` names a package script, not a shell command. Its name starts -with a letter or digit and uses only letters, digits, `:`, `_`, `.`, or `-`. -For a Docusaurus website, use `"validationScript": "build"` and retain its -existing build script. No Docusaurus dependency is required. +To inherit policy for several installations, use the configuration in the +[shared-defaults guide](shared-defaults.md#2-point-each-installation-at-the-source) +instead. Do not keep explicit local fields for policy you want to inherit. +The recommended shared file selects weekly security **and quarterly upgrades**, +so its caller needs both cron entries in step 3. ### 3. Add the caller workflow -Choose a reviewed full commit SHA from `hydra-ecosystem/github-actions` whose -[`node/contract.json`](../node/contract.json) has integration version **6**. +Choose a reviewed commit from `hydra-ecosystem/github-actions`. On GitHub, open +that commit, confirm its [`node/contract.json`](../node/contract.json) declares +integration version **6**, and copy its full 40-character SHA. Use the contract +and caller from that same revision. + Create `.github/workflows/dependency-audit.yml` using the following caller. -Replace **both** `REVIEWED_FULL_COMMIT_SHA` placeholders with that same SHA. -The guide intentionally uses a placeholder; installed workflows pin an immutable -reviewed revision. +Replace **both** `REVIEWED_FULL_COMMIT_SHA` placeholders with your chosen SHA. +The placeholders avoid prescribing a revision that becomes stale; installed +workflows must pin an immutable reviewed revision. ```yaml name: Node dependency audit @@ -155,9 +217,15 @@ jobs: app-private-key: ${{ secrets.NODE_AUDIT_APP_PRIVATE_KEY }} ``` -The dependency job has read access. A separate configuration reporter can update -one reusable issue. Only the publisher can write the maintenance branch and PR. -Leave the optional App variable and secret unset to use `GITHUB_TOKEN`. +This caller matches the basic configuration in step 2. If you chose the +recommended shared defaults, add `- cron: '0 2 1 */3 *'` under `on.schedule`. +For other policies, use the unique cron entries from the resolved security and +upgrades lists. Omit `on.schedule` when both lists are empty. See +[schedules and manual runs](audit-schedules.md) for complete examples. + +The dependency job is read-only. A separate reporter can update one configuration +issue; the publisher can write the maintenance branch and PR. Leave the optional +App variable and secret unset to use `GITHUB_TOKEN`. ### 4. Enable publication and run the first audit @@ -166,110 +234,97 @@ GitHub Actions to create and approve pull requests**, or configure the optional GitHub App below. Default token permissions can stay read-only. Enable repository issues for configuration diagnostics. Organization restrictions still apply. -Merge the setup into the consumer's default branch. Open **Actions → Node +Merge the setup into your repository's default branch. Open **Actions → Node dependency audit → Run workflow**, select the default branch, and choose -**security** or **upgrades**. Other dispatch branches are rejected. - -- A passing proposal opens or updates one maintenance PR. -- Failed installation or project checks fail the workflow and retain a draft - proposal when there are dependency changes to publish. -- No dependency changes leave the existing PR untouched. Read the current report - in the run summary or artifacts. -- Configuration failures stop before dependency changes and update one reusable - configuration issue. A successful preflight closes it. - -The PR title describes its actual changes, for example **Node: 3 routine -dependency updates, no security fixes**. The report leads with versions and -validation. Unchanged blocked findings appear as neutral background; new findings -remain visible. Passing project checks does not prove all vulnerabilities are fixed. -Nothing merges automatically. +**security**. Manual dispatch from other branches is rejected. + +Check the preflight logs or `node-audit-configuration` artifact for resolved +tool requirements and defaults sources. Read the run summary for frozen +installation, validation, and the audit result: + +| Result | What happens | +| --- | --- | +| Validated dependency changes | Opens or updates the single maintenance PR. | +| Dependency changes with failed installation or checks | Retains a draft proposal when possible and fails the workflow. | +| No dependency changes | Creates no PR and leaves any existing proposal untouched; read the current run summary or report artifact. | +| Invalid configuration | Stops before dependency changes and updates one reusable configuration issue. Successful preflight closes it. | + +Choose **upgrades** for a separate manual run when you want routine updates. +It also checks security and still obeys the cooldown and upgrade scope. It does +not enable automatic upgrades. Nothing merges automatically. See +[reports and PR behavior](../node/maintenance.md#reports-and-prs) for details. ## Toolchain selection -Project declarations remain authoritative. Explicit settings constrain them and -fail on disagreement; the workflow does not rewrite them. Exact npm/pnpm versions -are required. A pnpm Corepack integrity suffix is retained. Node may be a major -or exact stable release. Symbolic aliases such as `lts/*` are rejected. Installed -tools, `engines.node`, lockfile format and standalone scope are checked before -changes. See the [tested versions](../node/runtime/toolchain.json). +The basic setup follows project declarations. Explicit or inherited constraints +must agree with them; the workflow fails on disagreement and does not rewrite +files. Use `toolchain.node`, `toolchain.npm`, or `toolchain.pnpm` to constrain the +relevant tool, or `"project"` to follow its declaration. An empty object does not +erase inherited constraints. The unused package-manager constraint is ignored. + +Node may be a major or exact stable release; aliases such as `lts/*` are rejected. +An explicit `toolchain.node` can also supply Node when no declaration file exists. +Exact npm/pnpm versions are required in `packageManager`. A pnpm Corepack integrity +suffix is retained; npm integrity suffixes are unsupported. Installed tools, +`engines.node`, lockfile format, and standalone scope are checked before changes. +See the [tested versions](../node/runtime/toolchain.json). ## Validation and maintenance options | Setting | Behavior | | --- | --- | -| `validationScript` | Default `"build"`. Runs that script after a frozen install. Use `"test"`, `"check"`, or another existing script. Put prerequisites in the script. | -| `maintenance.mode` | With a schedule object, defaults to `"security-only"`. `"security-and-maintenance"` permits automatic routine upgrades on the upgrades cadence. | -| `maintenance.upgradeScope` | Default `"major"`. Routine direct upgrades may be limited to `"patch"` or `"minor"`. Transitive changes can accompany an allowed direct upgrade. | -| `minimumReleaseAgeDays` | Default ten. Controls the release cooldown; see package-manager details below. | - -Security-only runs retain changes only when a security fix is confirmed and -unrelated direct changes are excluded. Manual **upgrades** requests routine -work for one run without changing automatic policy. Routine scope is checked -against resolved versions; violations restore the accepted security proposal. -Security compatibility is independent of the routine scope. No forced upgrades -or automatic override removal are used. - -For **pnpm**, preflight requires workspace `minimumReleaseAge` to be at least -`minimumReleaseAgeDays * 1440`. Stricter project policy is preserved. The engine -can retain exact security-related release-age exceptions and reports them. - -For **npm**, the audit supplies a publication cutoff to security and routine -resolution using `--before`. Routine direct candidates also use release-time -metadata. There is no npm release-age exception mechanism in this version; -recent fixes can remain blocked until the cutoff permits them. Existing locked -versions are not downgraded just because they are younger than the cutoff. - -## Schedules and shared defaults - -[Shared defaults](shared-defaults.md) let many installations follow one public -GitHub JSON file. Local fields override inherited policy. Directory, validation -script, deployment and credentials stay local. - -The [recommended defaults](../defaults/node-maintenance.json) select Node 24, -pnpm 11.21.0 or npm 11.9.0, ten days, weekly security, and quarterly upgrades. -If you inherit them, mirror both schedules in your caller. JSON does not change -GitHub's workflow triggers. See [scheduling](audit-schedules.md) for cadence -examples and manual runs. +| `validationScript` | Existing nonempty package script; default `"build"`. Names start with a letter or digit and contain only letters, digits, `:`, `_`, `.`, or `-`. | +| `minimumReleaseAgeDays` | Minimum release cooldown in days; nonnegative integer, default `10`. pnpm workspace policy must be at least this strict. | +| `maintenance.mode` | `"security-only"` or `"security-and-maintenance"`. A schedule object defaults to security-only unless a mode is supplied locally or inherited. | +| `maintenance.upgradeScope` | Maximum routine direct upgrade level: `"patch"`, `"minor"` including patches, or `"major"` including both (default). Security fixes are handled separately. | +| `schedule` | Separate `security` and `upgrades` cron lists. Must match the caller's cron triggers. See the [scheduling guide](audit-schedules.md). | +| `defaults` | Public GitHub JSON source with `repository`, `path`, and `ref`. See [shared defaults](shared-defaults.md). | + +The [maintenance policy](../node/maintenance.md) explains security-fix evidence, +package-manager cooldown behavior, proposal rollback, and permissions. ## Optional: publish with a GitHub App An App can publish without enabling the repository-wide built-in-token PR setting. Create an App with repository **Contents: read/write** and **Pull requests: -read/write**, then install it on each repository you maintain. Set: +read/write**, then install it on each repository you maintain. Set these in each +repository's Actions configuration: + +- Variable `NODE_AUDIT_APP_CLIENT_ID`: the App's Client ID. +- Secret `NODE_AUDIT_APP_PRIVATE_KEY`: the App's PEM private key. -- Actions variable `NODE_AUDIT_APP_CLIENT_ID` to its Client ID. -- Actions secret `NODE_AUDIT_APP_PRIVATE_KEY` to its PEM private key. +The caller already forwards these values to the publisher. The dependency job +never receives the App key or publisher token. A configured App that cannot +authenticate fails publication without falling back to `GITHUB_TOKEN`. -The caller supplies these values only to the publisher, which mints and revokes -a repository-scoped token. The dependency job never receives the App key or -publisher token. Authentication failure stops publication without falling back -to `GITHUB_TOKEN`. Generated CI follows GitHub's token and repository policies. -Neither mode waits for consumer CI or merges the PR. +With the built-in token, workflows triggered by opening or updating the dependency +PR require approval by a user with write access. An App token avoids that prompt, +subject to the repository's other policies. The maintenance run already performs +the frozen install and selected project checks; neither authentication mode waits +for additional PR CI. See [GitHub's token behavior](https://docs.github.com/en/actions/concepts/security/github_token). ## Update or migrate an installation -This is a breaking replacement of the Docusaurus interface. Follow the -[migration checklist](node-migration.md) for an existing installation. There are -no aliases for the old configuration, script setting, action paths or workflows. +For an earlier Docusaurus installation, use the [migration checklist](node-migration.md). +There are no aliases for its old configuration, script option, or workflow paths. -For later updates, compare the proposed contract and caller template, update -both shared SHA references together, then run a manual audit from the default -branch. Toolchain changes still require separate local verification. +For later updates, compare the installed and proposed contract and caller template. +Update both workflow SHA references together, then run a manual security audit +from the default branch. Shared-defaults refs are independent of workflow pins; +review their changes separately. Verify toolchain changes locally first. ## Troubleshooting | Symptom | Next step | | --- | --- | -| Toolchain conflict | Align `.nvmrc`/`.node-version`, `packageManager`, `engines.node`, shared defaults and local constraints. | +| Toolchain conflict | Align Node declarations, `packageManager`, `engines.node`, and resolved toolchain constraints. | | Missing validation script | Set `validationScript` to an existing script and run it locally. | -| Workspace or lockfile rejected | Select a standalone project and verify one supported lockfile. | -| pnpm scripts need approval | Review and preserve the project's `allowBuilds` decisions. | +| Workspace or lockfile rejected | Select a standalone project with one supported lockfile; remove unsupported shrinkwrap or mixed lockfiles. | +| pnpm scripts need approval | Review the project's `allowBuilds` decisions. | | Cooldown rejected | Align pnpm workspace policy with the resolved minimum, or explicitly override that minimum. | -| Caller schedule mismatch | Mirror the resolved security and upgrades lists in the caller workflow. | +| Caller schedule mismatch | Mirror the resolved security and upgrades lists in the caller; manual dispatch also checks this. | | PR publication denied | Check Actions permissions, App installation, and organization policy. | | Default branch changed during audit | Rerun from the current default branch. Stale proposals are refused. | -See the [maintenance policy](../node/maintenance.md), [caller template](../node/caller.yml), -and [integration contract](../node/contract.json). The public repository contains -all required source, tests and CI; private AWD tools are optional. GitHub.com is -supported; GitHub Enterprise Server is outside the current scope. +All required source, tests, and workflows are checked into this public repository. +No external workflow generator is required. diff --git a/docs/node-migration.md b/docs/node-migration.md index 2365fe2..0eb4e34 100644 --- a/docs/node-migration.md +++ b/docs/node-migration.md @@ -1,63 +1,63 @@ # Migrate from Docusaurus maintenance -Integration version 6 replaces the Docusaurus interface with standalone Node -maintenance. This checklist applies to Hydra and OmegaConf and to any other -installation pinned to the earlier workflows. Updating this shared repository -does not update their pinned callers. +Integration version 6 replaces the Docusaurus-specific interface with standalone +Node maintenance. Use this checklist for an installation pinned to the earlier +workflows. New installations should use the [setup guide](node-maintenance.md). +There is no compatibility layer for the old paths or `buildScript` option. ## Rollout order -A workflow SHA pin does not freeze a defaults source whose `ref` is `main`. -This replacement removes `defaults/docusaurus.json`. An old installation that -reads that path from `main` will fail preflight as soon as the removal reaches -`main`, even if its workflow pin has not changed. +The new shared revision removes `defaults/docusaurus.json`. A workflow SHA pin +does not freeze a separate defaults source whose ref is `main`. An old caller +that follows the removed path can therefore fail before its workflow is updated. -Before merging the shared replacement, choose one of these rollout orders: +For an installation you cannot migrate immediately, pin its old defaults source +to an immutable commit that still contains the old file. Then migrate the caller +and configuration together using the checklist below. If the source path is +already missing, that pin also restores access while you prepare the migration. +Existing installations without shared defaults do not have this source-path issue. -- Pin the old defaults source to an immutable commit that contains the old file. - Then merge the shared replacement and migrate each consumer using the checklist. -- Publish and review the new commit, then migrate consumers to that exact commit - and the new defaults path/ref before moving the shared default branch. - -Preserve each consumer's existing policy while changing its source reference. -Do not leave the old path following `main` through the removal. Existing -installations without a shared defaults source are not affected by that removal. -These are operator steps; this repository does not migrate consumers automatically. +When adopting the new defaults path, choose a ref containing the new file and +review its values. Preserve local policy unless you intend to change it. These +are manual steps; the shared repository does not rewrite consumer installations. ## Consumer checklist 1. Choose a reviewed version-6 commit. Read `node/contract.json` and use `node/caller.yml` from that same revision. 2. Rename `.github/docusaurus.json` to `.github/node-maintenance.json`. -3. Rename `buildScript` to `validationScript`. Keep `"build"` for the existing - Docusaurus production build. Preserve the directory and maintenance policy. +3. Rename `buildScript` to `validationScript` if present. Keep `"build"` for an + existing Docusaurus production build. Preserve the directory and maintenance + policy. 4. Change shared defaults paths, if used, from `defaults/docusaurus.json` to - `defaults/node-maintenance.json`. Use a ref containing the new file. Check - inherited values and explicit overrides before removing any local field. -5. Change **both** caller workflow references to `node-audit.yml` and - `node-publish.yml` at the same reviewed SHA. Keep the caller filename - `.github/workflows/dependency-audit.yml` and mirror its resolved cron entries. -6. If an App is configured, supply its variable and secret as - `NODE_AUDIT_APP_CLIENT_ID` and `NODE_AUDIT_APP_PRIVATE_KEY`. Store them under - those names before changing the caller. Unset values use `GITHUB_TOKEN`. -7. Verify the Node declaration, exact supported `packageManager`, standalone - pnpm policy and lockfile locally. Toolchain selection now always follows - declarations unless explicit constraints are supplied; omitted `toolchain` - no longer selects the old fixed toolchain. Preserve deployment checks. -8. Remove the old caller if it had another filename. Run only the new caller; - leaving both enabled schedules duplicate work. -9. Inspect and close any old bot-owned **Docusaurus configuration** issue after - resolving it. The new reporter uses a new marker and will not adopt that issue. -10. Merge the consumer update, dispatch **security** from the default branch, - and inspect the resolved tools, configuration status, validation, and report. - Dispatch **upgrades** separately when routine work is wanted. - -The branch remains `maintenance/dependency-audit`, so a new proposal updates the -existing open maintenance PR instead of creating another one. Its title changes -to `Node: …`. A run without dependency changes retains the existing PR and report. -A successful run leaves an existing draft for human review. - -Hydra and OmegaConf can keep pnpm, their Docusaurus build scripts, Node 24 and -pnpm 11.21.0. Their integration revisions differ: inspect each current caller -and policy instead of copying one installation's configuration over the other. -No package-manager migration is required for this interface change. + `defaults/node-maintenance.json`. Use a ref containing the new file. Review + inherited values and local overrides; following the recommended defaults + enables quarterly routine upgrades as well as weekly security checks. +5. Replace both caller workflow references with `node-audit.yml` and + `node-publish.yml` at the same reviewed SHA. Use + `.github/workflows/dependency-audit.yml` as the caller filename. Match its cron + entries to the resolved [schedule](audit-schedules.md), and include the current + caller's permissions and manual `audit-kind` input forwarding. +6. If an App is configured, store and forward its variable and secret as + `NODE_AUDIT_APP_CLIENT_ID` and `NODE_AUDIT_APP_PRIVATE_KEY` before changing the + caller. Unset values use `GITHUB_TOKEN`; its PR-creation setting must be enabled. +7. [Verify the project locally](node-maintenance.md#1-verify-the-project-locally): + Node declaration, exact supported `packageManager`, standalone scope, lockfile, + pnpm policy if used, and validation script. Omitted `toolchain` now follows + project declarations rather than selecting the old fixed toolchain. Explicit + or inherited constraints must agree with those declarations. +8. Update references to the renamed configuration in project documentation, + workflow path filters, and existing checks. Remove an old caller with another + filename so it cannot schedule duplicate work. Keep deployment behavior intact. +9. Merge the update and dispatch **security** from the default branch. Inspect + resolved tools, configuration status, frozen installation, project validation, + and the report. Dispatch **upgrades** separately when routine work is wanted. +10. After resolving configuration failures, inspect and close any old bot-owned + **Docusaurus configuration** issue. The new reporter uses a new marker and + does not adopt that issue. + +The maintenance branch remains `maintenance/dependency-audit`, so a qualifying +proposal updates an existing open maintenance PR. Its title changes to `Node: …`. +A run with no dependency changes retains the existing PR and report; a successful +rerun leaves an existing draft for human review. No package-manager migration is +required for this interface change. diff --git a/docs/shared-defaults.md b/docs/shared-defaults.md index 3dbe928..44d4fc8 100644 --- a/docs/shared-defaults.md +++ b/docs/shared-defaults.md @@ -1,23 +1,18 @@ # Shared defaults for multiple Node installations -Define shared defaults once, reuse them across many Node installations, -and override settings in individual repositories when needed. A source is a -regular JSON file in any **public GitHub.com repository**. It needs no service, -App, or registration with this repository. - -Shared toolchain and maintenance defaults require **integration version 6**; -inherited schedules require **version 6**. The examples below include schedules. -Inspect [`node/contract.json`](../node/contract.json) at your chosen reviewed -workflow revision and confirm `integration_version` is `6`. Use the -[caller template](../node/caller.yml) from that same revision and pin both -reusable workflow calls to its full commit SHA. The -[general setup guide](node-maintenance.md#set-it-up) supplies the version-6 caller; -use the source configuration below to opt in to shared defaults. +Use one JSON file as the authoritative policy for several repositories. Each +installation inherits toolchain constraints, release cooldown, maintenance +options, and schedules, while keeping its own project settings and credentials. + +A source can be a regular file in **any public GitHub.com repository**. It needs +no service, App, or registration here. The consuming repository may be private. +Use integration version **6** and the [setup guide](node-maintenance.md#set-it-up) +for local project verification, the caller workflow, and publication permissions. ## 1. Choose an authoritative defaults file -For example, publish this as `defaults/node-maintenance.json` in your own public -configuration repository: +You can follow the [recommended defaults in this repository](../defaults/node-maintenance.json) +or publish an equivalent file in your own public configuration repository: ```json { @@ -38,15 +33,15 @@ configuration repository: } ``` -You can also use the [recommended defaults in this repository](../defaults/node-maintenance.json). -They live outside the `node/` action implementation tree. Other maintainers -can keep equivalent files in their own repositories and review changes through -their normal pull-request process. Supported Node/pnpm combinations still apply; -a shared file does not add support for an untested toolchain. +This policy checks security weekly and proposes routine upgrades quarterly. +It requires Node 24 and the listed version of the project's existing package +manager; it does not choose or migrate the package manager. A shared file cannot +add support for toolchain versions outside the [supported matrix](node-maintenance.md#supported-projects). ## 2. Point each installation at the source -In each project repository, add the source to `.github/node-maintenance.json`: +Use this as the configuration in step 2 of the setup guide, replacing `website` +and `build` with your project directory and validation script: ```json { @@ -60,48 +55,40 @@ In each project repository, add the source to `.github/node-maintenance.json`: } ``` -Change `repository` and `path` to use your own file. All three source fields are -required. `ref` can be a branch, tag, or full commit SHA. Sources are read without -credentials, so private source repositories are not supported; the project -repository itself may be private. - -The recommended file selects weekly security checks and quarterly upgrades, with -`security-and-maintenance` mode. Integration version 6 inherits its schedules and -supports [manual audit choices](audit-schedules.md). Updating a live source changes -the policy for consumers that inherit those fields; pin the defaults ref if each -policy update needs a separate review. +All three source fields are required. Change `repository` and `path` to use your +own file. `ref` can be a branch, tag, or full commit SHA. Sources are fetched +without credentials; private defaults repositories are not supported. -Each installation keeps its own directory, validation script, dependency -declarations, security exceptions, deployment, and credentials. These values are -not inherited. Keep the resolved schedule mirrored in the caller workflow. Remove -explicit local toolchain or maintenance values when you want to inherit them. -A local `maintenance.upgradeScope` keeps overriding the source after updates. +**Replace the basic setup's explicit policy fields rather than appending this +source to them.** Local fields keep overriding the source. In particular, the +basic `security-only` mode conflicts with inherited nonempty upgrade schedules. +Keep explicit local values only when you intend to override shared policy. -## 3. Allow the configuration issue reporter +## 3. Match the caller and run an audit -The version-6 caller grants these permissions on its **`jobs.audit`**: +Continue with [step 3 of the setup guide](node-maintenance.md#3-add-the-caller-workflow). +For the recommended defaults, the caller must contain both cron entries: ```yaml -permissions: - contents: read - issues: write +schedule: +- cron: '0 2 * * 1' +- cron: '0 2 1 */3 *' ``` -Use the complete [caller template](../node/caller.yml) from your selected -version-6 revision for these examples. Keep both workflow references pinned to -that same full SHA. -The workflow's internal audit job has only `contents: read`; a separate reporting -job has only `issues: write`. The reporting job reads a bounded JSON status -artifact and does not check out or execute project code. It receives no App key. +Place this under `on` in the caller. Keep both reusable workflow calls pinned +to the same reviewed version-6 SHA. Enable repository issues and publication as +shown in [step 4](node-maintenance.md#4-enable-publication-and-run-the-first-audit), +then run a manual security audit. Preflight checks the resolved tools, cooldown, +and schedules before dependency changes. -Enable repository issues. No App is required for reporting. The existing -[PR-creation setup](node-maintenance.md#4-enable-publication-and-run-the-first-audit) still applies -to publishing dependency proposals. +A defaults edit does not rewrite local workflow triggers. When shared schedules +change, update affected callers too. A mismatch fails preflight, including manual +runs, and updates the single configuration issue. See the +[scheduling guide](audit-schedules.md) for disabling or changing a cadence. ## Local overrides -Local settings win independently for each toolchain, maintenance and schedule -field: +Local fields override inherited values independently. For example: ```json { @@ -111,102 +98,62 @@ field: "path": "defaults/node-maintenance.json", "ref": "main" }, - "toolchain": { - "node": "22" - }, + "toolchain": {"node": "22"}, "minimumReleaseAgeDays": 14, - "maintenance": { - "upgradeScope": "patch" - } + "maintenance": {"upgradeScope": "patch"} } ``` -This site requires Node 22 and a fourteen-day minimum cooldown. It inherits pnpm -and maintenance mode, and restricts routine upgrades to patches. Its project -declarations must agree with those resolved settings. +This project requires Node 22 and a fourteen-day minimum cooldown. It inherits +the package-manager constraint, maintenance mode, and both schedules, while +limiting routine direct upgrades to patches. Its local declarations and pnpm +policy, if used, must satisfy those settings. -An empty `toolchain` object does not erase inherited constraints. Set either -`toolchain.node` or `toolchain.pnpm` to `"project"` to explicitly follow that -project's declaration instead of the inherited version requirement. +| Setting | Override behavior | +| --- | --- | +| `toolchain.node`, `toolchain.npm`, `toolchain.pnpm` | Each local value replaces that inherited constraint. Use `"project"` to follow the consuming project's declaration. An empty object erases nothing. | +| `minimumReleaseAgeDays` | A local integer replaces the inherited minimum; stricter pnpm workspace policy remains valid. | +| `maintenance.mode`, `maintenance.upgradeScope` | Each local value replaces that inherited field. | +| `schedule.security`, `schedule.upgrades` | Each local list replaces that cadence. Omitted lists inherit; `[]` disables a cadence. An empty object inherits both lists. | -Schedule lists are replaced per kind. For example, `"schedule": {"upgrades": []}` -disables automatic routine upgrades while retaining the inherited security -cadence. `"schedule": {"security": [], "upgrades": []}` disables both automatic -cadences. An empty schedule object inherits both lists. A legacy local cron list -replaces the whole inherited schedule and retains legacy selection rules. - -After a source or local schedule change, update the caller workflow's cron -entries to match the resolved lists. GitHub reads triggers from that local file. -Until it matches, preflight fails and the existing configuration issue reports -the required schedule; no workflow file is changed automatically. - -`minimumReleaseAgeDays` is a nonnegative integer and defaults to `10` when no -source or local setting supplies it. The project's `pnpm-workspace.yaml` must -have an integer `minimumReleaseAge` of at least `days * 1440` minutes. A stricter -workspace value is accepted. Exact-version security exceptions remain intact; -the workflow does not lower the global cooldown or rewrite declarations. +Directory, validation script, dependency declarations, security exceptions, +deployment, and credentials remain local. They are not inherited. Shared +constraints are checked against declarations; they never rewrite them. ## Use another project as the authoritative source -Point `repository` at that project's repository and `path` at its -`.github/node-maintenance.json`. Its shared settings become live defaults for dependent -projects. Source directory and validation script fields are ignored. Version 5 also -inherits its schedule, with local overrides taking precedence. +Point `repository` at that project's public repository and `path` at its +`.github/node-maintenance.json`. Only its shared fields are inherited; its +directory and validation script are ignored. -The source can itself inherit defaults. Its inherited values are applied first, -then its explicit shared values, then the dependent project's explicit values. -This is live inheritance, not a one-time copy. Sources must declare shared -values explicitly or inherit them; the resolver does not infer versions from +The source can itself inherit defaults. Its inherited values apply first, then +its explicit shared values, then the consuming project's explicit values. +This is live inheritance, not a one-time copy. Versions are not inferred from another project's `.nvmrc` or `package.json`. `"project"` always refers to the consuming project. Cycles, chains longer than eight sources, unknown settings, invalid JSON, unreadable sources, and oversized files fail validation. Source files are limited -to 64 KiB and are treated only as JSON data. Each source reference field is -limited to 2048 characters. +to 64 KiB; each source reference field is limited to 2048 characters. Sources are +treated only as JSON data. ## Manage shared updates -- **Follow a branch** for fleet-wide changes on each installation's next audit. -- **Use a full commit SHA** for fixed defaults and adopt updates through local PRs. -- A tag can move; use a full SHA when you need an immutable source. - -The source revision is independent of the workflow revision. Each audit resolves -branch/tag references to full commits and freezes the effective configuration -for that run. Failure reports record the source path, requested reference, and -resolved commit. Changing a source during a run does not change that run's -requirements. Future runs resolve the source again. - -Before changing fleet defaults, verify one site's declarations and build against -the proposed values. Align affected sites' declarations when adopting a change. -A shared change can cause sites that have drifted to fail their next preflight; -it does not migrate them automatically. - -## Failures and the single configuration issue - -Preflight runs before any project dependency command. Conflicting Node or pnpm -declarations, an insufficient cooldown, or a bad source produce a GitHub Actions -error annotation, a failed audit, and a configuration issue with the diagnostic, -source information, and workflow-run link. Failed configuration checks do not -publish a dependency proposal. - -There is **one bot-owned `Node audit configuration` issue per repository**: - -1. The first configuration failure opens it. -2. Repeated failures update its body without adding per-run comments. -3. Successful configuration and runtime validation closes it, even if a later - dependency install or build fails for a different reason. -4. A later configuration failure reopens the same issue, including when the - defaults source has changed. - -Fix the named declaration, or explicitly override the inherited setting, then -rerun the audit. The workflow never edits files to resolve a mismatch. Interrupted -preflight does not close an existing issue. Ordinary build or dependency failures -retain the existing audit-report/draft-PR behavior. - -Reporting is serialized per repository. Ambiguous duplicate marked issues, -disabled repository issues, or insufficient issue permission produce a reporting -error rather than creating another issue. Inspect that error before retrying. - -Automatic migration or repair PRs are not part of this feature. Repeated manual -repair effort can inform whether an opt-in migration feature is worthwhile. +- Follow a **branch** to apply shared changes on each installation's next audit. +- Use a **full commit SHA** when each policy update needs a local adoption PR. +- A tag can move; use a full SHA for an immutable source. + +The defaults ref is independent of the workflow pin. A pinned workflow that +follows defaults at `main` still receives future defaults changes. Each audit +resolves references to full commits and freezes its configuration for that run; +source changes apply to later runs. Configuration reports retain source identities. + +Before changing shared policy, verify a representative project's declarations +and validation script against the proposed values. Align affected declarations, +pnpm cooldowns, and caller triggers as needed. A shared change can cause the next +preflight to fail; no automatic migration or repair PR is generated. + +Failures reuse one **Node audit configuration** issue per repository. Fix the +named declaration or override, then rerun. Successful preflight closes the issue; +recurrence reopens it. See [configuration issue behavior](../node/maintenance.md#configuration-issues) +for reporting permissions and failure cases. diff --git a/node/maintenance.md b/node/maintenance.md index 647e819..fb6826a 100644 --- a/node/maintenance.md +++ b/node/maintenance.md @@ -1,141 +1,124 @@ -# Node dependency maintenance +# Node maintenance behavior -Use the [setup guide](../docs/node-maintenance.md) to install the shared workflows. -The project owns its dependency versions, build prerequisites, deployment, and -pnpm policy. `.github/node-maintenance.json` selects the project directory, toolchain, -validation script, maintenance options, and schedule. +Use the [setup guide](../docs/node-maintenance.md) to install the workflows. +This reference explains how dependency proposals, validation, reports, and +publication work. The project owns its dependency declarations, validation +script, build prerequisites, deployment, and package-manager policy. ## Toolchain and validation -Node and the package manager follow the project declarations by default. -Supported combinations use Node 22 (at least 22.13.0) or 24 with npm 11.9.0, -or pnpm 11.20.0/11.21.0. Explicit constraints must agree with declarations. -See [toolchain selection](../docs/node-maintenance.md#toolchain-selection). - -For pnpm, the recommended workspace policy uses a ten-day release delay, restricted dependency sources, -and explicit dependency-script approvals. Existing application constraints, -overrides, and exact security exceptions remain project configuration. - -`validationScript` defaults to `build` and must name an existing, nonempty project -package script. Put prerequisites and checks in that script. Invalid configuration -or a missing script stops the run before dependency changes. - -Integration version 6 supports [live shared defaults](../docs/shared-defaults.md) -from a JSON file in any public GitHub repository, including another project's -configuration. Toolchain, minimum cooldown and maintenance fields are -inherited. Explicit local fields override inherited fields. Each run resolves -references once and retains their full commit identities; source changes apply -to subsequent runs. It also inherits schedule lists, with per-kind local -overrides. Directory, validation script, deployment and credentials -remain local. - -`minimumReleaseAgeDays` defaults to ten. For pnpm, preflight requires the standalone -workspace's `minimumReleaseAge` to be an integer at least that many days in -minutes. Stricter workspace settings and existing security exceptions remain -valid. Conflicting toolchain declarations or insufficient cooldown fail loudly; -the workflow does not rewrite them or automatically migrate a project. +Preflight checks the selected standalone project, declared tools, supported +lockfile, validation script, and release cooldown before dependency changes. +Explicit or inherited toolchain constraints must agree with project declarations. +A mismatch fails the run; it does not rewrite files or migrate the project. +See [supported projects](../docs/node-maintenance.md#supported-projects) and +[toolchain selection](../docs/node-maintenance.md#toolchain-selection). + +`validationScript` defaults to `build` and must name an existing, nonempty package +script. Each proposal is validated with a frozen install and that script. Put +required setup, tests, lint, or build commands in the script. Passing it proves +only that those selected checks passed, not that every vulnerability was fixed. ## Maintenance behavior -Cron-list/omitted schedules default to `security-and-maintenance`. This attempts -compatible security fixes at all severities before routine stable upgrades, then performs a frozen -installation and the selected project checks. `security-only` skips routine -upgrade selection and resolution. +Security audits attempt compatible fixes at all severities. Security-only runs +retain changes only when before/after audit evidence confirms a security fix; +unrelated changes are discarded. The engine does not force incompatible upgrades +or remove project overrides automatically. +Upgrade audits attempt security fixes first, then routine stable upgrades. `maintenance.upgradeScope` limits routine **direct** upgrades to `patch`, `minor` -(including patches), or `major` (the default, including all levels). Restricted -scopes check the actual resolved proposal against the accepted security snapshot. -A violation restores that snapshot. Transitive changes can accompany an allowed -direct upgrade. Security-fix compatibility stays independent of the routine limit. -See [validation and maintenance options](../docs/node-maintenance.md#validation-and-maintenance-options). - -The interface supports [separate security and upgrade schedules](../docs/audit-schedules.md). -The recommended shared defaults check security weekly and propose routine -upgrades quarterly. Without shared maintenance settings, a schedule object -defaults to `security-only`; automatic upgrades require an explicit opt-in. -Security-only runs discard dependency changes that -fix no confirmed findings. A manual `audit-kind=upgrades` run can request routine -upgrades without enabling them automatically. Cron lists keep their mode. - -The cron-list shorthand defaults to January 30 at 02:00 UTC. Keep it in the project -configuration and mirror it in the caller workflow's cron trigger. The -**Node dependency audit** workflow also supports manual dispatch from the -consumer's default branch. +(including patches), or `major` (the default, including both). Restricted scopes +check the actual resolved proposal against the accepted security snapshot. A +violation restores that snapshot. Transitive changes can accompany an allowed +direct upgrade. Security-fix compatibility is independent of the routine limit. + +The [scheduling guide](../docs/audit-schedules.md) explains automatic opt-in and +manual audit choices. A manual upgrades run requests routine work once without +changing automatic policy. See [shared defaults](../docs/shared-defaults.md) to +apply policy across several repositories. + +## Release cooldown + +`minimumReleaseAgeDays` is a nonnegative integer and defaults to ten. + +For **pnpm**, the standalone workspace's `minimumReleaseAge` must be an integer +at least that many days in minutes (`days * 1440`). Stricter workspace policy is +preserved. The engine can retain exact security-related release-age exceptions +and reports them. Existing overrides and script approvals remain project policy. + +For **npm**, the engine supplies a publication cutoff using `--before` during +security and routine resolution. Routine direct candidates also use release-time +metadata. There is no npm release-age exception mechanism in this version; +recent fixes can remain blocked until the cutoff permits them. Existing locked +versions are not downgraded just because they are younger than the cutoff. ## Reports and PRs -The workflow opens or updates one PR whose title describes its dependency updates -and confirmed security fixes. For example, **Node: 9 routine dependency -updates, no security fixes** identifies a proposal with no confirmed security fix. -If audit evidence is unavailable, the title says **security fixes unconfirmed**. +All audit kinds use the same `maintenance/dependency-audit` branch and open PR. Only the project manifest, lockfile, and required pnpm security exceptions enter that PR. Reports remain in the PR body, run summary, and artifacts; they are not -committed. - -All audit kinds share that PR and its stable branch. The publisher updates an -outdated proposal and does not push another commit for an identical proposal. -An outdated title is corrected without another dependency commit or report rewrite. -Publisher jobs are serialized for the repository. The headline gives the proposed -dependency update count and validation status. Routine-only proposals omit unrelated -security counts from the headline; security proposals include confirmed fix counts. -The body leads with the proposed dependency versions and validation results. -Existing findings that all have known blockers and unchanged audit evidence appear -as neutral background below validation. Their severities and constraints are -expandable. Findings that need review or verification retain those distinctions. -Run settings and full audit evidence are also expandable. Existing blocked fixes -have no red status dots; newly reported findings remain visible with red markers. +committed. After a PR is merged or closed, a later qualifying proposal can open +the next PR. Nothing merges automatically. + +The title describes dependency updates and confirmed security fixes. For example, +**Node: 9 routine dependency updates, no security fixes** identifies routine work. +If audit evidence is unavailable, the title says **security fixes unconfirmed**. +The report leads with proposed versions and validation results. Routine-only +headlines omit unrelated security counts. Existing blocked findings with unchanged +audit evidence appear as neutral background, with severity and constraints in +expandable details. New findings and evidence that needs review remain visible. Reports retain unresolved findings, pin decisions, validation failures, changed locked version sets, and candidate dependency-file SHA-256 hashes. Grouped logs -retain successful and failed command output, including warnings. Passing project checks -does not prove every vulnerability is resolved. - -Failed installation/validation candidates are published as drafts when a candidate can be -retained, and the workflow fails after retaining the report. A successful rerun -leaves an existing draft for human review. A run with no dependency changes leaves -an existing PR branch and its report unchanged; the new report remains in the run -summary and artifacts. - -The workflow does not change Dependabot settings or dismiss alerts. If a project -chooses update suppression or alert dismissal, scope it to the selected project's -lockfile and preserve other ecosystems. Verify those repository controls separately. +retain successful and failed command output, including warnings. + +| Rerun result | Publisher behavior | +| --- | --- | +| Changed dependency proposal | Updates the existing PR and branch. | +| Same files, report, base branch, validation, and title | No commit, push, or PR edit. Run timestamps and workflow links alone do not count as changes. | +| Same proposal with new report findings | Updates the body without another dependency commit. | +| Same proposal with an outdated title | Corrects the title without rewriting the report or pushing a commit. | +| Retained proposal with failed installation or checks | Publishes or updates a draft and fails the workflow. | +| Successful rerun of an existing draft | Leaves it as a draft for human review. | +| No qualifying dependency changes | Creates no PR and leaves an existing branch and report unchanged. Read the new run summary or artifacts. | + +Publisher jobs are serialized per repository. Publication is refused if the +default branch's repository inputs changed since the audit; rerun from the current +default branch in that case. + +The workflow does not change Dependabot settings or dismiss alerts. If you choose +to suppress other update PRs or dismiss alerts, scope those repository controls +to the selected project's lockfile and verify them separately. + +## Configuration issues + +Preflight failures update one marked, bot-owned **Node audit configuration** issue +per repository. Repeated failures update its body without per-run comments. +Successful configuration and runtime checks close it, even if a later dependency +command or validation script fails. A later configuration failure reopens the +same issue. Interrupted preflight does not close it. + +Enable repository issues and grant the caller's audit job `issues: write`. +Ambiguous duplicate marked issues, disabled issues, or insufficient permission +produce a reporting error instead of another issue. Dependency or validation +failures do not create configuration issues. No automatic repair PR is generated. ## Publication and credentials -The caller grants `issues: write` to the shared audit workflow for a -separate, serialized configuration-reporting job. The dependency-execution job -is explicitly read-only. The reporting job consumes only the current run's JSON -status artifact and never checks out project code or receives the App key. -It opens one marked bot-owned configuration issue, updates that issue on repeated -failures, closes it after successful preflight, and reopens the same closed issue -on recurrence. Ordinary dependency/validation failures do not create configuration -issues. Automatic migration remains a possible follow-up, not current behavior. - -The audit has read access. The separate publisher can push the maintenance branch -and open its PR. It refuses publication if the default branch's repository inputs -changed since the audit; rerun from the current default branch in that case. - -By default, publication uses `GITHUB_TOKEN`. PR workflows triggered by opening or -updating these PRs require approval by a user with write access; see -[GitHub's token behavior](https://docs.github.com/en/actions/concepts/security/github_token). -Normal commit messages keep consumer CI/deployment eligible to run under their -configured triggers and token rules. Nothing is merged automatically. - -To avoid the built-in token's PR-workflow approval prompt, configure the -[optional GitHub App](../docs/node-maintenance.md#optional-publish-with-a-github-app). -It needs repository Contents and Pull requests read/write permissions and an -installation on each consumer repository. The publisher mints a short-lived token -limited to that repository and revokes it at job completion. The audit and project checks -never receive the key or publisher token. A configured App that cannot authenticate -fails publication without falling back to `GITHUB_TOKEN`. Neither authentication -mode waits for all consumer CI or merges the PR. - -The consumer calls the shared audit and publisher at the same full commit SHA. -Runtime scripts and supporting files stay in the shared repository. Before an -update, compare the installed and proposed public contracts and caller templates, -then change both SHA pins together and verify a manual audit. - -For npm, the engine enforces the resolved cooldown during security and routine -resolution using `--before`, and checks publication times when selecting routine -direct upgrades. npm has no release-age exception mechanism here; recent fixes -can remain blocked by the cooldown. pnpm retains its exact security exceptions. +The dependency-execution job has only `contents: read` and checkout credentials +are not persisted. A separate serialized reporter has only `issues: write`, +reads the current run's configuration artifact, and never checks out project code. +The publisher has Contents and Pull requests write access. Only it can push the +maintenance branch and open the PR. + +Publication uses `GITHUB_TOKEN` unless an optional App is configured. The App key +and publisher token never reach the dependency job or configuration reporter. +An App token is limited to the consumer repository and revoked at job completion. +A configured App that cannot authenticate stops publication without falling back +to the built-in token. See [credential setup and PR CI behavior](../docs/node-maintenance.md#optional-publish-with-a-github-app). + +The workflow does not add CI-skip markers. Consumer CI follows its configured +triggers and token rules. Neither authentication mode waits for additional PR CI +or merges the PR. Consumers call both shared workflows at the same full commit +SHA; update both pins together using the [update instructions](../docs/node-maintenance.md#update-or-migrate-an-installation). diff --git a/node/tests/test_shared_defaults.py b/node/tests/test_shared_defaults.py index 9093417..c115b98 100644 --- a/node/tests/test_shared_defaults.py +++ b/node/tests/test_shared_defaults.py @@ -588,16 +588,16 @@ def test_documented_examples_resolve_against_the_actual_recommended_defaults( assert selected["minimumReleaseAge"] == days * 1440 -def test_readme_default_and_override_examples_match_effective_shared_schedule( - site, monkeypatch -): +def test_scheduling_guide_examples_match_effective_shared_schedule(site, monkeypatch): root, _ = site public_root = RUNTIME.parents[1] defaults = json.loads((public_root / "defaults/node-maintenance.json").read_text()) examples = [ json.loads(block) for block in re.findall( - r"```json\n(.*?)\n```", (public_root / "README.md").read_text(), re.S + r"```json\n(.*?)\n```", + (public_root / "docs/audit-schedules.md").read_text(), + re.S, ) ] assert (