Skip to content

fix(0.6.2): entrypoint guard rejected the npm-installed ralphctl shim#102

Merged
lukas-grigis merged 1 commit into
mainfrom
fix/0.6.2-entrypoint-symlink
May 4, 2026
Merged

fix(0.6.2): entrypoint guard rejected the npm-installed ralphctl shim#102
lukas-grigis merged 1 commit into
mainfrom
fix/0.6.2-entrypoint-symlink

Conversation

@lukas-grigis
Copy link
Copy Markdown
Owner

Summary

Hotfix on top of 0.6.1. npm i -g ralphctl && ralphctl --version was silently exiting 0 with no output β€” the basename gate in shouldAutoInvoke() rejected the npm-bin symlink (<prefix>/bin/ralphctl β†’ dist/cli.mjs) because basename was ralphctl, not cli.mjs.

Root cause

src/application/cli/entrypoint.ts#shouldAutoInvoke checked basename(process.argv[1]) against the regex ^(entrypoint|cli)\.(ts|mjs|js)$. When invoked via the npm-bin symlink, argv[1] is the shim path. Basename ralphctl failed the regex β†’ main() never ran.

Fix

Replace the basename allowlist with the canonical "am I the entry module?" pattern: resolve argv[1] through realpathSync and compare pathToFileURL(...).href to import.meta.url. Dev (tsx src/.../entrypoint.ts) and direct (node dist/cli.mjs) both still pass; symlinked bins resolve through and pass too.

Regression test

New e2e in cli.e2e.test.ts creates a renamed symlink to dist/cli.mjs (tmpRoot/ralphctl-bin-shim) and spawns it via node. Asserts the version lands on stdout. Prevents the silent-skip shape from regressing.

Test plan

  • pnpm typecheck Β· pnpm lint Β· pnpm test β€” green locally (231 files / 2218 tests)
  • Manual: rebuilt dist/cli.mjs, symlinked /tmp/ralphctl-shim β†’ dist/cli.mjs, ran node /tmp/ralphctl-shim --version β†’ printed 0.6.1 (the version of the local working tree at build time)
  • CI green

shouldAutoInvoke gated on basename(process.argv[1]) matching cli.mjs /
entrypoint.ts. Under npm's global install, the bin entry symlinks
<prefix>/bin/ralphctl β†’ dist/cli.mjs β€” basename ralphctl, gate fails,
main() never runs. 0.6.0 / 0.6.1 silently no-op'd on a fresh
npm i -g ralphctl && ralphctl --version (exit 0, empty stdout).

Replace the basename allowlist with the canonical entry-module check:
realpathSync(argv[1]) β†’ pathToFileURL β†’ compare to import.meta.url.
Dev (tsx entrypoint.ts) and direct invocation (node dist/cli.mjs) both
pass; symlinked bins resolve through and pass.

Add a regression e2e that creates a renamed symlink to dist/cli.mjs and
spawns it β€” asserts a semver lands on stdout. Prevents the silent-skip
shape from regressing.

Bump to 0.6.2.
@lukas-grigis lukas-grigis merged commit 18306b5 into main May 4, 2026
1 check passed
@lukas-grigis lukas-grigis deleted the fix/0.6.2-entrypoint-symlink branch May 4, 2026 20:44
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