Skip to content

Commit 6d5c578

Browse files
author
ben
authored
ci: auto-stage npm version bumps (#13)
1 parent ce73e92 commit 6d5c578

5 files changed

Lines changed: 26 additions & 23 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,5 @@
5050
- Keep `portfolio-inventory.json` byte-canonical and consistent with the public package identity, version, repository, direct `@hraness/*` dependency edges, and Hraness-owned dependencies pinned by exact immutable GitHub specifiers.
5151
- Pair concrete behavior tests with property tests for parsing, resolution, ordering, path confinement, and round-trip laws.
5252
- Run `bun test src/benchmark.test.ts src/evaluation.test.ts src/evaluation-kb.test.ts src/search.test.ts src/sdk.test.ts` when changing rank fusion, retrieval defaults, frozen-corpus execution, or built-in evaluation adapters. The six-case synthetic rank-fusion fixture is a deterministic regression, not a retrieval-quality or performance benchmark. Keep real-corpus manifests versioned, judgments independent of rankings, raw lane evidence intact, and performance claims tied to named hardware and measured runs. Run `bun run check` before handing off a change; it must leave committed `dist/` and `bun.lock` unchanged.
53-
- Follow `docs/publishing.md` for the interactive npm bootstrap and later stage-only trusted publishing. A `package.json` push to `main` may continue only when the exact prior and current manifests prove a strictly increasing stable package version; an unchanged version must stop successfully before verification or OIDC, and `workflow_dispatch` remains the current-`main` recovery path. Keep source checkout, install, build, test, pack, and artifact upload in a read-only job; only its minimal dependent staging job may request OIDC, and that job must use the protected `npm-stage` environment, require reviewer `0thernet` while allowing self-review, and rebind the exact three-file artifact and current `main` before the canonical-registry stage-only mutation. Bind npm's trusted publisher to that exact environment. Disallow traditional publishing tokens, inspect the staged tarball, and approve it with 2FA. Preserve `contentPolicy.class=dual-use` and the root `DISCLOSURE` in every published version.
53+
- Follow `docs/publishing.md` for the interactive npm bootstrap and later stage-only trusted publishing. A `package.json` push to `main` may continue only when the exact prior and current manifests prove a strictly increasing stable package version; an unchanged version must stop successfully before verification or OIDC, and `workflow_dispatch` remains the current-`main` recovery path. Keep source checkout, install, build, test, pack, and artifact upload in a read-only job; only its minimal dependent staging job may request OIDC, and that job must use the exact `npm-stage` environment, restrict deployments to `main` without required deployment reviewers, and rebind the exact three-file artifact and current `main` before the canonical-registry stage-only mutation. Bind npm's trusted publisher to that exact environment. Disallow traditional publishing tokens, inspect the staged tarball, and approve its promotion with human 2FA. Preserve `contentPolicy.class=dual-use` and the root `DISCLOSURE` in every published version.
5454
- Treat a `v*` tag as a release request, not a completed release. Publish the exact npm version first. Before tagging, confirm repository-level immutable releases are enabled; use a strictly increasing stable package version, keep the tag equal to `v<package.json version>` on `main`, and let the read-only verification job compare the source and registry packages by exact extracted path, type, mode, size, and regular-file hashes before its write-scoped publisher creates the Release. Verify each transport's npm and registry integrity independently because compressed tarball bytes may vary across operating systems. Recover a failed post-tag Release only through the explicit current-`main` workflow dispatch; bind the current workflow helpers to reviewed Git blobs and invoke them by absolute path against the tagged working tree after its explicit check/build, with tag-owned Bun config and environment loading disabled. Keep `npm pack --ignore-scripts` so recovery never depends on or reruns a historical `prepack`. Never move the tag or republish npm. Do not create the next tag until that workflow and Release are verified because GitHub concurrency is not a durable queue. After tagging, verify the matching non-draft immutable Release is Latest.

docs/publishing.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ in the npm package settings:
7777
- allowed action: `npm stage publish` only
7878
- environment: `npm-stage`
7979

80-
Create the protected `npm-stage` GitHub environment before enabling later
81-
publishing. Require reviewer `0thernet`, allow that maintainer to approve their
82-
own deployment with `prevent_self_review: false`, and restrict deployments to
83-
`main`. The npm trusted publisher must name that exact environment. Then
80+
Create the `npm-stage` GitHub environment before enabling later publishing.
81+
Restrict deployments to `main` and configure no required deployment reviewers,
82+
so a verified version bump reaches npm staging without a second GitHub
83+
approval. The npm trusted publisher must name that exact environment. Then
8484
require publishing two-factor authentication and disallow traditional tokens.
8585
Do not add an npm publishing token to GitHub. Preserve
8686
`contentPolicy.class=dual-use` and the root `DISCLOSURE` in every package.
@@ -96,9 +96,9 @@ Do not add an npm publishing token to GitHub. Preserve
9696
`npm-pack.json`, and `npm-package.sha256`, bound to the source commit,
9797
version, complete inventory, size, integrity, dual-use declaration, and
9898
disclosure.
99-
3. Approve the protected `npm-stage` environment as a maintainer. The minimal
100-
OIDC job then revalidates the artifact and current `main` before it stages
101-
the package.
99+
3. The minimal OIDC job starts automatically after verification. Its exact
100+
`npm-stage` environment allows only `main`, and the job revalidates the
101+
artifact and current branch head before it stages the package.
102102
4. Inspect and approve the staged package through npm with two-factor
103103
authentication.
104104
5. Verify the public registry package in a clean consumer.
@@ -108,15 +108,16 @@ Do not add an npm publishing token to GitHub. Preserve
108108

109109
If the automatic run is missing or fails before npm staging completes,
110110
dispatch **Stage npm package** from current `main`. Manual recovery runs the
111-
same verification and protected staging jobs. The workflow rejects a tag,
112-
another branch, or a commit behind the current default-branch head.
111+
same verification and main-branch-restricted staging jobs. The workflow rejects
112+
a tag, another branch, or a commit behind the current default-branch head.
113113

114114
The verification job checks out source, installs dependencies without
115115
lifecycle scripts, runs the complete gate, creates the three-file artifact,
116116
and smokes the exact tarball. Its dependent staging job is the only job with
117-
OIDC authority, and the protected `npm-stage` environment requires maintainer
118-
approval before that job starts. It checks out no source and runs no repository
119-
code. It rebinds identity, filename, inventory, count, modes, sizes, SHA-1,
117+
OIDC authority. The exact `npm-stage` environment restricts deployments to
118+
`main` and has no required reviewers, so the job starts automatically after
119+
verification. It checks out no source and runs no repository code. It rebinds
120+
identity, filename, inventory, count, modes, sizes, SHA-1,
120121
SHA-512, and the independent SHA-256 manifest before mutation. Immediately
121122
before staging,
122123
it fetches current `main` into a new bare Git directory, then rehashes all

scripts/check-workflow-yaml.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
)).toThrow("must recheck current default-branch HEAD");
5454
});
5555

56-
test("keeps npm staging version-selected, protected, tokenless, artifact-bound, and stage-only", async () => {
56+
test("keeps npm staging version-selected, environment-bound, tokenless, artifact-bound, and stage-only", async () => {
5757
const path = resolve(import.meta.dir, "../.github/workflows/npm-stage.yml");
5858
const source = await readFile(path, "utf8");
5959

@@ -101,13 +101,13 @@ jobs:
101101
expect(stage).not.toContain("./scripts/");
102102
});
103103

104-
test("requires the protected environment and fail-closed version selector", async () => {
104+
test("requires the exact environment and fail-closed version selector", async () => {
105105
const path = resolve(import.meta.dir, "../.github/workflows/npm-stage.yml");
106106
const source = await readFile(path, "utf8");
107107
expect(() => validateNpmStageWorkflow(
108108
source.replace("environment: npm-stage", "environment: unprotected"),
109109
"npm-stage.yml",
110-
)).toThrow("protected npm-stage environment");
110+
)).toThrow("exact npm-stage environment");
111111
expect(() => validateNpmStageWorkflow(
112112
source.replace(
113113
'git show "$BEFORE_SHA:package.json"',

scripts/check-workflow-yaml.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export function validateNpmStageWorkflow(source: string, label: string): void {
8585
throw new Error(`${label} staging must hold only id-token: write`);
8686
}
8787
if (stage.environment !== "npm-stage") {
88-
throw new Error(`${label} staging must use the protected npm-stage environment`);
88+
throw new Error(`${label} staging must use the exact npm-stage environment`);
8989
}
9090
if (!Array.isArray(select.steps)) {
9191
throw new Error(`${label} select steps must be a sequence`);

scripts/npm-release-workflow.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,23 +315,25 @@ describe("npm release workflows", () => {
315315
"Do not reuse the\ninteractive path for a later release",
316316
"[Stage a later version](#stage-a-later-version)",
317317
"version is unchanged",
318-
"protected `npm-stage` environment",
319-
"reviewer `0thernet`",
320-
"`prevent_self_review: false`",
318+
"exact `npm-stage` environment",
319+
"configure no required deployment reviewers",
320+
"allows only `main`",
321321
"rebinds the release helpers to their reviewed Git blobs",
322322
"invokes those files by absolute path",
323323
"`npm pack --ignore-scripts`",
324324
npmRegistry,
325325
] as const) expect(guide).toContain(required);
326326
expect(guide).toMatch(/the only job with\s+OIDC authority/u);
327-
expect(guide).toMatch(/requires maintainer\s+approval/u);
327+
expect(guide).toMatch(/has no required reviewers, so the job starts automatically/u);
328+
expect(guide).toMatch(/approve the staged package through npm with two-factor\s+authentication/u);
328329
expect(guide).toMatch(/checks out no source and runs no\s+repository\s+code/u);
329330
expect(guide).toMatch(/exactly the tarball,\s+`npm-pack\.json`, and `npm-package\.sha256`/u);
330331
expect(guide).toMatch(/new bare\s+Git directory/u);
331332
expect(guide).toMatch(/do not import a\s+script from the tagged tree/u);
332333
expect(agents).toContain("only its minimal dependent staging job may request OIDC");
333-
expect(agents).toContain("protected `npm-stage` environment");
334-
expect(agents).toContain("require reviewer `0thernet` while allowing self-review");
334+
expect(agents).toContain("use the exact `npm-stage` environment");
335+
expect(agents).toContain("restrict deployments to `main` without required deployment reviewers");
336+
expect(agents).toContain("approve its promotion with human 2FA");
335337
expect(agents).toContain("bind the current workflow helpers to reviewed Git blobs");
336338
expect(agents).toContain("recovery never depends on or reruns a historical `prepack`");
337339
});

0 commit comments

Comments
 (0)