publish-npm-package

Use skill if you are automating npm publishing via GitHub Actions and need auth, versioning, provenance, or workflow-template choices.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "publish-npm-package" with this command: npx skills add yigitkonur/skills-by-yigitkonur/yigitkonur-skills-by-yigitkonur-publish-npm-package

npm Publish CI/CD

Set up npmjs publishing via GitHub Actions with a complete, internally consistent release flow: auth, version management, workflow trigger, provenance, and recovery.

Trigger

Use this skill when the task is about:

  • publishing to npmjs.org from GitHub Actions
  • replacing manual npm login / npm publish with CI/CD
  • choosing OIDC trusted publishing vs NPM_TOKEN
  • choosing semantic-release vs changesets vs release-please vs manual trigger
  • adding provenance, least-privilege permissions, or supply-chain hardening
  • wiring single-package or monorepo npm releases
  • debugging a failing npm publish workflow

This skill assumes npmjs.org. For deep package.json / exports / files shaping, route to references/packaging/package-config.md instead of expanding that detail here.

Always classify the repo first

Before writing YAML, answer these questions and record the answers — they drive the auth (Step 1) and versioning (Step 2) decisions below:

  1. Is the package public or private?
  2. Is publishing on GitHub Actions with a GitHub-hosted runner?
  3. Is this a single package or a monorepo/workspaces repo?
  4. Does the team already use conventional commits (the majority of meaningful commits follow type: description format)?
  5. Should every releasable merge publish automatically, or should there be a human-reviewed Release/Version PR?
  6. Is this greenfield (never published), or does the package already exist with tags/versions/releases?
  7. Is the target really npmjs.org?

⚠️ Steering (F-01): Record answers explicitly (e.g., as inline comments or a checklist). Without a recorded classification, the auth and versioning decisions become guesswork. The table below maps answers directly to choices.

Quick-reference decision matrix

Q1: Public?Q2: GH Actions + hosted runner?Q3: Single/Mono?Q4: Conv. commits?Q5: Auto/Human gate?→ Auth→ Versioning
YesYesSingleYesAutoOIDCsemantic-release
YesYesSingleYesHuman gateOIDCrelease-please
YesYesSingleNoHuman gateOIDCchangesets
YesYesSingleNo (greenfield, will adopt)Human gateOIDCrelease-please
YesYesMonorepoAnyAnyOIDCchangesets (default)
NoAnyAnyAnyAnyToken(any versioning)
AnyNo (self-hosted/GHES)AnyAnyAnyToken(any versioning)

Always follow this order

  1. Choose authentication.
  2. Choose the versioning model.
  3. Route to the exact workflow template.
  4. Check package/repo prerequisites.
  5. Implement the smallest complete configuration set.
  6. Verify with dry runs and publish checks.
  7. Add recovery notes if the workflow is already failing or the repo is mid-migration.

1) Choose authentication first

SituationChooseWhyRead next
Public package + GitHub Actions + GitHub-hosted runnerOIDC trusted publishingBest default: zero secret sprawl, repo-bound trust, provenance-friendlyreferences/auth/oidc-trusted-publishing.md
Private package, self-hosted runner, GHES, non-GitHub CI, or OIDC unsupportedGranular access tokenCorrect fallback when OIDC is unavailablereferences/auth/granular-tokens.md
Existing classic automation token setupMigrate to granular token unless blockedSmaller blast radius, expiry, better hygienereferences/auth/granular-tokens.md

Auth rules

  • Default to OIDC whenever it is supported.
  • OIDC requires GitHub-hosted runners, npm CLI >= 9.5.0, exact package.json.repository.url, id-token: write, contents: read, and public package visibility unless npm Enterprise support exists.
  • If the publish runner is self-hosted, do not choose OIDC and hope it works; use a granular token.
  • Token-based publishing should use secrets.NPM_TOKEN exposed as NODE_AUTH_TOKEN in the publish step. Do not use npm login in CI.
  • Use one granular token per repo or workflow surface, and rotate it on a regular schedule instead of treating it as permanent infrastructure.
  • Do not keep both OIDC and token auth just because an old workflow used both. The only valid mixed setup is when token auth is still required but you also grant id-token: write for provenance.
  • Avoid classic automation tokens for new work.

⚠️ Steering (F-07): OIDC auth means zero npm secrets. If your workflow has NODE_AUTH_TOKEN or NPM_TOKEN in the publish step, you are using token-based auth, not OIDC — even if id-token: write is set. Pure OIDC relies on GitHub's identity federation with npm; no token is exchanged via environment variables.

2) Choose the versioning model, not just a tool

NeedChooseChoose whenAvoid whenRead next
Publish automatically on every releasable mergesemantic-releaseSingle package, strong conventional-commit discipline, no human release gateMonorepos, weak commit discipline, teams that want a reviewable release PRreferences/versioning/semantic-release.md
Generate a reviewable Release PR, publish after mergerelease-pleaseConventional commits already exist, team wants a human merge gate, explicit release PR is desirableCommit messages are not trustworthy, or the goal is zero human release handlingreferences/versioning/release-please.md
Put version intent in each PR and batch releases deliberatelychangesetsMonorepos, single-package repos without strict conventional commits, explicit version notes, teams that want a human-reviewed Version PR without adopting conventional commitsThe repo wants publish-on-merge with no human-authored version datareferences/versioning/changesets.md
Rare/simple releasesmanual trigger + npm versionAutomation would be heavier than the release frequencyThe task explicitly asks for full release automationmatching workflow "Manual Trigger" section

⚠️ Steering (F-04): changesets is not monorepo-only. It works perfectly for single-package repos and does not require conventional commits. If a single-package repo wants a human-reviewed Version PR without adopting conventional commits, changesets is the right choice.

Quick-decision flowchart

Is this a monorepo?
  YES → changesets (default) — or release-please if conv. commits already drive the repo
  NO (single package) →
       Does the team use conventional commits?
         YES →
              Want fully automatic publish? → semantic-release
              Want a human-reviewed Release PR? → release-please
         NO →
              Greenfield and willing to adopt? → release-please (commit to the format)
              Existing repo, won't adopt? → changesets
              Rare releases? → manual trigger

Versioning rules

  • Monorepo default: changesets. Use release-please only when conventional commits already drive the repo. Treat semantic-release in monorepos as a last resort, not the default.
  • Single-package repo without conventional commits that wants a human release gate: changesets is a viable choice — it is not monorepo-only. Alternatively, adopt conventional commits and choose release-please.
  • If conventional commits are weak or absent in an existing repo, do not silently pick semantic-release or release-please. For greenfield repos, adopting conventional commits is a valid starting choice — pick release-please if the team commits to the format going forward.
  • For existing published packages, bootstrap before the first automated run:
    • semantic-release: initial tag / baseline release state
    • release-please: config + manifest aligned to the current published version
    • changesets: initialized config and contributor workflow for changeset files

⚠️ Steering (F-05): Distinguish greenfield (team can choose to adopt conventional commits going forward) from existing repo (commit history already exists without them). For greenfield, picking release-please + committing to conventional commits is valid. For existing repos with inconsistent history, changesets avoids the commit-discipline prerequisite.

3) Route to the exact workflow template

Do not hand-assemble publish YAML from memory if a matching reference already exists.

⚠️ Steering (F-13): Use the workflow template's configuration files as the starting point. If the versioning reference (e.g., release-please.md) shows different or additional config options, treat them as customization, not the baseline. The workflow template is the source of truth for the config file set.

AuthVersioningWorkflow template
OIDCsemantic-releasereferences/workflows/oidc-workflows.md1. OIDC + semantic-release
OIDCchangesetsreferences/workflows/oidc-workflows.md2. OIDC + changesets
OIDCrelease-pleasereferences/workflows/oidc-workflows.md3. OIDC + release-please
OIDCmanual triggerreferences/workflows/oidc-workflows.md4. OIDC + Manual Trigger
Tokensemantic-releasereferences/workflows/token-workflows.md1. Token + semantic-release
Tokenchangesetsreferences/workflows/token-workflows.md2. Token + changesets
Tokenrelease-pleasereferences/workflows/token-workflows.md3. Token + release-please
Tokenmanual triggerreferences/workflows/token-workflows.md4. Token + Manual Trigger

Monorepo routing

  • Read references/monorepo/publishing-patterns.md before choosing a monorepo flow.
  • Prefer the changesets template for most workspaces repos.
  • Use the release-please template when the repo already relies on conventional commits and wants a Release PR gate.
  • Do not default to semantic-release for monorepos unless the repo is already invested in that ecosystem and the limitation is understood.

4) Pre-implementation prerequisites

Before editing or validating the workflow, confirm:

Package/repo prerequisites

  • The code is pushed to a GitHub repository (the workflow runs on GitHub Actions).
  • package.json.repository.url exactly matches the GitHub repo URL, including casing.
  • Scoped public packages set publishConfig.access: "public" or an equivalent publish flag.
  • Prefer publishConfig.provenance: true so provenance is not a human-memory step.
  • The repo's lockfile is committed and CI uses deterministic installs (npm ci or the repo's equivalent).
  • npm pack --dry-run shows the intended tarball contents.
  • Packaging details such as files, exports, types, and dual ESM/CJS output are correct. Use references/packaging/package-config.md for those details.

CI prerequisites

  • actions/setup-node uses registry-url: https://registry.npmjs.org
  • publish jobs run build/test before publish
  • explicit concurrency is present to avoid publish races
  • permissions are least-privilege and set deliberately
  • For production hardening, consider pinning GitHub Actions to full SHAs with a tag comment (e.g., actions/checkout@<sha> # v4). The reference templates use tags for readability — pin to SHAs before shipping to production. See references/security/supply-chain.md for SHA pinning guidance.

⚠️ Steering (F-17): The workflow templates use @v4 style tags for readability. For production, pin to full commit SHAs with a tag comment: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4. This prevents supply-chain attacks via mutable tags.

First-publish / OIDC bootstrap (critical for greenfield)

⚠️ Steering (F-11): OIDC requires the package to already exist on npm. For a brand-new package, you hit a chicken-and-egg problem: you can't link a non-existent package to your GitHub repo.

Bootstrap steps for first publish with OIDC:

  1. Create a granular access token on npmjs.com (see references/auth/granular-tokens.md).
  2. Add it as NPM_TOKEN in your repo's GitHub Actions secrets.
  3. Publish once manually: npm publish --access public (or use the token-based workflow template for the first release).
  4. Go to https://www.npmjs.com/package/<your-package>/accessPublishing accessAdd GitHub Actions to link the package to your repo.
  5. Remove the NPM_TOKEN secret and switch the workflow to pure OIDC.

Migration prerequisites

  • For semantic-release, ensure full git history (fetch-depth: 0) and baseline tags exist.
  • For release-please, ensure both config and manifest files exist with the manifest version matching package.json. For never-published packages, use the package.json version (typically 1.0.0 or 0.1.0).
  • For changesets, ensure contributors know when to add a changeset and how empty changesets are handled.

⚠️ Steering (F-10/F-18): "Current version" in the release-please manifest means the version in package.json. For greenfield packages that have never been published, set the manifest to match package.json exactly. Do not guess "0.0.0" unless package.json says "0.0.0".

Guardrails: avoid half-configured release automation

  • Prefer OIDC over granular tokens, and granular tokens over classic automation tokens.
  • Prefer changesets over semantic-release for monorepos unless there is a strong existing reason not to.
  • Prefer release-please over semantic-release when the team wants a human-reviewed Release PR.
  • Prefer workflow templates from the references over inventing a new workflow from memory.
  • Do not choose semantic-release without real conventional-commit discipline, fetch-depth: 0, and a current npm plugin/toolchain that supports the auth mode you selected.
  • Do not create a release-please workflow without the two-job pattern: one job creates/updates the Release PR, and a second publish job runs only when release_created == 'true'.
  • Do not create a changesets flow without a contributor rule for adding changesets.
  • Do not assume OIDC works on self-hosted runners.
  • Do not commit tokenized .npmrc files or hardcode _authToken values. A placeholder-based .npmrc is fine; real tokens are not.
  • Do not forget scoped public package access settings; E403 on a scoped package often means public access was never configured.
  • Do not rely on a human remembering --provenance; prefer package or tool config that bakes it in.
  • Do not use pull_request_target for untrusted code to reach release secrets or publish permissions.
  • If the repo is mid-migration, finish the auth + versioning + workflow + bootstrap state together. Half-migrated release automation is worse than a temporary manual process.

Verification before calling the setup done

Always run

  • npm pack --dry-run — verify tarball contents
  • build/test in the same workflow that publishes
  • a check that the intended versioning tool is actually wired, not just installed

Auth-specific checks

CheckOIDCToken
Runner typeGitHub-hostedAny
Permissionscontents: read, id-token: writeN/A
Registry configactions/setup-noderegistry-url: https://registry.npmjs.orgSame
Secret wiringNone needed (no NODE_AUTH_TOKEN)NPM_TOKEN in secrets, NODE_AUTH_TOKEN in publish step
Post-publish verifynpm audit signaturesnpm whoami (diagnostic only)

Tool-specific checks

ToolDry-run commandKey filesCritical check
semantic-releasenpx semantic-release --dry-run.releaserc or release.config.jsFull git history, baseline tags, plugin order
changesetsnpx changeset status.changeset/config.jsonRelease/version PR path matches template
release-pleaserelease-please release-pr --repo-url=<owner/repo> --token=TOKEN --dry-run (optional, requires CLI install).release-please-config.json, .release-please-manifest.jsonManifest version matches package.json, publish gated on release_created == 'true'

⚠️ Steering (F-15): release-please has no built-in dry-run equivalent to npx semantic-release --dry-run. Verify by confirming config + manifest files exist and the manifest version matches package.json. The CLI dry-run above is optional and requires npm i -g release-please.

Recovery routing

If the task is about an existing failure, jump straight to the narrowest reference instead of rereading everything.

ProblemRead firstImmediate focus
OIDC / provenance failurereferences/troubleshooting/common-issues.md, then references/auth/oidc-trusted-publishing.mdmissing id-token: write, missing contents: read, wrong repo URL, self-hosted runner, missing npmjs registry config
Token auth failurereferences/troubleshooting/common-issues.md, then references/auth/granular-tokens.mdexpired token, wrong scopes, wrong secret wiring, rotation mistakes
semantic-release failurereferences/troubleshooting/common-issues.md, then references/versioning/semantic-release.mdshallow clone, missing baseline tag, commit messages not releasable, old plugin versions
changesets failurereferences/troubleshooting/common-issues.md, then references/versioning/changesets.mdforgotten changesets, release PR drift, access/public config, prerelease state
release-please failurereferences/troubleshooting/common-issues.md, then references/versioning/release-please.mdmanifest drift, missing releasable commits, duplicate/stuck Release PRs, broken publish gating
Published wrong version / broken packagereferences/troubleshooting/common-issues.mdunpublish within 72 hours if allowed, otherwise deprecate and patch forward
Token leak / security incidentreferences/security/supply-chain.mdrevoke, rotate, audit publishes, harden workflow before re-enabling release

Smallest reading set by scenario

Public single-package repo, fully automatic

  • references/auth/oidc-trusted-publishing.md
  • references/versioning/semantic-release.md
  • references/workflows/oidc-workflows.md1. OIDC + semantic-release
  • references/security/supply-chain.md

Public single-package repo, reviewable release gate

  • references/auth/oidc-trusted-publishing.md
  • references/versioning/release-please.md or references/versioning/changesets.md
  • references/workflows/oidc-workflows.md3. OIDC + release-please or 2. OIDC + changesets
  • references/security/supply-chain.md

Monorepo / workspaces

  • references/monorepo/publishing-patterns.md
  • references/versioning/changesets.md (default) or references/versioning/release-please.md
  • matching OIDC/token workflow section
  • references/security/supply-chain.md

Private package, self-hosted runner, or non-GitHub CI

  • references/auth/granular-tokens.md
  • chosen versioning reference
  • matching section in references/workflows/token-workflows.md

Failing existing workflow

  • references/troubleshooting/common-issues.md
  • auth reference for the current auth mode
  • versioning reference for the current tool

Steering experiences — quick reference

These are the highest-impact traps found during derailment testing. Each is documented in detail at the relevant decision point above and in the reference files.

TrapImpactWhat to do
Using NODE_AUTH_TOKEN/NPM_TOKEN and calling it "OIDC"P0 — silent wrong authOIDC means zero npm secrets. If the publish step has NODE_AUTH_TOKEN, it is token auth.
First-publish with OIDC on a package that does not exist on npm yetP0 — 404 errorBootstrap: publish once with a granular token, then switch to OIDC.
Picking semantic-release/release-please without conventional commitsP0 compound — blockedUse changesets if the team will not adopt conventional commits.
Assuming changesets is monorepo-onlyP1 — wrong tool choicechangesets works for single-package repos and does not require conventional commits.
Confusing greenfield "will adopt" with existing "does not have"P1 — wrong guidanceGreenfield can adopt conventional commits (design choice). Existing repos without them need changesets.
Copying config from versioning reference instead of workflow templateP1 — config mismatchWorkflow template is baseline; versioning reference shows customization options.
Using @v4 action tags in productionP1 — supply-chain riskPin to full SHAs with tag comments for production workflows.
Not verifying manifest version matches package.jsonP1 — release-please misfireManifest must match package.json. For greenfield, use the package.json version exactly.

Final reminder

Keep SKILL.md focused on decisions, sequencing, and guardrails. Read only:

  1. the auth reference,
  2. the versioning reference,
  3. the exact workflow-template section,
  4. and security/troubleshooting only if needed.

Do not expand into full YAML or packaging deep dives here when the references already cover them.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

develop-typescript

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

init-devin-review

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

test-mcp-by-cli

No summary provided by upstream source.

Repository SourceNeeds Review
publish-npm-package | V50.AI