From 0abedd10710e327d31fc6ce9a647922b876e72cf Mon Sep 17 00:00:00 2001 From: OB Date: Mon, 8 Jun 2026 20:48:26 +0700 Subject: [PATCH 1/6] test(ci): self-test rust/base and the security workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a Rust fixture crate and a self-actions job that runs rust/base on it end-to-end (fmt, clippy, test). Wire the security-gate.yml and security.yml reusable workflows into self-security via local ./ refs — testable now that v0 carries the security composites. Ignore Rust target/. --- .github/workflows/self-actions.yml | 14 ++++++++++++++ .github/workflows/self-security.yml | 9 +++++++-- .gitignore | 1 + CHANGELOG.md | 9 +++++++++ CLAUDE.md | 2 +- package.json | 2 +- test/fixtures/rust-crate/Cargo.lock | 7 +++++++ test/fixtures/rust-crate/Cargo.toml | 5 +++++ test/fixtures/rust-crate/README.md | 3 +++ test/fixtures/rust-crate/rust-toolchain.toml | 2 ++ test/fixtures/rust-crate/src/lib.rs | 16 ++++++++++++++++ 11 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/rust-crate/Cargo.lock create mode 100644 test/fixtures/rust-crate/Cargo.toml create mode 100644 test/fixtures/rust-crate/README.md create mode 100644 test/fixtures/rust-crate/rust-toolchain.toml create mode 100644 test/fixtures/rust-crate/src/lib.rs diff --git a/.github/workflows/self-actions.yml b/.github/workflows/self-actions.yml index e6c3e47..3b6a89a 100644 --- a/.github/workflows/self-actions.yml +++ b/.github/workflows/self-actions.yml @@ -220,3 +220,17 @@ jobs: [ -f "${m}" ] || { echo "::error::javascript/base did not run ${m%-ran}"; exit 1; } done echo "::notice::javascript/base ran install (sfw + frozen lockfile), lint, build, test on the fixture" + + rust-base: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + path: _src + - name: Stage the rust fixture at the workspace root + shell: bash + run: cp -a _src/test/fixtures/rust-crate/. . + - uses: ./_src/.github/actions/rust/base + - name: Confirm rust/base completed the gates + shell: bash + run: echo "::notice::rust/base ran fmt --check, clippy -D warnings, and test on the fixture" diff --git a/.github/workflows/self-security.yml b/.github/workflows/self-security.yml index c0ca488..6aaf057 100644 --- a/.github/workflows/self-security.yml +++ b/.github/workflows/self-security.yml @@ -1,8 +1,7 @@ # Self-CI Security name: Self-CI Security -# Local `./` refs so a PR self-tests its own composite changes — the @v0-pinned -# security.yml can't (a reusable workflow's `./` resolves to the caller's checkout). +# Local `./` refs so a PR self-tests its own security composites and workflows. on: push: branches: [main] @@ -25,3 +24,9 @@ jobs: steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - uses: ./.github/actions/security/osv-scanner + + security-gate: + uses: ./.github/workflows/security-gate.yml + + security: + uses: ./.github/workflows/security.yml diff --git a/.gitignore b/.gitignore index f5a0faa..f64738e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .claude/output/ CLAUDE.local.md node_modules/ +target/ *.log *.bak .actionlint-cache/ diff --git a/CHANGELOG.md b/CHANGELOG.md index e663054..3d660a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v0.2.1 - 08/06/2026 + +### Tests +- `self-actions` — self-test `rust/base` end-to-end on a Rust fixture crate (`test/fixtures/rust-crate`): stage it at the workspace root, run the composite, assert `fmt` / `clippy` / `test` pass. +- `self-security` — self-test the `security-gate.yml` and `security.yml` reusable workflows via local `./` refs, now that `v0` carries the security composites. + +### Configuration +- `.gitignore` — add `target/` for Rust build artifacts. + ## v0.2.0 - 08/06/2026 ### Features diff --git a/CLAUDE.md b/CLAUDE.md index 1897664..a0002cf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,7 @@ Reusable GitHub Actions workflows + composite actions for the Coroboros stack. - `.github/workflows/rust-packages.yml` — bundled Cargo pipeline (`preflight` matrix / `security-gate` / `package` / `publish` / `security`) + opt-in cargo-dist binary layer (`dist-plan` / `dist-build` / `dist-host` / `dist-publish`, gated on `[package.metadata.dist]` or `[workspace.metadata.dist]`). - `.github/workflows/security-gate.yml` — blocking gate `publish` `needs:`. `supply-chain` (auto-routed: `Cargo.toml` → `security/rust/cargo-deny` advisories+bans+sources, else `security/osv-scanner`) + `secret-scan` (gitleaks). A separate reusable workflow so the caller's `publish` can `needs:` the whole gate as one job, running each scan once. Imposed via the package workflows, importable standalone by a non-package repo. - `.github/workflows/security.yml` — advisory layer, never blocks: `dependency-review` (PR-only) + `licenses` (Rust, `continue-on-error`, `security/rust/cargo-deny` `checks: licenses`). License/quality policy lives here, off the gate. -- `.github/workflows/{self,self-security,self-release,self-actions}.yml` — self-CI: lint, gitleaks + osv (composites via local `./`), the `v0` rolling-tag move, and `self-actions` smoke-testing the composites against the real checkout on every PR. +- `.github/workflows/{self,self-security,self-release,self-actions}.yml` — self-CI: lint, the security composites + `security-gate`/`security` workflows via local `./`, the `v0` rolling-tag move, and `self-actions` smoke-testing every composite (plus `javascript/base`/`rust/base` on `test/fixtures/`) every PR. Workflow self-tests resolve their `@v0` composites against the released `v0`, so a brand-new composite is testable only once a release moves `v0` onto it. - `.github/actions/{check-docs,javascript/base,rust/{base,native-deps,test-deps,install-dist,pin-version},security/{gitleaks,osv-scanner,rust/cargo-deny},release/{verify-tag,generate-changelog,github-release,commit-artifacts}}/action.yml` — composites. - `.github/dependabot.yml` — auto-PRs for pinned action SHAs. `renovate.json` + `.github/workflows/renovate.yml` — self-hosted Renovate (needs the `RENOVATE_TOKEN` PAT secret, scope `repo` + `workflow`) auto-bumps the version-pinned tooling; `.github/renovate/sync-tool-sha.sh` re-syncs each paired tarball SHA-256 in the same PR. - `security/.gitleaks.toml` — canonical gitleaks ruleset. diff --git a/package.json b/package.json index 6e82b0c..f26e262 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@coroboros/ci", - "version": "0.2.0", + "version": "0.2.1", "private": true, "description": "Reusable GitHub Actions CI for the Coroboros stack.", "license": "SEE LICENSE IN LICENSE.md", diff --git a/test/fixtures/rust-crate/Cargo.lock b/test/fixtures/rust-crate/Cargo.lock new file mode 100644 index 0000000..251a6b1 --- /dev/null +++ b/test/fixtures/rust-crate/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ci-fixture-rust" +version = "0.0.0" diff --git a/test/fixtures/rust-crate/Cargo.toml b/test/fixtures/rust-crate/Cargo.toml new file mode 100644 index 0000000..2415667 --- /dev/null +++ b/test/fixtures/rust-crate/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "ci-fixture-rust" +version = "0.0.0" +edition = "2021" +publish = false diff --git a/test/fixtures/rust-crate/README.md b/test/fixtures/rust-crate/README.md new file mode 100644 index 0000000..275d333 --- /dev/null +++ b/test/fixtures/rust-crate/README.md @@ -0,0 +1,3 @@ +# ci-fixture-rust + +Minimal crate exercising `rust/base` in self-CI. Not published. diff --git a/test/fixtures/rust-crate/rust-toolchain.toml b/test/fixtures/rust-crate/rust-toolchain.toml new file mode 100644 index 0000000..292fe49 --- /dev/null +++ b/test/fixtures/rust-crate/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "stable" diff --git a/test/fixtures/rust-crate/src/lib.rs b/test/fixtures/rust-crate/src/lib.rs new file mode 100644 index 0000000..0dd63a9 --- /dev/null +++ b/test/fixtures/rust-crate/src/lib.rs @@ -0,0 +1,16 @@ +//! Minimal crate exercising `rust/base` (fmt, clippy, test) in self-CI. Not published. + +/// Adds two numbers. +pub fn add(a: i32, b: i32) -> i32 { + a + b +} + +#[cfg(test)] +mod tests { + use super::add; + + #[test] + fn adds() { + assert_eq!(add(2, 2), 4); + } +} From 71e1fdca3d95cb38f64c78946a4b000f265f4b7e Mon Sep 17 00:00:00 2001 From: OB Date: Mon, 8 Jun 2026 20:53:58 +0700 Subject: [PATCH 2/6] docs(ci): add a Self-CI section to the README --- CHANGELOG.md | 3 +++ README.md | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d660a6..537bf5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - `self-actions` — self-test `rust/base` end-to-end on a Rust fixture crate (`test/fixtures/rust-crate`): stage it at the workspace root, run the composite, assert `fmt` / `clippy` / `test` pass. - `self-security` — self-test the `security-gate.yml` and `security.yml` reusable workflows via local `./` refs, now that `v0` carries the security composites. +### Documentation +- `README` — add a Self-CI section documenting how the repo tests its own workflows and composites. + ### Configuration - `.gitignore` — add `target/` for Rust build artifacts. diff --git a/README.md b/README.md index a13690c..d041e91 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Drop into any `@coroboros/*` repo via `uses: coroboros/ci/.github/workflows/ Date: Mon, 8 Jun 2026 20:59:44 +0700 Subject: [PATCH 3/6] refactor(ci): rename self.yml to self-lint, self-actions to self-test --- .github/renovate/sync-tool-sha.sh | 2 +- .github/workflows/{self.yml => self-lint.yml} | 4 ++-- .github/workflows/{self-actions.yml => self-test.yml} | 4 ++-- CHANGELOG.md | 5 ++++- CLAUDE.md | 2 +- README.md | 6 +++--- renovate.json | 2 +- 7 files changed, 14 insertions(+), 11 deletions(-) rename .github/workflows/{self.yml => self-lint.yml} (98%) rename .github/workflows/{self-actions.yml => self-test.yml} (99%) diff --git a/.github/renovate/sync-tool-sha.sh b/.github/renovate/sync-tool-sha.sh index 3581285..78057bd 100644 --- a/.github/renovate/sync-tool-sha.sh +++ b/.github/renovate/sync-tool-sha.sh @@ -6,7 +6,7 @@ set -euo pipefail GITLEAKS_YML=".github/actions/security/gitleaks/action.yml" -SELF_YML=".github/workflows/self.yml" +SELF_YML=".github/workflows/self-lint.yml" DIST_YML=".github/actions/rust/install-dist/action.yml" sha256_of() { diff --git a/.github/workflows/self.yml b/.github/workflows/self-lint.yml similarity index 98% rename from .github/workflows/self.yml rename to .github/workflows/self-lint.yml index 5cde8f8..cca41ad 100644 --- a/.github/workflows/self.yml +++ b/.github/workflows/self-lint.yml @@ -1,5 +1,5 @@ -# Self-CI -name: Self-CI +# Self-CI Lint +name: Self-CI Lint on: push: diff --git a/.github/workflows/self-actions.yml b/.github/workflows/self-test.yml similarity index 99% rename from .github/workflows/self-actions.yml rename to .github/workflows/self-test.yml index 3b6a89a..cfe07eb 100644 --- a/.github/workflows/self-actions.yml +++ b/.github/workflows/self-test.yml @@ -1,5 +1,5 @@ -# Self-CI Actions -name: Self-CI Actions +# Self-CI Test +name: Self-CI Test # A PR self-tests its composites via local `./` refs against the real checkout # (GITHUB_SHA == HEAD); `commit-artifacts` pushes to a local bare remote, with diff --git a/CHANGELOG.md b/CHANGELOG.md index 537bf5b..330a3e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,11 @@ ## v0.2.1 - 08/06/2026 +### Refactor +- `self.yml` → `self-lint`, `self-actions` → `self-test` — align the self-CI workflow names (`self-lint` · `self-test` · `self-security` · `self-release`). + ### Tests -- `self-actions` — self-test `rust/base` end-to-end on a Rust fixture crate (`test/fixtures/rust-crate`): stage it at the workspace root, run the composite, assert `fmt` / `clippy` / `test` pass. +- `self-test` — self-test `rust/base` end-to-end on a Rust fixture crate (`test/fixtures/rust-crate`): stage it at the workspace root, run the composite, assert `fmt` / `clippy` / `test` pass. - `self-security` — self-test the `security-gate.yml` and `security.yml` reusable workflows via local `./` refs, now that `v0` carries the security composites. ### Documentation diff --git a/CLAUDE.md b/CLAUDE.md index a0002cf..d4b7a2c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,7 @@ Reusable GitHub Actions workflows + composite actions for the Coroboros stack. - `.github/workflows/rust-packages.yml` — bundled Cargo pipeline (`preflight` matrix / `security-gate` / `package` / `publish` / `security`) + opt-in cargo-dist binary layer (`dist-plan` / `dist-build` / `dist-host` / `dist-publish`, gated on `[package.metadata.dist]` or `[workspace.metadata.dist]`). - `.github/workflows/security-gate.yml` — blocking gate `publish` `needs:`. `supply-chain` (auto-routed: `Cargo.toml` → `security/rust/cargo-deny` advisories+bans+sources, else `security/osv-scanner`) + `secret-scan` (gitleaks). A separate reusable workflow so the caller's `publish` can `needs:` the whole gate as one job, running each scan once. Imposed via the package workflows, importable standalone by a non-package repo. - `.github/workflows/security.yml` — advisory layer, never blocks: `dependency-review` (PR-only) + `licenses` (Rust, `continue-on-error`, `security/rust/cargo-deny` `checks: licenses`). License/quality policy lives here, off the gate. -- `.github/workflows/{self,self-security,self-release,self-actions}.yml` — self-CI: lint, the security composites + `security-gate`/`security` workflows via local `./`, the `v0` rolling-tag move, and `self-actions` smoke-testing every composite (plus `javascript/base`/`rust/base` on `test/fixtures/`) every PR. Workflow self-tests resolve their `@v0` composites against the released `v0`, so a brand-new composite is testable only once a release moves `v0` onto it. +- `.github/workflows/{self-lint,self-test,self-security,self-release}.yml` — self-CI: lint, the security composites + `security-gate`/`security` workflows via local `./`, the `v0` rolling-tag move, and `self-test` smoke-testing every composite (plus `javascript/base`/`rust/base` on `test/fixtures/`) every PR. Workflow self-tests resolve their `@v0` composites against the released `v0`, so a brand-new composite is testable only once a release moves `v0` onto it. - `.github/actions/{check-docs,javascript/base,rust/{base,native-deps,test-deps,install-dist,pin-version},security/{gitleaks,osv-scanner,rust/cargo-deny},release/{verify-tag,generate-changelog,github-release,commit-artifacts}}/action.yml` — composites. - `.github/dependabot.yml` — auto-PRs for pinned action SHAs. `renovate.json` + `.github/workflows/renovate.yml` — self-hosted Renovate (needs the `RENOVATE_TOKEN` PAT secret, scope `repo` + `workflow`) auto-bumps the version-pinned tooling; `.github/renovate/sync-tool-sha.sh` re-syncs each paired tarball SHA-256 in the same PR. - `security/.gitleaks.toml` — canonical gitleaks ruleset. diff --git a/README.md b/README.md index d041e91..189adf9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Drop into any `@coroboros/*` repo via `uses: coroboros/ci/.github/workflows/.yml@v0`, or compose around the composite actions under `.github/actions/`. [![latest](https://img.shields.io/github/v/release/coroboros/ci?style=flat-square&label=latest&color=000000)](https://github.com/coroboros/ci/releases) -[![ci](https://img.shields.io/github/actions/workflow/status/coroboros/ci/self.yml?branch=main&style=flat-square&label=ci&color=000000)](https://github.com/coroboros/ci/actions/workflows/self.yml) +[![ci](https://img.shields.io/github/actions/workflow/status/coroboros/ci/self-lint.yml?branch=main&style=flat-square&label=ci&color=000000)](https://github.com/coroboros/ci/actions/workflows/self-lint.yml) [![branch](https://img.shields.io/badge/branch-main-000000?style=flat-square)](https://github.com/coroboros/ci) [![license](https://img.shields.io/badge/license-All%20Rights%20Reserved-000000?style=flat-square)](LICENSE.md) [![stars](https://img.shields.io/github/stars/coroboros/ci?style=flat-square&label=stars&color=000000)](https://github.com/coroboros/ci) @@ -305,8 +305,8 @@ Section format: `## vX.Y.Z - DD/MM/YYYY`. Idempotent. Reuses an existing hand-cu The workflows and composites are the product, so every PR runs them against this repo: -- **Lint** (`self.yml`) — `actionlint` + shellcheck, `yamllint`. -- **Composites** (`self-actions.yml`) — smoke every composite (`release/*`, `rust/*`, `security/*`) against the real checkout, and run `javascript/base` + `rust/base` end-to-end on a `test/fixtures/` package and crate. +- **Lint** (`self-lint.yml`) — `actionlint` + shellcheck, `yamllint`. +- **Composites** (`self-test.yml`) — smoke every composite (`release/*`, `rust/*`, `security/*`) against the real checkout, and run `javascript/base` + `rust/base` end-to-end on a `test/fixtures/` package and crate. - **Security** (`self-security.yml`) — the `gitleaks` / `osv-scanner` composites and the `security-gate` / `security` workflows, via local `./` refs. - **Release** (`self-release.yml`) — moves the rolling `v0` tag onto each stable release. diff --git a/renovate.json b/renovate.json index 6b2cf7d..527a111 100644 --- a/renovate.json +++ b/renovate.json @@ -65,7 +65,7 @@ "commands": ["bash .github/renovate/sync-tool-sha.sh"], "fileFilters": [ ".github/actions/security/gitleaks/action.yml", - ".github/workflows/self.yml", + ".github/workflows/self-lint.yml", ".github/actions/rust/install-dist/action.yml" ], "executionMode": "branch" From 698e96f14edab4a7ce50de0110add4f927651af4 Mon Sep 17 00:00:00 2001 From: OB Date: Mon, 8 Jun 2026 21:47:19 +0700 Subject: [PATCH 4/6] docs(ci): aggregate the CI badge across all self workflows; reframe the Self-CI intro --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 189adf9..93be782 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Drop into any `@coroboros/*` repo via `uses: coroboros/ci/.github/workflows/.yml@v0`, or compose around the composite actions under `.github/actions/`. [![latest](https://img.shields.io/github/v/release/coroboros/ci?style=flat-square&label=latest&color=000000)](https://github.com/coroboros/ci/releases) -[![ci](https://img.shields.io/github/actions/workflow/status/coroboros/ci/self-lint.yml?branch=main&style=flat-square&label=ci&color=000000)](https://github.com/coroboros/ci/actions/workflows/self-lint.yml) +[![ci](https://img.shields.io/github/checks-status/coroboros/ci/main?style=flat-square&label=ci&color=000000)](https://github.com/coroboros/ci/actions) [![branch](https://img.shields.io/badge/branch-main-000000?style=flat-square)](https://github.com/coroboros/ci) [![license](https://img.shields.io/badge/license-All%20Rights%20Reserved-000000?style=flat-square)](LICENSE.md) [![stars](https://img.shields.io/github/stars/coroboros/ci?style=flat-square&label=stars&color=000000)](https://github.com/coroboros/ci) @@ -303,12 +303,12 @@ Section format: `## vX.Y.Z - DD/MM/YYYY`. Idempotent. Reuses an existing hand-cu ## Self-CI -The workflows and composites are the product, so every PR runs them against this repo: +`coroboros/ci` runs a CI on itself — lint, security, and the `v0` release move — plus a test layer that exercises its own composite actions, which are the product: - **Lint** (`self-lint.yml`) — `actionlint` + shellcheck, `yamllint`. -- **Composites** (`self-test.yml`) — smoke every composite (`release/*`, `rust/*`, `security/*`) against the real checkout, and run `javascript/base` + `rust/base` end-to-end on a `test/fixtures/` package and crate. - **Security** (`self-security.yml`) — the `gitleaks` / `osv-scanner` composites and the `security-gate` / `security` workflows, via local `./` refs. - **Release** (`self-release.yml`) — moves the rolling `v0` tag onto each stable release. +- **Test** (`self-test.yml`) — smoke every composite (`release/*`, `rust/*`, `security/*`) against the real checkout, and run `javascript/base` + `rust/base` end-to-end on a `test/fixtures/` package and crate. A workflow self-test resolves its composites at the released `@v0`, so a brand-new composite is testable through a workflow only once a release moves `v0` onto it. From 2ef320b72161bc5ab9861b27745b99a431280761 Mon Sep 17 00:00:00 2001 From: OB Date: Mon, 8 Jun 2026 21:55:12 +0700 Subject: [PATCH 5/6] docs(ci): order and punctuate the lint tools to match self-lint.yml --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 93be782..eeba3a5 100644 --- a/README.md +++ b/README.md @@ -305,7 +305,7 @@ Section format: `## vX.Y.Z - DD/MM/YYYY`. Idempotent. Reuses an existing hand-cu `coroboros/ci` runs a CI on itself — lint, security, and the `v0` release move — plus a test layer that exercises its own composite actions, which are the product: -- **Lint** (`self-lint.yml`) — `actionlint` + shellcheck, `yamllint`. +- **Lint** (`self-lint.yml`) — `actionlint`, `yamllint`, `shellcheck`. - **Security** (`self-security.yml`) — the `gitleaks` / `osv-scanner` composites and the `security-gate` / `security` workflows, via local `./` refs. - **Release** (`self-release.yml`) — moves the rolling `v0` tag onto each stable release. - **Test** (`self-test.yml`) — smoke every composite (`release/*`, `rust/*`, `security/*`) against the real checkout, and run `javascript/base` + `rust/base` end-to-end on a `test/fixtures/` package and crate. From 65316350b498f832bf97afe7750d0268b7638fd1 Mon Sep 17 00:00:00 2001 From: OB Date: Mon, 8 Jun 2026 22:00:05 +0700 Subject: [PATCH 6/6] docs(ci): use text instead of a checkmark glyph in the secrets table --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eeba3a5..ad502d6 100644 --- a/README.md +++ b/README.md @@ -325,9 +325,9 @@ Zero `inputs:` — configuration flows through the caller's `secrets:` block. Ev | name | required | description | | :--- | :---: | :--- | -| `NPM_CONFIG_FILE` | ✔ | `.npmrc` content. Written to repo root by `javascript/base`. `${VAR}` references inside are expanded by npm at install time. | +| `NPM_CONFIG_FILE` | yes | `.npmrc` content. Written to repo root by `javascript/base`. `${VAR}` references inside are expanded by npm at install time. | | `NPM_EXTRA_CONFIG` | | Extra `.npmrc` lines appended after `NPM_CONFIG_FILE`. A **secret** — it lands in `.npmrc`, so it can carry auth material and must stay masked. | -| `NPM_PACKAGE_REGISTRY` | ✔ | npm package registry URL. | +| `NPM_PACKAGE_REGISTRY` | yes | npm package registry URL. | | `NPM_PACKAGE_PROXY_REGISTRY` | | Optional npm proxy registry URL. | | `NPM_PACKAGE_REGISTRY_TOKEN` | | npm Granular Access Token, scoped to the publishing organization with create-new-package permission. Required only for the token bootstrap (first publish of a new scoped package, before npm Trusted Publisher is bound). Absent → OIDC. |