From ecb0aa4fb843c69dee79b2f4214df849f2683b3c Mon Sep 17 00:00:00 2001 From: "Derek Palmer (Creative)" Date: Fri, 29 May 2026 16:15:50 -0400 Subject: [PATCH] ci(npm-publish): lock OIDC trusted publishing; correct changelog Add a workflow guard test asserting the npmjs publish job uses tokenless OIDC trusted publishing (id-token write, --provenance --access public) and references no NPM_TOKEN/NODE_AUTH_TOKEN, so a regression to long-lived tokens is caught. Correct the stale 0.4.1 NPM_TOKEN guidance forward in the 0.4.5 changelog. npm package-contents validation in release-pr already landed in #52. Closes #48 --- CHANGELOG.md | 1 + tests/test_workflows_yaml.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4da6be..b739a89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Retired `SPEC.md`; GitHub Issues now own work tracking. - `CONTEXT.md` and agent docs: added npm release and GitHub Issues glossary terms. - CodeRabbit automatic review disabled. +- **npmjs publishing is now OIDC trusted publishing** (tokenless). The `NPM_TOKEN` secret referenced in the 0.4.1 notes is no longer used or required; auth comes from the `npm` environment's `id-token: write` permission. (#48) ## [0.4.4] — 2026-05-26 diff --git a/tests/test_workflows_yaml.py b/tests/test_workflows_yaml.py index 9f4653b..30e6e85 100644 --- a/tests/test_workflows_yaml.py +++ b/tests/test_workflows_yaml.py @@ -91,6 +91,29 @@ def test_pypi_publish_workflow_uses_version_tag_and_oidc(): assert "pypa/gh-action-pypi-publish" in steps_text +def test_npm_publish_workflow_uses_oidc_trusted_publishing(): + wf = WORKFLOWS_DIR / "npm-publish.yml" + text = wf.read_text() + doc = yaml.safe_load(text) + trigger = _trigger(doc) + assert isinstance(trigger, dict), "trigger must be a mapping" + assert "v*.*.*" in trigger.get("push", {}).get("tags", []) + + publish = doc["jobs"].get("publish") + assert isinstance(publish, dict), "missing publish job" + # OIDC trusted publishing is tokenless: id-token write, no NPM_TOKEN. + assert publish.get("permissions", {}).get("id-token") == "write" + steps_text = "\n".join(str(step) for step in publish.get("steps", [])) + assert "--provenance" in steps_text + assert "--access public" in steps_text + + # The npmjs publish job must not reuse a long-lived token; that would + # silently bypass OIDC trusted publishing. + publish_only = yaml.dump(publish) + assert "NPM_TOKEN" not in publish_only + assert "NODE_AUTH_TOKEN" not in publish_only + + def test_docker_publish_workflow_uses_version_tag_and_ghcr(): wf = WORKFLOWS_DIR / "docker-publish.yml" doc = yaml.safe_load(wf.read_text())