@schalkneethling/calavera-skill-npm-trusted-publishing-github-workflow 0.2.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Schalk Neethling
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://calavera.schalkneethling.com/schemas/calavera-artifact.schema.json",
3
+ "schemaVersion": 1,
4
+ "id": "skill-npm-trusted-publishing-github-workflow",
5
+ "type": "skill",
6
+ "displayName": "npm trusted publishing GitHub workflow",
7
+ "payload": "payload/npm-trusted-publishing-github-workflow",
8
+ "compatibility": {
9
+ "calavera": ">=2.2.0 <3"
10
+ }
11
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@schalkneethling/calavera-skill-npm-trusted-publishing-github-workflow",
3
+ "version": "0.2.0-next.1",
4
+ "description": "npm trusted publishing GitHub workflow artifact for Calavera.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+ssh://git@github.com/schalkneethling/create-project-calavera.git",
9
+ "directory": "packages/artifacts/skill-npm-trusted-publishing-github-workflow"
10
+ },
11
+ "files": [
12
+ "calavera-artifact.json",
13
+ "payload"
14
+ ],
15
+ "type": "module",
16
+ "exports": {
17
+ ".": "./calavera-artifact.json",
18
+ "./package.json": "./package.json"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public",
22
+ "provenance": true
23
+ }
24
+ }
@@ -0,0 +1,341 @@
1
+ ---
2
+ name: npm-trusted-publishing-github-workflow
3
+ description: >
4
+ Generate, repair, or debug the GitHub Actions workflow FILE that performs an OIDC
5
+ trusted publish of a pnpm package — the concrete publish.yml, its test → build →
6
+ publish job shape, the package tarball artifact handoff, Node-version inference from
7
+ package.json, pnpm and runtime setup via pnpm/setup, the npm-CLI-version upgrade step, and
8
+ repository.url/Sigstore provenance matching. Use when the user wants the actual
9
+ workflow written or fixed, or is debugging a specific CI failure: npm publish
10
+ E404/E403/422, NODE_AUTH_TOKEN appearing unexpectedly, provenance or id-token errors,
11
+ pnpm/setup version resolution, or runtime version problems.
12
+ For the broader publishing SECURITY POSTURE — account 2FA, repository and branch
13
+ hardening, GitHub environments, changesets versus changelogithub, sole-maintainer risk,
14
+ or auditing an existing pipeline — use the npm-package-publishing skill instead.
15
+ ---
16
+
17
+ # NPM Trusted Publish
18
+
19
+ ## Goal
20
+
21
+ Implement the same hardened npm trusted publishing pattern every time, without rediscovering the details from CI logs.
22
+
23
+ ## Related skills
24
+
25
+ This skill generates and debugs the publish workflow file. For the surrounding security posture — account and repository 2FA, branch protection, GitHub publish environments, release-strategy choice, and sole-maintainer risk — use the `npm-package-publishing` skill. The two are complementary: `npm-package-publishing` decides how publishing should be set up, this skill writes and fixes the YAML that does it.
26
+
27
+ One number to keep consistent between the two: both skills use Node 24.8.0 or higher as the publish-step floor. Node 24.8.0 bundles npm 11.6.0, which already exceeds the npm CLI 11.5.1 minimum that trusted publishing requires, so on that floor no manual npm upgrade is needed. If a project must publish on an older Node, it has to upgrade npm to 11.5.1 or later first — the publish job retains a guard step for exactly that case.
28
+
29
+ ## Workflow
30
+
31
+ 1. Inspect `package.json`, `.npmrc`, lockfiles, and existing `.github/workflows/*.yml`.
32
+ 2. Resolve every workflow dependency to its latest stable version at the moment the file is created, and pin each to the full-length commit SHA of that version. Never leave third-party or GitHub-owned actions pinned to tag-based refs such as `@v4`, `@v6`, or `@v7` in the final workflow; tag refs weaken supply-chain integrity and violate pinned-action policy. The SHAs in this skill's template are placeholders that will be out of date; never copy them verbatim. See "Pinning actions to current SHAs" below for the procedure.
33
+ 3. Preserve pinned action SHAs when they already exist; annotate each with a version comment so Dependabot can bump it.
34
+ 4. Drive the test and build jobs' Node version from the project's **existing** target, not from a number invented for this workflow. Read it from the repo's current `devEngines.runtime`, `.nvmrc`, `.node-version`, `volta.node`, or CI config; if none exists, ask the developer rather than guessing. For pnpm projects, prefer `pnpm/setup` and either let it read `devEngines.runtime` from `package.json` or set its `runtime` input to the resolved existing target, such as `node@22`. Do not point runtime selection at `engines.node`, which is the consumer compatibility range — an unbounded range like `>=20` can float CI away from the version developers actually run.
35
+ 5. Never raise the project's Node version, create a new `.nvmrc`, or overwrite an existing one to "match" the publish step. The publish step's Node 24.8.0 (step 11) is an isolated requirement of the publish action and must not propagate to `.nvmrc`, to `engines.node`, or to the test and build jobs. A project that targets Node 22 keeps testing and building on Node 22; only the final `npm publish` invocation runs on 24.8.0, and it does not rebuild the artifact. Conflating these two numbers is the most likely way this skill is misapplied — do not do it.
36
+ 6. Ensure every job that reads the repo (including any reading `.nvmrc`) runs `actions/checkout` first.
37
+ 7. For pnpm workflows, use `pnpm/setup` from the GitHub Marketplace instead of combining `pnpm/action-setup` with `actions/setup-node`. It installs pnpm from `@pnpm/exe` and installs the requested JavaScript runtime through `pnpm runtime set` in one step. Pin it to a full commit SHA like every other action.
38
+ 8. Set `install: false` on `pnpm/setup` in release/publish workflows, then run `pnpm install --frozen-lockfile --ignore-scripts` explicitly. The action can auto-install by default, but release workflows should keep install flags visible and hardened.
39
+ 9. Do not use Corepack in release workflows: it is still marked experimental and downloads the package manager from the network on first use, which is an avoidable failure surface in a release pipeline.
40
+ 10. Set `persist-credentials: false` on every `actions/checkout` step. Never rely on checkout's default credential persistence. If a workflow genuinely must push to git, use an explicit, narrowly scoped credential only for that push step.
41
+ 11. Target Node 24.8.0 or higher in the publish step. That floor bundles npm 11.6.0, which already exceeds the npm CLI 11.5.1 minimum trusted publishing requires, so no manual npm upgrade is needed there. Keep a guard step that upgrades npm only when the resolved Node ships an npm below 11.5.1, so the workflow stays correct if a project pins an older Node. An npm that is too old silently falls back to token auth or fails to attempt OIDC at all.
42
+ 12. Pack into a dedicated artifact directory, usually `package/*.tgz`.
43
+ 13. In the publish job, download the artifact to `package`, find the `.tgz`, and publish its resolved path.
44
+ 14. Use GitHub OIDC trusted publishing, not npm tokens. Provenance is generated automatically under trusted publishing, so the `--provenance` flag is not required.
45
+ 15. Add a `concurrency` group keyed on the release so two tag pushes cannot race into overlapping publishes.
46
+
47
+ ## GitHub Token Permissions
48
+
49
+ Every GitHub Actions workflow this skill creates or edits must declare explicit least-privilege
50
+ `GITHUB_TOKEN` permissions. Add a top-level `permissions:` block that grants the workflow-wide
51
+ minimum, usually `contents: read`, then add job-level `permissions:` only where a job needs more.
52
+
53
+ For trusted npm publishing, only the publish job should receive `id-token: write`; test and build
54
+ jobs should stay at `contents: read`. If a project genuinely needs another scope, grant it only to
55
+ the specific job that requires it and document why in the workflow review notes. Never rely on
56
+ GitHub's repository default token permissions.
57
+
58
+ Every `actions/checkout` step must include `persist-credentials: false`, including jobs that build
59
+ or upload artifacts. Persisted checkout credentials unnecessarily leave `GITHUB_TOKEN` available to
60
+ later build, test, packaging, and artifact steps.
61
+
62
+ ## Package Metadata
63
+
64
+ Three different Node versions live in three different places, and keeping them separate is deliberate — conflating them is the main way this workflow goes wrong. `engines.node` in `package.json` is the _consumer_ floor: the only one that constrains people who install the package, and it should reflect what the package actually supports (npm warns, but does not hard-fail, when a consumer is outside it). The test and build jobs run on the project's _own_ target version, read from `devEngines.runtime` or the existing `.nvmrc`/`.node-version`/`volta.node`/CI config; these are development and CI targets, so they do not leak into the consumer contract. The publish step pins Node 24.8.0 or higher independently, purely because that floor bundles an npm new enough for OIDC. These three are not meant to agree: a repo can develop and test on Node 22, keep `engines.node` at its true support range, and still publish on Node 24 — all without affecting consumers, and without changing what the project builds and tests against.
65
+
66
+ The publish-step version must never be copied into the other two. Do not raise `engines.node` to 24.8.0, and do not set or bump `.nvmrc` or `devEngines.runtime` to 24, to "make things consistent". Doing so would move the test and build jobs onto Node 24, so the package would be validated against a version above its actual target and a Node-22 incompatibility could ship uncaught. The publish job runs `npm publish` on the already-built tarball with scripts ignored, so its Node version never rebuilds or retests the code; it plays no role in building the artifact.
67
+
68
+ ```json
69
+ {
70
+ "engines": {
71
+ "node": ">=20"
72
+ },
73
+ "packageManager": "pnpm@11.0.4",
74
+ "devEngines": {
75
+ "runtime": { "name": "node", "version": "^22.0.0", "onFail": "download" }
76
+ },
77
+ "repository": {
78
+ "type": "git",
79
+ "url": "git+https://github.com/OWNER/REPO.git"
80
+ }
81
+ }
82
+ ```
83
+
84
+ The `engines.node` value above is the _consumer_ floor and should reflect what the package actually supports; `>=20` is only an example, and a bounded upper limit is sensible if the package genuinely needs one. Do not raise it to 24.8.0 to satisfy CI — the publish step pins its own Node version, and the test and build jobs read theirs from `devEngines.runtime` or another existing project target, so the trusted-publishing requirement never leaks into the consumer contract.
85
+
86
+ For pnpm projects, prefer declaring the development runtime in `devEngines.runtime` so `pnpm/setup`
87
+ can read the runtime and version from `package.json`. If the repo already uses `.nvmrc`,
88
+ `.node-version`, `volta.node`, or existing CI config instead, keep that source of truth and set
89
+ `pnpm/setup`'s `runtime` input to the same resolved version. Do not default to the publish step's
90
+ 24.8.0.
91
+
92
+ The `repository.url` field is not cosmetic. Provenance verification runs through Sigstore, which compares the repository in the OIDC token against `package.json`. A mismatch fails the publish with a 422 error that the user-facing npm docs do not explain. Make sure the owner/name in `repository.url` matches the repository actually running the workflow.
93
+
94
+ Do not add npm auth tokens for trusted publishing.
95
+
96
+ ## Workflow Template
97
+
98
+ Use this shape for pnpm packages, adapting only names, test commands, and existing pinned action SHAs. The `@<sha>` values below are **placeholders**: before writing the file, resolve each action to its latest stable release and replace the placeholder with that release's full-length commit SHA, keeping the `# vX.Y.Z` comment accurate. Do not copy the example SHAs — see "Pinning actions to current SHAs".
99
+
100
+ ```yaml
101
+ # NOTE: every action SHA below is a PLACEHOLDER and is almost certainly out of date.
102
+ # Re-resolve each action to its latest stable release and pin to that SHA before use.
103
+ # See "Pinning actions to current SHAs".
104
+ name: Publish
105
+
106
+ on:
107
+ release:
108
+ types: [published]
109
+
110
+ permissions:
111
+ contents: read
112
+
113
+ concurrency:
114
+ group: publish-${{ github.event.release.tag_name }}
115
+ cancel-in-progress: false
116
+
117
+ jobs:
118
+ test:
119
+ name: Test
120
+ runs-on: ubuntu-latest
121
+ timeout-minutes: 60
122
+ permissions:
123
+ contents: read
124
+ steps:
125
+ - name: Checkout
126
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2 — PLACEHOLDER SHA, re-resolve before use
127
+ with:
128
+ persist-credentials: false
129
+
130
+ - name: Setup pnpm and Node.js
131
+ uses: pnpm/setup@1111111111111111111111111111111111111111 # v1.0.0 — PLACEHOLDER SHA, re-resolve before use
132
+ with:
133
+ # Runtime is read from devEngines.runtime when present. If the project uses
134
+ # .nvmrc/.node-version/volta instead, set runtime to that exact target
135
+ # (for example, runtime: node@22). Do not use engines.node here.
136
+ install: false
137
+
138
+ - name: Install dependencies
139
+ run: pnpm install --frozen-lockfile --ignore-scripts
140
+
141
+ - name: Check package
142
+ run: pnpm run package:check
143
+
144
+ - name: Run tests
145
+ run: pnpm test
146
+
147
+ build:
148
+ name: Pack package
149
+ needs: test
150
+ runs-on: ubuntu-latest
151
+ timeout-minutes: 10
152
+ permissions:
153
+ contents: read
154
+ steps:
155
+ - name: Checkout
156
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2 — PLACEHOLDER SHA, re-resolve before use
157
+ with:
158
+ persist-credentials: false
159
+
160
+ - name: Setup pnpm and Node.js
161
+ uses: pnpm/setup@1111111111111111111111111111111111111111 # v1.0.0 — PLACEHOLDER SHA, re-resolve before use
162
+ with:
163
+ # Runtime is read from devEngines.runtime when present. If the project uses
164
+ # .nvmrc/.node-version/volta instead, set runtime to that exact target
165
+ # (for example, runtime: node@22). Do not use engines.node here.
166
+ install: false
167
+
168
+ - name: Install dependencies
169
+ run: pnpm install --frozen-lockfile --ignore-scripts
170
+
171
+ - name: Create package directory
172
+ run: mkdir package
173
+
174
+ - name: Create package tarball
175
+ run: pnpm pack --pack-destination package
176
+
177
+ - name: Upload package tarball
178
+ uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 — PLACEHOLDER SHA, re-resolve before use
179
+ with:
180
+ name: npm-package
181
+ path: package/*.tgz
182
+ if-no-files-found: error
183
+ retention-days: 7
184
+
185
+ publish:
186
+ name: Publish to npm
187
+ needs: build
188
+ runs-on: ubuntu-latest
189
+ timeout-minutes: 10
190
+ environment: publish
191
+ permissions:
192
+ contents: read
193
+ id-token: write
194
+ steps:
195
+ - name: Checkout
196
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2 — PLACEHOLDER SHA, re-resolve before use
197
+ with:
198
+ persist-credentials: false
199
+
200
+ - name: Setup pnpm and Node.js
201
+ uses: pnpm/setup@1111111111111111111111111111111111111111 # v1.0.0 — PLACEHOLDER SHA, re-resolve before use
202
+ with:
203
+ # Pinned for the publish step only. 24.8.0 bundles npm 11.6.0, new enough
204
+ # for OIDC; this is independent of engines.node, the consumer floor.
205
+ runtime: node@24.8.0
206
+ install: false
207
+
208
+ - name: Ensure npm is new enough for trusted publishing
209
+ # No-op on Node >= 24.8.0; the guard only matters if Node is pinned lower.
210
+ run: |
211
+ required="11.5.1"
212
+ pinned_npm="11.6.0"
213
+ current="$(npm --version)"
214
+ if node - "$current" "$required" <<'NODE'
215
+ const [current, required] = process.argv.slice(2);
216
+ const parse = (version) => version.split(".").map((part) => Number.parseInt(part, 10));
217
+ const [currentMajor, currentMinor, currentPatch] = parse(current);
218
+ const [requiredMajor, requiredMinor, requiredPatch] = parse(required);
219
+ const currentParts = [currentMajor, currentMinor, currentPatch];
220
+ const requiredParts = [requiredMajor, requiredMinor, requiredPatch];
221
+ let isBelow = false;
222
+ for (const [index, part] of currentParts.entries()) {
223
+ if (part !== requiredParts[index]) {
224
+ isBelow = part < requiredParts[index];
225
+ break;
226
+ }
227
+ }
228
+ process.exit(isBelow ? 1 : 0);
229
+ NODE
230
+ then
231
+ echo "npm $current satisfies $required."
232
+ else
233
+ echo "npm $current is below $required; upgrading to pinned npm $pinned_npm."
234
+ npm install -g "npm@$pinned_npm"
235
+ fi
236
+ npm --version
237
+
238
+ - name: Download package tarball
239
+ uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 — PLACEHOLDER SHA, re-resolve before use
240
+ with:
241
+ name: npm-package
242
+ path: package
243
+
244
+ - name: Publish to npm
245
+ run: |
246
+ tarball="$(find package -type f -name '*.tgz' -print -quit)"
247
+
248
+ if [ -z "$tarball" ]; then
249
+ echo "No package tarball found in downloaded artifact."
250
+ find package -maxdepth 3 -type f -print
251
+ exit 1
252
+ fi
253
+
254
+ npm publish "$(realpath "$tarball")" --ignore-scripts --access public --registry https://registry.npmjs.org
255
+ ```
256
+
257
+ ## Pinning actions to current SHAs
258
+
259
+ The template's SHAs are stale by design. Action versions and their commit SHAs change over time, so resolve them fresh whenever a `publish.yml` is created or reviewed. Pin to the full-length commit SHA, never a tag or branch, because a tag can be moved to point at malicious code after you have reviewed it. Tag-based refs such as `@v4`, `@v6`, and `@v7` are acceptable only as temporary input to a pinning tool; they must not survive in committed workflow YAML.
260
+
261
+ There are two reliable ways to produce current pins.
262
+
263
+ The preferred approach is to let tooling resolve and pin for you. Write the workflow first using human-readable tags only in the temporary draft consumed by the tool (for example `actions/checkout@v4`), then run `npx actions-up@1.16.0` in the repository to rewrite every `uses:` reference to the latest stable release pinned to its commit SHA, with a version comment appended. This is the same tool the `npm-package-publishing` skill recommends, and it removes the chance of a hand-typed SHA being wrong. After it runs, confirm each line carries a `@<40-hex-sha> # vX.Y.Z` form.
264
+
265
+ If resolving manually, for each action find the latest stable release tag, then read the exact commit that tag points to and pin that commit:
266
+
267
+ ```bash
268
+ # Latest stable release tag for an action (skips pre-releases)
269
+ gh release view --repo actions/checkout --json tagName --jq .tagName
270
+
271
+ # The commit SHA that the tag resolves to — pin THIS value
272
+ gh api repos/actions/checkout/git/refs/tags/v4.2.2 --jq .object.sha
273
+ ```
274
+
275
+ For an annotated tag the first lookup may return a tag object rather than a commit; dereference it with `gh api repos/<owner>/<repo>/git/tags/<sha> --jq .object.sha` to reach the underlying commit. Pin the commit SHA, not the tag SHA.
276
+
277
+ Keep the pins current after creation by letting Dependabot manage action updates. This is why every `uses:` line carries a `# vX.Y.Z` comment: Dependabot reads the comment to know which version a SHA represents and to raise update PRs. The companion Dependabot configuration should include a `github-actions` ecosystem entry pointing at `/` so the publish workflow is covered. Periodically re-running `npx actions-up@1.16.0` is a reasonable backstop if Dependabot is not enabled.
278
+
279
+ ## Checks
280
+
281
+ After edits:
282
+
283
+ ```bash
284
+ ruby -e 'require "yaml"; YAML.load_file(".github/workflows/publish.yml"); puts "YAML ok"'
285
+ ```
286
+
287
+ If the project uses pnpm, validate packing without publishing:
288
+
289
+ ```bash
290
+ pack_dir="$(mktemp -d)"
291
+ pnpm pack --pack-destination "$pack_dir"
292
+ ```
293
+
294
+ Confirm no placeholder markers survived into the generated file, and that every action is pinned to a 40-character SHA rather than a tag:
295
+
296
+ ```bash
297
+ # Must print nothing and fail if a marker remains
298
+ if grep -n "PLACEHOLDER" .github/workflows/publish.yml; then
299
+ echo "Placeholder marker found" >&2
300
+ exit 1
301
+ fi
302
+
303
+ # Every uses: line must reference a 40-hex SHA, not a tag
304
+ if ! grep -qE "uses: [^@]+@" .github/workflows/publish.yml; then
305
+ echo "No uses lines found"
306
+ exit 1
307
+ fi
308
+
309
+ bad_refs="$(grep -nE "uses: [^@]+@[^ ]+" .github/workflows/publish.yml \
310
+ | grep -vE "@[0-9a-f]{40} " || true)"
311
+ if [ -n "$bad_refs" ]; then
312
+ echo "$bad_refs"
313
+ echo "Unpinned action found" >&2
314
+ exit 1
315
+ fi
316
+ echo "All actions SHA-pinned"
317
+ ```
318
+
319
+ ## Failure Clues
320
+
321
+ - `NODE_AUTH_TOKEN: ***` appears in the publish log: token auth is being used or injected. Trusted publishing should not need it.
322
+ - `E404 Not Found - PUT ... could not be found or you do not have permission`: often an auth/scope permission problem, especially if local manual publish works.
323
+ - `422 Unprocessable Entity` during publish with provenance: the repository in the OIDC token does not match `package.json`. Check `repository.url` first.
324
+ - npm silently publishing with a token despite trusted-publisher config: the runner's npm CLI is older than 11.5.1. This should not happen on the pinned Node 24.8.0 (which bundles npm 11.6.0); if the publish step was moved to an older Node, confirm the guard step actually upgraded npm and reported a version at or above 11.5.1.
325
+ - Tests or build now run on a newer Node than the project targets (for example Node 24 when the project is on 22): `.nvmrc` was created or bumped to match the publish step. Reset it to the project's actual target; the publish step's 24.8.0 must stay confined to the publish job.
326
+ - `package.json does not exist` from `pnpm/setup`: the job runs setup before checkout, or `package-json-file` points at the wrong path.
327
+ - `pnpm/setup` cannot resolve a pnpm version: the `packageManager` field is missing. Add the correct `packageManager` field or set the action's `version` input explicitly as a fallback.
328
+ - `pnpm/setup` installs the wrong runtime: `devEngines.runtime` is missing or does not match the project's existing CI target. Add or correct `devEngines.runtime`, or set the action's `runtime` input explicitly.
329
+ - Publishing an already-published version will fail even after the workflow is fixed.
330
+
331
+ ## External Setup Reminder
332
+
333
+ Repo changes cannot create npm's trusted publisher entry. Remind the user to verify npm package settings:
334
+
335
+ - provider: GitHub Actions
336
+ - repository owner/name matches the repo
337
+ - workflow filename matches `.github/workflows/publish.yml`
338
+ - publish environment matches the workflow if npm is configured with one
339
+ - at least one allowed action is selected: configurations created after 20 May 2026 require explicitly selecting an allowed action (for example, allow `npm publish`), or the publish will be rejected
340
+
341
+ The first version of a brand-new package cannot be published via OIDC, because npm requires the package to exist before its trusted-publisher settings can be edited. Publish the initial version manually or with a token, then configure trusted publishing for subsequent releases.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "npm Trusted Publishing Workflow"
3
+ short_description: "Build a secure npm OIDC publish workflow."
4
+ default_prompt: "Use $npm-trusted-publishing-github-workflow to create a trusted npm publish workflow."