Skip to content

fix(npm): restore execute bits for prebuilt binaries#204

Merged
benvinegar merged 2 commits intomainfrom
fix/prebuilt-npm-binary-perms
Apr 14, 2026
Merged

fix(npm): restore execute bits for prebuilt binaries#204
benvinegar merged 2 commits intomainfrom
fix/prebuilt-npm-binary-perms

Conversation

@benvinegar
Copy link
Copy Markdown
Member

Summary

  • declare the native prebuilt binary in each platform package's bin manifest so npm restores execute bits on install
  • tighten the prebuilt smoke test to verify the real nested optional-dependency install path
  • add a regression test for the platform package manifest and note the fix in the changelog

Testing

  • bun run typecheck
  • bun test ./scripts
  • bun run build:prebuilt:npm
  • bun run check:prebuilt-pack
  • bun run smoke:prebuilt-install
  • verified on a fresh DigitalOcean Ubuntu 24.04 droplet with registry installs only:
    • hunkdiff@0.9.3 installs nested hunkdiff-linux-x64/bin/hunk as 644 and fails with spawnSync ... EACCES
    • hunkdiff@0.9.4-beta.0 installs that binary as 755 and hunk --help succeeds

Fixes #202.

This PR description was generated by Pi using GPT-5

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 14, 2026

Greptile Summary

Fixes the EACCES regression (#202) caused by prebuilt platform binaries shipping without execute bits after a global npm install -g hunkdiff. The fix is precise: adding a bin entry to each platform package's manifest tells npm to restore execute bits during extraction, covering root-owned installs where a post-install chmod cannot run. The smoke test is meaningfully tightened to mirror the real nested optional-dependency install path and to explicitly verify the mode of the installed binary before invoking it.

Confidence Score: 5/5

  • Safe to merge — the fix is targeted and correct, and the smoke test now validates the real install path.
  • All findings are P2 (minor test-coverage gap and a pre-existing cleanup edge case). The core fix correctly leverages npm's bin-field behaviour to restore execute bits, and the new smoke test exercises the actual nested optional-dependency path that was broken.
  • scripts/prebuilt-package-helpers.test.ts — the new manifest test could assert name and version for stronger regression coverage.

Important Files Changed

Filename Overview
scripts/prebuilt-package-helpers.ts Extracts manifest building into buildPlatformPackageManifest; the core fix is the bin entry that triggers npm's execute-bit restoration on install. Logic is correct and handles the windows/win32 OS name translation consistently.
scripts/prebuilt-package-helpers.test.ts Adds a regression test for the new buildPlatformPackageManifest; asserts bin, os, and cpu but omits name and version assertions, which are the other fields most likely to regress.
scripts/smoke-prebuilt-install.ts Smoke test substantially improved: now exercises the real nested optional-dependency install path, patches the meta-package's optionalDependencies to the local tarball, and verifies execute-bit mode before invoking hunk --help.
scripts/stage-prebuilt-npm.ts Replaces the inlined manifest object with a call to buildPlatformPackageManifest; mechanical refactor, no behavioural change beyond the added bin field.
CHANGELOG.md Entry added under [Unreleased] / Fixed following the established format; no issues.

Sequence Diagram

sequenceDiagram
    participant User
    participant npm
    participant hunkdiff as hunkdiff (meta pkg)
    participant platform as hunkdiff-linux-x64 (platform pkg)
    participant bin as installDir/bin/hunk

    User->>npm: npm install -g hunkdiff
    npm->>hunkdiff: extract + install
    hunkdiff->>npm: "optionalDependencies: { hunkdiff-linux-x64: "^0.9.x" }"
    npm->>platform: extract + install as nested dep
    Note over platform: bin.hunk → ./bin/hunk<br/>npm sets chmod +x on bin/hunk
    platform-->>npm: binary installed with 755
    hunkdiff->>bin: JS wrapper symlink → hunkdiff/bin/hunk.js
    bin-->>User: hunk --help ✓ (no EACCES)
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: scripts/prebuilt-package-helpers.test.ts
Line: 83-98

Comment:
**Test coverage gap for `name` and `version` fields**

The test verifies `bin`, `os`, and `cpu` but doesn't assert `name` (derived from `spec.packageName`) or `version` (passed through from `rootPackage`). These are the fields most likely to silently regress — e.g. if the manifest builder accidentally dropped `name` or pulled `version` from the wrong source, no test would catch it.

```suggestion
  test("buildPlatformPackageManifest declares the native binary as a bin entry", () => {
    const manifest = buildPlatformPackageManifest(
      {
        version: "1.2.3",
        description: "Desktop diff viewer",
        license: "MIT",
      },
      getPlatformPackageSpecForHost("linux", "x64"),
    );

    expect(manifest.name).toBe("hunkdiff-linux-x64");
    expect(manifest.version).toBe("1.2.3");
    expect(manifest.bin).toEqual({
      hunk: "./bin/hunk",
    });
    expect(manifest.os).toEqual(["linux"]);
    expect(manifest.cpu).toEqual(["x64"]);
  });
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: scripts/smoke-prebuilt-install.ts
Line: 50-69

Comment:
**Temp dirs not cleaned up on early-exit error**

`packageDir`, `installDir`, and `smokeMetaDir` are all created before the `try` block. If either the `node` or `bash` resolution check throws, the three temp dirs are left behind. This was a pre-existing gap for the first two dirs, but `smokeMetaDir` is new here.

Moving the `mkdtempSync` calls inside the `try` block (or wrapping the resolution checks inside it too) would close the leak. At minimum, adding a comment acknowledging the intentional trade-off would avoid surprises.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(npm): restore execute bits for prebu..." | Re-trigger Greptile

Comment thread scripts/prebuilt-package-helpers.test.ts
Comment thread scripts/smoke-prebuilt-install.ts Outdated
@benvinegar benvinegar merged commit 1d019f8 into main Apr 14, 2026
3 checks passed
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.

EACCESS error

1 participant