Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ on:
npm registry to install the build's dependencies from, for runners
that cannot reach registry.npmjs.org — an Artifactory npm remote, say.
Empty means whatever npm resolves on the runner. The lockfile needs
no change: npm rewrites its registry.npmjs.org hosts to this registry.
no change: it names no registry, only versions and integrity hashes.
type: string
required: false
default: ''
Expand Down Expand Up @@ -149,8 +149,8 @@ jobs:
mirror-token: ${{ secrets.node-mirror-token }}

# Same mechanism as the publishing actions (actions/lib/npm-registry.sh):
# project-level npm config next to the lockfile, applied to this install
# and nothing else. Inlined rather than run from the checkout so that a
# project-level npm config appended next to the lockfile, applied to this
# install and nothing else. Inlined rather than run from the checkout so that a
# caller pinning an older kb-ref still gets the registry it asked for.
- name: Point npm at a private registry
if: ${{ inputs.npm-registry != '' }}
Expand All @@ -176,7 +176,7 @@ jobs:
if [ -n "${KB_NPM_TOKEN:-}" ]; then
echo "${registry#*:}:_authToken=\${KB_NPM_TOKEN}"
fi
} > .npmrc
} >> .npmrc
echo "npm installs from ${registry}${KB_NPM_TOKEN:+ (authenticated)}"

- name: Install
Expand Down
5 changes: 5 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Registry-agnostic lockfile: no `resolved` URL per package, so npm fetches
# from whichever registry is configured at install time and the integrity
# hash still pins the tarball. Regenerating the lockfile behind a private
# mirror then changes nothing but what actually changed.
omit-lockfile-registry-resolved=true
21 changes: 11 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,17 @@ anything there, rebuild, then run it.
repository must carry `"optional": true` so a fresh clone still builds. Never
add a registry entry that requires network or a sibling checkout without it.

### Lockfiles resolve to registry.npmjs.org

Every `resolved` in `package-lock.json` and `actions/package-lock.json` must
point at `https://registry.npmjs.org/`, and `tests/private-registry.spec.js`
fails the build otherwise. Consumers on private networks install from an
internal mirror by *configuring* a registry; npm rewrites the default host to
it at fetch time, but only the default host. A lockfile regenerated behind a
corporate `~/.npmrc` bakes that registry's URLs in and installs in one network
only. If your machine has such an `.npmrc`, run
`npm install --registry=https://registry.npmjs.org/` when touching either lockfile.
### Lockfiles name no registry

`package-lock.json` and `actions/package-lock.json` carry a version and an
`integrity` hash per package but no `resolved` URL: the committed `.npmrc` next
to each sets `omit-lockfile-registry-resolved=true`. npm then fetches from
whichever registry is configured at install time — registry.npmjs.org, or an
internal mirror on a private network — and the hash still pins the tarball. A
lockfile regenerated behind a corporate `~/.npmrc` therefore stays portable, no
`--registry` flag needed. Keep both `.npmrc` files;
`tests/private-registry.spec.js` fails if either loses the setting or a
lockfile gains a registry URL.

### Two build modes, one document

Expand Down
9 changes: 5 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,11 @@ Every embedded test runs twice, as Playwright projects `chromium` (:4201) and
- `artifact-safety.spec.js` — tarball extraction guards (traversal, absolute paths, symlinks).
- `nginx-config.spec.js` — static assertions on `nginx.conf`/`nginx.headers.conf`, including
that the CSP the Express mirror serves is byte-identical to nginx's.
- `private-registry.spec.js` — both lockfiles resolve to `registry.npmjs.org` (npm rewrites
only that host to a configured mirror), the `npm-registry`/`npm-token`/`node-mirror` inputs
exist on both actions and `build-image.yml`, and `actions/lib/npm-registry.sh` writes the
project `.npmrc` without ever putting the token on disk.
- `private-registry.spec.js` — both lockfiles name no registry (no `resolved` URL, an
integrity hash on every package; the committed `.npmrc` sets
`omit-lockfile-registry-resolved`), the `npm-registry`/`npm-token`/`node-mirror` inputs
exist on both actions and `build-image.yml`, and `actions/lib/npm-registry.sh` appends to
the project `.npmrc` without ever putting the token on disk.
- `skill.spec.js` — `skills/kb-docs-add/`: frontmatter satisfies the Agent Skills spec
(name ↔ directory, portable fields only), no scripts shipped, every referenced file exists,
`examples/` are byte-identical to the contract's code blocks, and the docs carry the install
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ The build and tests are fully hermetic — they use the committed
`tests/fixtures/docs-example.dist.tar.gz` fixture (registered via `apps.json`),
so no `GITHUB_TOKEN`, network, or sibling repository is required.

If your `~/.npmrc` points at a corporate registry, install with
`--registry=https://registry.npmjs.org/` before committing a lockfile change:
both lockfiles must keep resolving to the public registry (see `AGENTS.md`).
Both lockfiles name no registry (`omit-lockfile-registry-resolved` in the
committed `.npmrc`), so a lockfile change made behind a corporate registry is
fine to commit as it is (see `AGENTS.md`).

See [`CLAUDE.md`](CLAUDE.md) for an architecture overview and the full command
list, and [`README.md`](README.md) for usage.
Expand Down
5 changes: 5 additions & 0 deletions actions/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Registry-agnostic lockfile: no `resolved` URL per package, so npm fetches
# from whichever registry is configured at install time and the integrity
# hash still pins the tarball. Regenerating the lockfile behind a private
# mirror then changes nothing but what actually changed.
omit-lockfile-registry-resolved=true
22 changes: 12 additions & 10 deletions actions/lib/npm-registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
# Consuming repositories may run on runners inside a private network where
# registry.npmjs.org is unreachable and every package has to come from an
# internal mirror (a JFrog Artifactory npm remote, typically). The lockfiles in
# this repository resolve every package to registry.npmjs.org, and that is fine:
# npm's `replace-registry-host` (default `npmjs`) rewrites that host to the
# configured registry at fetch time, and the lockfile's integrity hashes still
# verify because the mirror serves the same tarballs. All that is missing is a
# way to say which registry — this script.
# this repository name no registry at all: the committed `.npmrc` beside each
# sets `omit-lockfile-registry-resolved`, so a package carries a version and an
# integrity hash but no `resolved` URL. npm fetches it from whichever registry is
# configured when it installs, and the hash still verifies because the mirror
# serves the same tarballs. All that is missing is a way to say which registry —
# this script.
#
# Usage: npm-registry.sh [DIR]
#
Expand All @@ -17,10 +18,11 @@
# whatever the runner's own npm configuration says.
# KB_NPM_TOKEN optional bearer token for that registry.
#
# Writes DIR/.npmrc (default: the current directory), which npm reads as
# project-level config for anything installed from DIR. Project config layers
# on top of the runner's user and global config rather than replacing it, and
# it reaches no other step of the calling workflow.
# Appends to DIR/.npmrc (default: the current directory), which npm reads as
# project-level config for anything installed from DIR. Appending keeps the
# committed settings already there. Project config layers on top of the
# runner's user and global config rather than replacing it, and it reaches no
# other step of the calling workflow.
#
# The token is written as a `${KB_NPM_TOKEN}` reference, which npm expands from
# the environment when it runs, so the secret never lands on disk. The step
Expand Down Expand Up @@ -60,7 +62,7 @@ auth_key="${registry#*:}"
if [ -n "$token" ]; then
echo "${auth_key}:_authToken=\${KB_NPM_TOKEN}"
fi
} > "${dir}/.npmrc"
} >> "${dir}/.npmrc"

if [ -n "$token" ]; then
echo "npm installs from ${registry} (authenticated)"
Expand Down
Loading
Loading