Skip to content

fix: remove dependency on postinstall script#260

Closed
KrishnaSSH wants to merge 1 commit into
zeropsio:mainfrom
KrishnaSSH:main
Closed

fix: remove dependency on postinstall script#260
KrishnaSSH wants to merge 1 commit into
zeropsio:mainfrom
KrishnaSSH:main

Conversation

@KrishnaSSH
Copy link
Copy Markdown

What Type of Change is this?

  • New Feature
  • Fix
  • Improvement
  • Release
  • Other

Description (required)

fixes bun compatibility bun blocks postinstall scripts by default,
so the binary was never downloaded. this pr removes the dependency on postinstall script

Copy link
Copy Markdown
Collaborator

@l-hellmann l-hellmann left a comment

Choose a reason for hiding this comment

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

A couple of items that can't be one-click suggestions:

  • utils/install.js is now dead code — it was only the postinstall entry point that this PR removes, but it's still shipped via files: ["/utils"]. Safe to delete.
  • Trailing newlinespackage.json, utils/binary.js, and utils/run.js now end without a trailing newline. Worth re-adding (the package ships standard).

Comment thread tools/npm/utils/binary.js
Comment on lines +40 to +44
const getBinaryPath = () => {
const { join } = require("path");
const platform_arch = getPlatform();
return join(__dirname, "bin", "bin", `zcli-${platform_arch}`);
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This re-derives the installDir/bin/bin/name layout that binary-source.js already owns, so the two can drift. Suggest delegating to a small public getter on the Binary class:

Suggested change
const getBinaryPath = () => {
const { join } = require("path");
const platform_arch = getPlatform();
return join(__dirname, "bin", "bin", `zcli-${platform_arch}`);
};
const getBinaryPath = () => getBinary().getPath();

This needs a matching getPath() on Binary in binary-source.js. It can't reuse the existing _getBinaryPath(), which calls process.exit(1) when the binary isn't installed — the very case we're checking here:

getPath() {
  return join(this.installDirectory, "bin", this.name);
}

Comment thread tools/npm/utils/run.js
Comment on lines +8 to +13
install()
.then(() => run())
.catch((err) => {
process.stderr.write(`download failed: ${err.message}\n`);
process.exit(1);
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

binary-source.install() already handles download failures internally (error()process.exit(1)), so this .catch is unreachable. Simplify to:

Suggested change
install()
.then(() => run())
.catch((err) => {
process.stderr.write(`download failed: ${err.message}\n`);
process.exit(1);
});
install().then(() => run());

@l-hellmann
Copy link
Copy Markdown
Collaborator

Thanks again for tackling the Bun issue here @KrishnaSSH 🙏

After reviewing, I prototyped an alternative in #261 using the optionalDependencies + os/cpu approach — the one from the Sentry "Publishing binaries on npm" post (also what esbuild/swc/biome do). It delivers the binary at install time as a normal optional dependency, which Bun honors via os/cpu, fixing the same Bun problem without the runtime-download tradeoffs.

That said, the runtime-download approach here is a valid smaller step. If we'd rather go this route, here's what it would still need to avoid regressing existing users (beyond the inline suggestions above):

Required to not regress:

  1. Download into a permanent, user-writable per-user dir — not the package dir. As-is the binary lands in utils/bin inside the package, which on a global npm i -g is root-owned, so a non-root user running zcli hits EACCES. Use an OS data dir (~/.local/share, ~/Library/Application Support, %LOCALAPPDATA%). It must be a dedicated zcli dir — not the CLI's existing zerops config dir, since uninstall() does rimraf(installDirectory) and would otherwise delete the user's saved login token.
  2. Make the existence check version-aware. Once the binary lives in a persistent dir it survives upgrades, so a version-blind existsSync check would never re-download — users would keep running the old binary. Stamp the version on install and compare it on run (handles upgrades and downgrades).
  3. Await the extraction stream. res.data.pipe(tar.x(...)) isn't awaited, so install().then(run) can spawn a half-written binary. Wrap the pipe in a promise that resolves on the stream's close event.

Plus the smaller items (inline above):

  • delegate getBinaryPath to a single shared getter instead of re-deriving the path
  • drop the unreachable .catch in run.js
  • remove the now-dead install.js
  • re-add the missing trailing newlines

One caveat either way: moving the binary out of node_modules means uninstall cleanup relies on the preuninstall hook — which Bun also skips — so Bun users would leave a small orphaned data dir. Acceptable (a leftover file), but worth noting.

I prototyped all three "required" changes locally and they check out — happy to push them onto this branch if we pick this direction. Either way, would love your thoughts on which way to go.

@KrishnaSSH
Copy link
Copy Markdown
Author

makes sense, thanks for the thorough
review.

@KrishnaSSH KrishnaSSH closed this May 25, 2026
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.

2 participants