Skip to content

fix(npm): pass absolute tarball paths to npm publish - #4

Merged
ajay-bhargava merged 1 commit into
mainfrom
fix/npm-bootstrap-path
Jul 27, 2026
Merged

fix(npm): pass absolute tarball paths to npm publish#4
ajay-bhargava merged 1 commit into
mainfrom
fix/npm-bootstrap-path

Conversation

@ajay-bhargava

@ajay-bhargava ajay-bhargava commented Jul 27, 2026

Copy link
Copy Markdown
Member

Found during a real bootstrap attempt.

Symptom

==> npm publish dist-npm/planmonster-olk-darwin-arm64-1.10.0-pm.1.tgz --access public
npm error code 128
npm error command git --no-replace-objects ls-remote ssh://git@github.com/dist-npm/planmonster-olk-darwin-arm64-1.10.0-pm.1.tgz.git
npm error git@github.com: Permission denied (publickey).

Cause

bootstrap-npm.sh passed tarball paths exactly as globbed from the directory argument, so a relative argument produced npm publish dist-npm/pkg.tgz. npm parses a bare relative path as the GitHub shorthand <owner>/<repo> and tries to clone it. Nothing to do with the user's keys or npm auth.

Reproduced and verified locally:

Invocation Result
npm publish dist-npm/pkg.tgz --dry-run code 128, tries to clone github.com/dist-npm/...
npm publish ./dist-npm/pkg.tgz --dry-run parses correctly: name: @planmonster/olk-darwin-arm64

The existing --dry-run did not catch this because it only echoes the command.

Fix

Resolve the directory argument to an absolute path once, before any tarball argument is built.

Regression test

New scripts/test-bootstrap-npm.sh, wired into the package job. It packs all seven wrapper tarballs (no Go binaries needed — the placeholder layout packs fine), runs the bootstrap script in dry-run mode against a relative directory, and asserts every publish argument is absolute.

Proof it works:

against the unfixed script:  NOT ABSOLUTE: dist-npm/... (EXIT=1)
against the fixed script:    bootstrap script test: PASS

It also locks in the validation added in 89ac14f: publish order (6 platform packages, launcher last), an extra tarball, a missing platform package, an empty directory, and --tag swallowing a following flag.

Also

Corrects version examples in docs/npm-publishing.md from 0.9.5-pm.N to 1.10.0-pm.N. git describe puts this fork at v1.10.0-16-g764c720, so 1.10.0 is the upstream base; the 0.9.5 in server.json is stale and CI stamps it from the tag regardless.


View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.

The bootstrap script passed the tarball paths exactly as globbed from the
directory argument, so `bash scripts/bootstrap-npm.sh dist-npm` produced
`npm publish dist-npm/planmonster-olk-darwin-arm64-1.10.0-pm.1.tgz`. npm parses
a bare relative path as the GitHub shorthand <owner>/<repo> and tries to clone
ssh://git@github.com/dist-npm/planmonster-olk-darwin-arm64-1.10.0-pm.1.tgz.git,
which fails:

  npm error code 128
  npm error An unknown git error occurred
  npm error git@github.com: Permission denied (publickey).

Reported against a real bootstrap attempt. The dry run did not catch it,
because it only echoes the command.

Resolve the directory argument to an absolute path once, before building any
tarball argument. An absolute path (like a "./" prefix) is unambiguously a file.

Adds scripts/test-bootstrap-npm.sh, wired into the ci.yml package job. It packs
the seven wrapper tarballs (no Go binaries needed), runs the bootstrap script
in dry-run mode against a *relative* directory, and asserts that every publish
argument is absolute. Verified that the test fails against the unfixed script
and passes against the fixed one. It also covers the publish order and the
input validation from 89ac14f: an extra tarball, a missing platform package, an
empty directory, and `--tag` swallowing a following flag.

Corrects the version examples in docs/npm-publishing.md from 0.9.5-pm.N to
1.10.0-pm.N: git describe puts this fork at v1.10.0-16, so 1.10.0 is the
upstream base. The 0.9.5 in server.json is stale and is stamped by CI anyway.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The npm bootstrap script now resolves relative tarball directories to absolute paths. A new regression test validates packaging, publish ordering, malformed inputs, and option handling. CI runs the test, while npm publishing documentation and the packaged artifact are updated for version 1.10.0-pm.1.

Changes

NPM bootstrap validation and release flow

Layer / File(s) Summary
Bootstrap path handling and regression coverage
scripts/bootstrap-npm.sh, scripts/test-bootstrap-npm.sh
Tarball paths are normalized to absolute paths, and the new test covers packaging, publish arguments and order, invalid tarball sets, and option validation.
Packaging workflow integration
.github/workflows/ci.yml
The package job runs the bootstrap regression test before verifying the working tree is unchanged.
Release version examples and packaged output
docs/npm-publishing.md, dist-npm/...
Publishing examples use version 1.10.0-pm.1, and the matching npm archive is replaced with updated binary contents.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@ajay-bhargava
ajay-bhargava merged commit e21f21f into main Jul 27, 2026
4 checks passed
@ajay-bhargava
ajay-bhargava deleted the fix/npm-bootstrap-path branch July 27, 2026 20:31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e1e25881-821c-4ab0-b90a-f388e3604404

📥 Commits

Reviewing files that changed from the base of the PR and between 764c720 and e848d56.

📒 Files selected for processing (11)
  • .github/workflows/ci.yml
  • dist-npm/planmonster-olk-darwin-arm64-1.10.0-pm.1.tgz
  • dist-npm/planmonster-olk-darwin-x64-1.10.0-pm.1.tgz
  • dist-npm/planmonster-olk-linux-arm64-1.10.0-pm.1.tgz
  • dist-npm/planmonster-olk-linux-x64-1.10.0-pm.1.tgz
  • dist-npm/planmonster-olk-win32-arm64-1.10.0-pm.1.tgz
  • dist-npm/planmonster-olk-win32-x64-1.10.0-pm.1.tgz
  • dist-npm/planmonster-olkcli-1.10.0-pm.1.tgz
  • docs/npm-publishing.md
  • scripts/bootstrap-npm.sh
  • scripts/test-bootstrap-npm.sh

Comment on lines +25 to +29
cleanup() {
git checkout -- npm/olk/package.json npm/olk-*/package.json 2>/dev/null || true
rm -rf "$tmp"
}
trap cleanup EXIT

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not discard the caller’s working-tree changes.

Both cleanup paths use git checkout, which can erase a developer’s unstaged edits to the package manifests when this regression test is run locally. Snapshot the files and restore those exact bytes, or run the build in an isolated temporary worktree instead. This also prevents the CI working-tree check from masking manifest mutations.

Also applies to: 38-38

Comment on lines +69 to +82
echo "==> assert publish order: 6 platform packages, launcher last"
order="$(printf '%s\n' "$out" | grep -c 'DRY RUN: npm publish')"
[ "$order" -eq 7 ] || {
echo "expected 7 publish commands, saw $order" >&2
exit 1
}
last="$(printf '%s\n' "$out" | grep 'DRY RUN: npm publish' | tail -1)"
case "$last" in
*olkcli-*) ;;
*)
echo "launcher must be published last, saw: $last" >&2
exit 1
;;
esac

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the complete publish sequence, not just its length.

The current check only requires seven publish commands and a launcher-like final argument. It would still pass if a platform package were duplicated, omitted, or published in the wrong position. Capture the seven basenames and compare them with an explicit expected sequence containing each platform package exactly once and the launcher last.

Comment on lines +93 to +104
echo "==> assert input validation"
cp -r "$tmp/dist-npm" "$tmp/extra"
cp "$tmp/extra"/*olkcli-*.tgz "$tmp/extra/unrelated-9.9.9.tgz"
expect_failure "an extra/unexpected tarball" extra

cp -r "$tmp/dist-npm" "$tmp/missing"
rm -f "$tmp/missing"/*olk-win32-arm64*.tgz
expect_failure "a missing platform package" missing

cp -r "$tmp/dist-npm" "$tmp/empty-ish"
rm -f "$tmp/empty-ish"/*.tgz
expect_failure "no tarballs at all" empty-ish

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add an isolated mixed-version fixture.

The “extra/unexpected tarball” case adds an eighth file named 9.9.9; it does not test a valid seven-tarball set containing one package from a different version. Add that scenario separately so version-consistency validation cannot regress without this test failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant