Problem
The current distribution approach for hookdeck-cli relies exclusively on postinstall scripts via go-npm, which creates a poor developer experience with modern package managers, particularly pnpm v10+.
Impact on Users
When users install hookdeck-cli with pnpm v10+, they encounter this error:
$ pnpm install -D hookdeck-cli
# Install succeeds but shows warning about ignored build scripts
$ pnpm hookdeck
./node_modules/.bin/hookdeck: cannot execute binary file
Root Cause: pnpm v10+ blocks lifecycle scripts by default for security reasons. The package ships with a Linux x86-64 binary as a placeholder, and the postinstall script is supposed to download the correct platform-specific binary. When the script is blocked, users are left with the wrong binary.
Current Workarounds (Poor UX)
Users must manually configure pnpm before installation:
Option 1: Create pnpm-workspace.yaml before installing:
onlyBuiltDependencies:
- hookdeck-cli
Option 2: Add to package.json before installing:
{
"pnpm": {
"onlyBuiltDependencies": ["hookdeck-cli"]
}
}
Option 3: Post-install approval (multi-step):
pnpm install -D hookdeck-cli # Fails
pnpm approve-builds # Interactive selection required
pnpm rebuild hookdeck-cli # Finally works
None of these are acceptable for a good CLI installation experience.
Reproduction Steps
- Create new project with pnpm v10+
- Run
pnpm install -D hookdeck-cli
- Try to run
pnpm hookdeck --version
- Result:
cannot execute binary file error
Environment:
- pnpm v10.19.0
- macOS (darwin arm64)
- hookdeck-cli v1.1.0
Recommended Solution
Switch to the optionalDependencies pattern used by industry-standard tools like esbuild, swc, and Sentry CLI. This approach:
- ✅ Works with pnpm v10+ without configuration
- ✅ No postinstall scripts required
- ✅ Works with
--ignore-scripts flag
- ✅ Faster installs (only downloads ~1-2MB for one platform)
- ✅ Better offline support (binaries in npm registry)
- ✅ Can still use postinstall as optional fallback
Implementation Pattern
1. Create platform-specific packages:
Publish separate packages for each platform:
@hookdeck/cli-darwin-arm64
@hookdeck/cli-darwin-x64
@hookdeck/cli-linux-x64
@hookdeck/cli-linux-arm64
@hookdeck/cli-win32-x64
etc.
Each platform package contains:
{
"name": "@hookdeck/cli-darwin-arm64",
"version": "1.1.0",
"os": ["darwin"],
"cpu": ["arm64"],
"bin": {
"hookdeck": "./hookdeck"
},
"files": ["hookdeck"]
}
2. Update main package to use optionalDependencies:
{
"name": "hookdeck-cli",
"version": "1.1.0",
"bin": {
"hookdeck": "./bin/hookdeck.js"
},
"optionalDependencies": {
"@hookdeck/cli-darwin-arm64": "1.1.0",
"@hookdeck/cli-darwin-x64": "1.1.0",
"@hookdeck/cli-linux-x64": "1.1.0",
"@hookdeck/cli-linux-arm64": "1.1.0",
"@hookdeck/cli-win32-x64": "1.1.0",
"@hookdeck/cli-win32-arm64": "1.1.0"
}
}
3. Create JavaScript wrapper to detect and run correct binary:
#!/usr/bin/env node
// bin/hookdeck.js
const { execFileSync } = require('child_process');
const { existsSync } = require('fs');
const path = require('path');
function getPlatformBinary() {
const platform = process.platform;
const arch = process.arch;
const packageName = `@hookdeck/cli-${platform}-${arch}`;
try {
const binaryPath = require.resolve(`${packageName}/hookdeck`);
if (existsSync(binaryPath)) {
return binaryPath;
}
} catch (e) {
// Platform package not installed
}
// Fallback: could still use postinstall download as backup
const fallbackPath = path.join(__dirname, 'hookdeck');
if (existsSync(fallbackPath)) {
return fallbackPath;
}
console.error(`Unsupported platform: ${platform}-${arch}`);
console.error('Please report this issue at https://github.com/hookdeck/hookdeck-cli/issues');
process.exit(1);
}
try {
const binaryPath = getPlatformBinary();
execFileSync(binaryPath, process.argv.slice(2), { stdio: 'inherit' });
} catch (error) {
process.exit(error.status || 1);
}
4. (Optional) Keep postinstall as fallback:
The postinstall script can remain as a fallback mechanism for edge cases, but the CLI will work without it:
{
"scripts": {
"postinstall": "node scripts/install-fallback.js"
}
}
How npm/pnpm Handle This
When a user runs pnpm install hookdeck-cli:
- npm/pnpm reads the
optionalDependencies list
- Automatically filters based on current platform's
os and cpu
- Only installs the matching platform package (e.g.,
@hookdeck/cli-darwin-arm64)
- The wrapper script detects and executes that binary
- No postinstall script needed - everything works out of the box
Migration Benefits
- Immediate: Users can install without any pnpm configuration
- Security: Works with
--ignore-scripts flag
- Performance: Only downloads 1-2MB instead of running download script
- Reliability: Binaries are part of npm registry (better caching, offline support)
- Compatibility: Works with all package managers (npm, yarn, pnpm, bun)
References
Additional Context
This issue affects all users on pnpm v10+ (released in 2024), which is becoming the default in many environments. The current workarounds require users to understand pnpm's security model and manually configure their workspace before installation - this is not acceptable for a CLI tool that should "just work."
The optionalDependencies pattern is now the industry standard for distributing native binaries via npm. Making this change would dramatically improve the developer experience and align hookdeck-cli with best practices used by major tools in the ecosystem.
Problem
The current distribution approach for
hookdeck-clirelies exclusively on postinstall scripts viago-npm, which creates a poor developer experience with modern package managers, particularly pnpm v10+.Impact on Users
When users install
hookdeck-cliwith pnpm v10+, they encounter this error:$ pnpm install -D hookdeck-cli # Install succeeds but shows warning about ignored build scripts $ pnpm hookdeck ./node_modules/.bin/hookdeck: cannot execute binary fileRoot Cause: pnpm v10+ blocks lifecycle scripts by default for security reasons. The package ships with a Linux x86-64 binary as a placeholder, and the postinstall script is supposed to download the correct platform-specific binary. When the script is blocked, users are left with the wrong binary.
Current Workarounds (Poor UX)
Users must manually configure pnpm before installation:
Option 1: Create
pnpm-workspace.yamlbefore installing:Option 2: Add to
package.jsonbefore installing:{ "pnpm": { "onlyBuiltDependencies": ["hookdeck-cli"] } }Option 3: Post-install approval (multi-step):
None of these are acceptable for a good CLI installation experience.
Reproduction Steps
pnpm install -D hookdeck-clipnpm hookdeck --versioncannot execute binary fileerrorEnvironment:
Recommended Solution
Switch to the optionalDependencies pattern used by industry-standard tools like esbuild, swc, and Sentry CLI. This approach:
--ignore-scriptsflagImplementation Pattern
1. Create platform-specific packages:
Publish separate packages for each platform:
Each platform package contains:
{ "name": "@hookdeck/cli-darwin-arm64", "version": "1.1.0", "os": ["darwin"], "cpu": ["arm64"], "bin": { "hookdeck": "./hookdeck" }, "files": ["hookdeck"] }2. Update main package to use optionalDependencies:
{ "name": "hookdeck-cli", "version": "1.1.0", "bin": { "hookdeck": "./bin/hookdeck.js" }, "optionalDependencies": { "@hookdeck/cli-darwin-arm64": "1.1.0", "@hookdeck/cli-darwin-x64": "1.1.0", "@hookdeck/cli-linux-x64": "1.1.0", "@hookdeck/cli-linux-arm64": "1.1.0", "@hookdeck/cli-win32-x64": "1.1.0", "@hookdeck/cli-win32-arm64": "1.1.0" } }3. Create JavaScript wrapper to detect and run correct binary:
4. (Optional) Keep postinstall as fallback:
The postinstall script can remain as a fallback mechanism for edge cases, but the CLI will work without it:
{ "scripts": { "postinstall": "node scripts/install-fallback.js" } }How npm/pnpm Handle This
When a user runs
pnpm install hookdeck-cli:optionalDependencieslistosandcpu@hookdeck/cli-darwin-arm64)Migration Benefits
--ignore-scriptsflagReferences
Additional Context
This issue affects all users on pnpm v10+ (released in 2024), which is becoming the default in many environments. The current workarounds require users to understand pnpm's security model and manually configure their workspace before installation - this is not acceptable for a CLI tool that should "just work."
The optionalDependencies pattern is now the industry standard for distributing native binaries via npm. Making this change would dramatically improve the developer experience and align hookdeck-cli with best practices used by major tools in the ecosystem.