@schalkneethling/calavera-skill-npm-publishing-best-practices 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-publishing-best-practices",
5
+ "type": "skill",
6
+ "displayName": "npm publishing best practices",
7
+ "payload": "payload/npm-publishing-best-practices",
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-publishing-best-practices",
3
+ "version": "0.2.0-next.1",
4
+ "description": "npm publishing best practices 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-publishing-best-practices"
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,436 @@
1
+ ---
2
+ name: npm-package-publishing
3
+ description: >
4
+ Audit and improve the security posture of npm package publishing: account security, npm trusted
5
+ publishing strategy, GitHub repository hardening, token removal, release governance, dependency
6
+ update policy, provenance, and supply-chain risk. Use when the user asks about npm publishing best
7
+ practices, publishing security, OIDC/trusted publishing strategy, npm token hygiene, release
8
+ automation posture, Changesets versus changelog strategies, publint, provenance, or auditing an
9
+ existing publishing pipeline. For writing or debugging the concrete GitHub Actions publish
10
+ workflow file, use the npm-trusted-publishing-github-workflow skill instead.
11
+ ---
12
+
13
+ # npm Package Publishing — Best Practices
14
+
15
+ Based on the [e18e publishing guide](https://e18e.dev/docs/publishing.html). Reference it for
16
+ the canonical source; this skill distils the actionable steps.
17
+
18
+ ## Agent Workflow
19
+
20
+ 1. Inspect the repository before recommending changes:
21
+ - `package.json`
22
+ - lockfiles and `packageManager`
23
+ - `.npmrc`, `.yarnrc.yml`, `.github/workflows/*`
24
+ - release tooling such as Changesets, changelogithub, semantic-release, or release-it
25
+ - existing npm/GitHub tokens or `NODE_AUTH_TOKEN` usage in workflows
26
+ 2. Classify the request:
27
+ - **Audit**: report risks by severity, with file and line references where possible.
28
+ - **Implementation**: make scoped repository changes, then run relevant validation.
29
+ - **Strategy**: explain tradeoffs and identify user-only settings.
30
+ 3. Separate agent-doable work from user-only UI work. Do not claim account, npmjs.com, or GitHub
31
+ settings are configured unless verified by authenticated tool/API access.
32
+ 4. For concrete GitHub Actions publish workflow creation or CI failure debugging, hand off to the
33
+ `npm-trusted-publishing-github-workflow` skill.
34
+
35
+ ## Freshness Rule
36
+
37
+ Before changing trusted publishing requirements, supported CI providers, npm CLI minimums, Node.js
38
+ minimums, provenance behavior, or GitHub release/security settings, verify the current official npm
39
+ and GitHub documentation when browsing is available. These requirements change over time.
40
+
41
+ ## Responsibility Split
42
+
43
+ Agent can usually:
44
+
45
+ - Edit `.github/workflows/*`.
46
+ - Add or update repository package-manager config such as `.npmrc` or `.yarnrc.yml`.
47
+ - Remove `NODE_AUTH_TOKEN` usage from publish steps when trusted publishing is used.
48
+ If private dependencies require registry auth during install, keep that token scoped to read-only
49
+ install steps only.
50
+ - Add Dependabot/Renovate config.
51
+ - Run `publint`, workflow linting, tests, package builds, and other local validation.
52
+ - Report exact user steps for npm/GitHub settings.
53
+
54
+ User or authenticated UI/API access required:
55
+
56
+ - Enable npm/GitHub 2FA.
57
+ - Configure npm trusted publisher settings on npmjs.com.
58
+ - Set package publishing access to require 2FA and disallow tokens.
59
+ - Enable GitHub repository/org Actions restrictions.
60
+ - Configure branch/tag rulesets and immutable releases.
61
+ - Remove repository/org secrets when the agent lacks GitHub settings access.
62
+
63
+ ## Non-Negotiables
64
+
65
+ - Do not add `NODE_AUTH_TOKEN` to publish steps when trusted publishing is available.
66
+ Use OIDC for publishing; if private dependencies require install auth, use a read-only token only
67
+ for the install step.
68
+ - Do not store npm publish tokens in GitHub Actions secrets for OIDC-capable publishing.
69
+ - Do not run install lifecycle scripts in release workflows unless the user explicitly accepts the
70
+ risk.
71
+ - Do not show tag-pinned actions as compliant with SHA pinning; resolve all third-party and
72
+ GitHub-owned actions to full commit SHAs. Tag-based refs such as `@v6` or `@v7` weaken
73
+ supply-chain integrity and violate pinned-action policy.
74
+ - Do not create or edit GitHub Actions workflows without explicit least-privilege `permissions:`
75
+ for `GITHUB_TOKEN`. Set a restrictive top-level default such as `contents: read`, then grant
76
+ elevated scopes only on the jobs that need them.
77
+ - Do not use `actions/checkout` with default credential persistence. Always set
78
+ `persist-credentials: false`; if a job must push to git, provide an explicit, narrowly scoped
79
+ credential only for that push step.
80
+ - Do not say npm/GitHub account settings are complete unless they were actually checked.
81
+ - Do not use self-hosted runners for npm trusted publishing unless official npm docs currently
82
+ support them.
83
+
84
+ > **Package manager note.** The example install and configuration commands in this skill use `npm`
85
+ > to match the e18e source material, but should be adapted to whichever package manager the project
86
+ > already uses — `pnpm`, `yarn`, `bun`, etc. Adapt commands accordingly:
87
+ >
88
+ > | npm | pnpm | yarn |
89
+ > | --------------------------------- | ------------------------------------------------- | ------------------------------------------------- |
90
+ > | `npm ci --ignore-scripts` | `pnpm install --frozen-lockfile --ignore-scripts` | `yarn install --frozen-lockfile --ignore-scripts` |
91
+ > | `npm i -g npm@12.0.1` | `pnpm add -g pnpm@11.13.1` | `yarn set version 4.17.1` |
92
+ > | `ignore-scripts=true` in `.npmrc` | `ignore-scripts=true` in `.npmrc` | `enableScripts: false` in `.yarnrc.yml` |
93
+ >
94
+ > Detect the project's package manager by checking for a lockfile (`pnpm-lock.yaml`,
95
+ > `yarn.lock`, `bun.lock`) or a `packageManager` field in `package.json` before
96
+ > generating any commands or workflow steps.
97
+ >
98
+ > Section 2.2's trusted-publishing requirement is npm-specific: the publish step must run with a
99
+ > supported Node.js version and npm CLI version even when the rest of the workflow uses another
100
+ > package manager.
101
+ >
102
+ > For pnpm projects on GitHub Actions, prefer the Marketplace action
103
+ > [`pnpm/setup`](https://github.com/marketplace/actions/setup-pnpm-with-runtime) to set up pnpm and
104
+ > the JavaScript runtime in one step. It installs pnpm from `@pnpm/exe`, can read
105
+ > `devEngines.runtime` from `package.json`, and replaces the separate
106
+ > `pnpm/action-setup` + `actions/setup-node` pattern. In release workflows, pin `pnpm/setup` to a
107
+ > full commit SHA, set `install: false`, and run the explicit hardened install command yourself so
108
+ > flags like `--frozen-lockfile --ignore-scripts` remain visible.
109
+
110
+ ---
111
+
112
+ ## 1 · Prerequisites
113
+
114
+ ### 1.1 · Enforce 2FA everywhere
115
+
116
+ | Account | Location | Recommended method |
117
+ | ------- | -------------------------------------- | ---------------------------------------------------------------------------- |
118
+ | npm | Account → Security | Security key (YubiKey, Touch ID, Windows Hello); fallback: authenticator app |
119
+ | GitHub | Settings → Password and authentication | Same priority order |
120
+
121
+ Use a password manager with generated passwords for both accounts.
122
+
123
+ ### 1.2 · Harden GitHub Actions settings
124
+
125
+ `Settings → Actions → General`:
126
+
127
+ - ✅ Require actions to be pinned to a **full-length commit SHA**
128
+ - ✅ Require approval for first-time contributors
129
+ - ✅ Set default workflow permissions to **"Read repository contents and packages"**
130
+
131
+ > If the repository belongs to an organisation, apply these settings at org level for consistency
132
+ > across all repositories.
133
+
134
+ ### 1.3 · Configure branch protection
135
+
136
+ `Settings → Branches` → create a ruleset for `main`:
137
+
138
+ - ✅ Require a pull request before merging
139
+ - ✅ Require at least 1 approval
140
+ - ✅ Dismiss stale approvals when new commits are pushed
141
+ - ✅ Require approval of the most recent reviewable push
142
+
143
+ ### 1.4 · Remove legacy npm tokens
144
+
145
+ `Settings → Secrets & Variables → Actions`: remove npm publish tokens. Retain any read-only
146
+ registry credential required for private installs, scoped to install steps only.
147
+
148
+ ---
149
+
150
+ ## 2 · Trusted Publishing (OIDC)
151
+
152
+ Trusted publishing means GitHub Actions authenticates directly with npm via OIDC — no npm token
153
+ ever touches the repository.
154
+
155
+ ### 2.1 · Configure on npmjs.com
156
+
157
+ 1. Open your package page → **Settings** tab → **Trusted Publishing** section.
158
+ 2. Add a trusted publisher:
159
+ - **Organisation / user**: your GitHub org or username
160
+ - **Repository**: the repository name
161
+ - **Workflow filename**: e.g. `publish.yml`
162
+ 3. Check **"Require two-factor authentication and disallow tokens"** — this forces manual publishes
163
+ to use 2FA as well.
164
+
165
+ > For bulk configuration across many packages, use
166
+ > [`open-packages-on-npm`](https://github.com/antfu/open-packages-on-npm) to open each package
167
+ > in a new tab, then the
168
+ > [npm-trusted-publisher userscript](https://github.com/sxzz/userscripts/blob/main/src/npm-trusted-publisher.md)
169
+ > to configure them rapidly.
170
+
171
+ ### 2.2 · npm CLI version requirement
172
+
173
+ The publish step **must** use npm CLI ≥ 11.5.1 for automatic OIDC trusted publishing.
174
+ Node.js 22.14.0 or newer can use trusted publishing when the workflow installs npm CLI 11.5.1 or
175
+ newer before publishing:
176
+
177
+ ```yaml
178
+ - run: npm i -g npm@12.0.1
179
+ ```
180
+
181
+ ---
182
+
183
+ ## 3 · Standard Publish Workflow
184
+
185
+ Use the [e18e setup-publish template](https://github.com/e18e/setup-publish/blob/main/templates/default.yml)
186
+ as your base, or scaffold it with:
187
+
188
+ ```bash
189
+ npx @e18e/setup-publish@1.1.0
190
+ ```
191
+
192
+ ### 3.1 · Job structure
193
+
194
+ The workflow **must** separate build from publish. This ensures publish permissions (the OIDC
195
+ token) are never exposed to build-time code.
196
+
197
+ ```text
198
+ test → build → publish
199
+ ```
200
+
201
+ ### 3.2 · Non-negotiable workflow constraints
202
+
203
+ | Constraint | Why |
204
+ | ------------------------------------------------------ | ----------------------------------------------------------- |
205
+ | All actions pinned to full-length commit SHA | Prevents supply-chain attacks via mutable tags |
206
+ | Explicit least-privilege `GITHUB_TOKEN` permissions | Avoids inheriting broad repository defaults |
207
+ | `actions/checkout` uses `persist-credentials: false` | Reduces token exposure to later workflow steps |
208
+ | `npm ci --ignore-scripts` (or `--ignore-scripts` flag) | Prevents malicious lifecycle scripts running during install |
209
+ | Build and publish in **separate jobs** | Isolates publish permissions from arbitrary build code |
210
+
211
+ Always include a top-level `permissions:` block with the workflow-wide minimum, usually
212
+ `contents: read`. Add job-level permissions only when a job requires more access; for npm trusted
213
+ publishing, `id-token: write` belongs on the publish job only.
214
+
215
+ Every checkout step must disable persisted credentials:
216
+
217
+ ```yaml
218
+ - uses: actions/checkout@<full-length-commit-sha>
219
+ with:
220
+ persist-credentials: false
221
+ ```
222
+
223
+ This is especially important for jobs that build packages or upload artifacts, because later steps
224
+ should not inherit a repository token unless they explicitly need one.
225
+
226
+ ### 3.3 · Suppress lifecycle scripts project-wide
227
+
228
+ Add to `.npmrc` in the repository:
229
+
230
+ ```ini
231
+ ignore-scripts=true
232
+ ```
233
+
234
+ Also apply globally on developer machines:
235
+
236
+ ```bash
237
+ npm config set -g ignore-scripts true
238
+ ```
239
+
240
+ ### 3.4 · Creating a release
241
+
242
+ ```bash
243
+ git tag v1.0.0
244
+ git push origin v1.0.0
245
+ ```
246
+
247
+ Then in GitHub UI: **Releases → Draft a new release** → choose the tag → **Generate release notes**
248
+ → **Publish release**. This triggers the workflow.
249
+
250
+ ---
251
+
252
+ ## 4 · Alternative Workflow Strategies
253
+
254
+ ### 4.1 · Changesets (recommended for teams)
255
+
256
+ Use the [changesets template](https://github.com/e18e/setup-publish/blob/main/templates/changesets.yml).
257
+
258
+ - Merged PRs automatically update a release pull request
259
+ - Changelog is generated by changesets and included in the release PR
260
+ - Releasing = merging the generated release PR — no manual tagging
261
+
262
+ ### 4.2 · changelogithub (changelog from commit messages)
263
+
264
+ Use the [changelogithub template](https://github.com/e18e/setup-publish/blob/main/templates/changelogithub.yml).
265
+
266
+ - Tags are still pushed manually
267
+ - GitHub release + changelog are created automatically on tag push
268
+ - Package is published on tag push
269
+
270
+ ---
271
+
272
+ ## 5 · Ongoing Maintenance
273
+
274
+ ### 5.1 · Keep dependencies updated
275
+
276
+ Set up **Dependabot** or **Renovate** to receive automated PRs for dependency updates,
277
+ addressing security vulnerabilities promptly.
278
+
279
+ ### 5.2 · Keep GitHub Actions updated
280
+
281
+ All third-party and GitHub-owned actions must be pinned to a full commit SHA, not a tag. Tag refs
282
+ such as `@v6` or `@v7` are mutable and must not be committed to workflow files. To migrate existing
283
+ workflows and keep them current:
284
+
285
+ ```bash
286
+ npx actions-up@1.16.0
287
+ ```
288
+
289
+ Run this periodically, or let Dependabot/Renovate manage action updates once SHAs are in place.
290
+
291
+ ### 5.3 · Lint workflows for vulnerabilities
292
+
293
+ [`zizmor`](https://github.com/zizmorcore/zizmor) detects template injection vulnerabilities and
294
+ excessive permission scopes in GitHub workflow files:
295
+
296
+ ```bash
297
+ zizmor .github/workflows/publish.yml
298
+ ```
299
+
300
+ Integrate this into CI or run it before merging workflow changes.
301
+
302
+ ### 5.4 · Validate package.json and exports
303
+
304
+ [`publint`](https://publint.dev) checks for common publishing issues: missing files, incorrect
305
+ `exports` fields, wrong `main`/`module` paths, and more.
306
+
307
+ ```bash
308
+ npx publint@0.3.21
309
+ ```
310
+
311
+ Review the [full list of publint rules](https://publint.dev/rules) to understand what it checks.
312
+ Run this locally before tagging a release.
313
+
314
+ ### 5.5 · Visualise dependency changes
315
+
316
+ [`multiocular`](https://github.com/multiocular-com/multiocular) shows exactly what code changed
317
+ between dependency versions, helping catch unexpected changes or potential security issues.
318
+
319
+ ---
320
+
321
+ ## 6 · Further Security Hardening
322
+
323
+ ### 6.1 · Use a GitHub environment (important for shared repos)
324
+
325
+ Without a GitHub environment, **any account with write access can trigger a publish** by creating
326
+ a branch and modifying the release workflow — bypassing code review entirely.
327
+
328
+ `Settings → Environments`:
329
+
330
+ 1. Create an environment named `publish`.
331
+ 2. **Do not** allow administrator bypass of protection rules.
332
+ 3. Limit deployment to explicit branch names only (e.g. `main`, `v1`). Do not use wildcards.
333
+ Remove stale branches promptly.
334
+ 4. Update the publish job in `publish.yml`:
335
+
336
+ ```yaml
337
+ jobs:
338
+ publish:
339
+ environment: publish
340
+ ```
341
+
342
+ 5. Update the npm trusted publisher settings on `npmjs.com` to include the environment name.
343
+
344
+ > Optionally configure the `publish` environment to require **manual approval** before the job
345
+ > proceeds — providing a human gate even if malicious code reaches a release branch.
346
+
347
+ ### 6.2 · Consider hardware security keys _(optional)_
348
+
349
+ Physical security keys (YubiKey, etc.) are significantly more resistant to phishing and credential
350
+ theft than authenticator apps or SMS. They are worth recommending but should never be enforced —
351
+ not everyone has access to one, and a good authenticator app is a perfectly reasonable alternative.
352
+ Mention this as a suggestion, not a requirement.
353
+
354
+ ### 6.3 · Protect all long-lived branches and tags
355
+
356
+ Apply branch protection rules to every long-lived branch. Protect release tags separately with a
357
+ tag ruleset; branch protection does not apply to tags.
358
+
359
+ ### 6.4 · Enable immutable releases
360
+
361
+ Enable [GitHub immutable releases](https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/immutable-releases)
362
+ to prevent modification or deletion of tags and releases after creation.
363
+
364
+ ---
365
+
366
+ ## 7 · Sole Maintainer Considerations
367
+
368
+ There is an [open feature request](https://github.com/orgs/community/discussions/174507) to
369
+ support 2FA for GitHub environment approvals. Until that lands, trusted publishing carries a risk
370
+ for solo maintainers: a leaked GitHub token could be used to publish through the trusted workflow
371
+ without a 2FA challenge.
372
+
373
+ **Recommendation for sole maintainers**: consider publishing locally with `npm publish` and
374
+ 2FA-protected npm access until environment-level 2FA approval is supported. Continue to follow
375
+ all other security recommendations in this document regardless.
376
+
377
+ ---
378
+
379
+ ## Audit Output Format
380
+
381
+ For audits, lead with findings:
382
+
383
+ - **P0/P1/P2/P3** severity.
384
+ - File and line when repo-local.
385
+ - Risk.
386
+ - Recommended fix.
387
+ - Whether the agent can implement it now or the user must configure it externally.
388
+
389
+ Then include:
390
+
391
+ - Validation run.
392
+ - Remaining user-only checklist.
393
+ - Suggested next change.
394
+
395
+ ---
396
+
397
+ ## Quick Reference Checklist
398
+
399
+ Use this when setting up a new package or auditing an existing one.
400
+
401
+ ### Account security
402
+
403
+ - [ ] npm 2FA enabled (security key preferred)
404
+ - [ ] GitHub 2FA enabled (security key preferred)
405
+ - [ ] Password manager in use
406
+
407
+ ### Repository settings
408
+
409
+ - [ ] Actions pinned to full-length commit SHAs (enforced in settings)
410
+ - [ ] First-time contributor approval required
411
+ - [ ] Default workflow permissions set to read-only
412
+ - [ ] `main` branch protected (PR + review required)
413
+ - [ ] Release tags protected by a tag ruleset
414
+ - [ ] No publish-capable npm tokens in repository secrets
415
+
416
+ ### Trusted publishing
417
+
418
+ - [ ] OIDC trusted publisher configured on npmjs.com
419
+ - [ ] "Require 2FA, disallow tokens" enabled on npm
420
+ - [ ] Publish step uses npm CLI ≥ 11.5.1 and a Node.js version supported by official npm docs
421
+ - [ ] GitHub environment (`publish`) configured with branch restrictions
422
+
423
+ ### Workflow hygiene
424
+
425
+ - [ ] Build and publish are separate jobs
426
+ - [ ] Workflows declare explicit least-privilege `GITHUB_TOKEN` permissions
427
+ - [ ] Every `actions/checkout` step sets `persist-credentials: false`
428
+ - [ ] `npm ci --ignore-scripts` used in all install steps
429
+ - [ ] `ignore-scripts=true` in `.npmrc`
430
+ - [ ] `zizmor` passes on all workflow files
431
+
432
+ ### Package quality
433
+
434
+ - [ ] `npx publint@0.3.21` passes with no errors
435
+ - [ ] Dependabot or Renovate configured
436
+ - [ ] `actions-up` run to migrate to SHA-pinned actions
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "npm Publishing Security"
3
+ short_description: "Audit secure npm package publishing workflows."
4
+ default_prompt: "Use $npm-package-publishing to audit this package publishing setup."