GitHub Actions: npm publish and "git@github.com: Permission denied (publickey)"
Nothing is wrong with SSH or with your npm token. npm read the path you gave it as a GitHub repository. An argument that does not start with ./, ../, /, ~/ or file: is tried as a GitHub shorthand before it is tried as a file (npa.js in npm-package-arg), and dist/pkg-1.0.0.tgz fits: owner dist, repository pkg-1.0.0.tgz. The .tgz at the end does not change that. npm then asks git for that repository over SSH, and the runner has no SSH key for github.com. Nothing reached the registry.
What it looks like
npm error command git --no-replace-objects ls-remote ssh://git@github.com/release-tarballs/verax-ai-inventory-0.8.0.tgz.git npm error git@github.com: Permission denied (publickey).
The repository in the ls-remote line is your path with .git added. That is how to recognize it.
The fix: start the path with ./
- run: npm publish ./release-tarballs/verax-ai-inventory-0.8.0.tgz --provenance --access public
The same for a folder: carlesandres/house publishes ./artifacts/npm-main, not artifacts/npm-main (#351). A path with two or more slashes is read as a folder today, but npm's documentation for package specs says a folder should always start with / or ./, and calls the other reading legacy behavior that may be removed. Add ./ there too.
With a glob, a pattern that matches nothing stays as the text you wrote. dsh-plugin-toolkit checks that there is exactly one tarball before it publishes (#10):
- name: Publish
run: |
set -- ./dist/*.tgz
if [ "$#" -ne 1 ] || [ ! -f "$1" ]; then
echo "expected one tarball in dist/, found: $*"
exit 1
fi
npm publish "$1" --provenance --access public
Why it shows up on a release day
The line usually fails the first time it runs:
- The publish moved to tarballs packed in an earlier job. A build job packs and uploads an artifact, and the publish job downloads it to
dist/orrelease-tarballs/, one slash deep. verax's change to publish the packed tarballs had not run until the 0.8.0 release (#252). - A path got shorter. carlesandres/house moved its packages to
artifacts/npm-binary-*; before that the paths had more than one slash (#351). - The step never ran before. qso's 1.4.0 had been published by hand, so the job skipped the step, and 1.5.0 was the first run (#37).
Check it before a release
--dry-run parses the argument the same way and sends nothing. dsh-plugin-toolkit and verax both reproduced the failure this way, and verax saw npm name the spec github:release-tarballs/…:
npm publish dist/pkg-1.0.0.tgz --dry-run # fails: read as github:dist/pkg-1.0.0.tgz npm publish ./dist/pkg-1.0.0.tgz --dry-run # reads the tarball, sends nothing
carlesandres/house and verax also added a test that fails when the publish line in the workflow has no ./.
After the fix, do not just re-run
A re-run uses the same GITHUB_SHA and GITHUB_REF as the run it repeats (GitHub's documentation), so a release started by a tag runs the tag's workflow again, with the old line. The projects above did one of these:
- A new patch version and tag. qso released 1.5.1 with the same library code (#37).
- Move the tag to the fixed commit, when nothing of that version was published. dsh-plugin-toolkit did this for 0.13.12 (#10).
Before you publish again, check what is already on npm: npm view <name>@<version> version prints the version if it is there. npm refuses to publish over a version that exists, and a job that publishes several packages in a row may have published the ones before the failure.
Telling it apart
- "ENEEDAUTH" or "404 Not Found - PUT": npm reached the registry and had no valid login.
- The same
Permission denied (publickey)from a git step, or from a dependency written as agit+ssh://URL: the runner really has no SSH key for GitHub.actions/checkouttakes a deploy key in itsssh-keyinput. - "ENOENT" or "Could not read package.json" with the path in it: npm read it as a path, and nothing was there.
See failed jobs from IntelliJ IDEA
Pipeline Inbox for GitHub Actions Workflows, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, lists the runs of all your repositories in an IDE tool window, with their jobs and steps, and your current branch's runs are in the status bar. Reading is free; re-running failed jobs, cancelling, running workflows and reviewing deployments are Pro (US$1.50 a month, with a 30-day trial).
GitHub and GitHub Actions are trademarks of GitHub, Inc.; npm is a trademark of npm, Inc.; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with, sponsored or endorsed by GitHub, Inc., npm, Inc. or JetBrains.
More: npm publish and "ENEEDAUTH" · "Artifact download failed after 5 retries" · "Artifact not found for name" · GitHub Actions plugins for IntelliJ IDEA compared · 日本語