fix(installer): disable dependency hooks explicitly across Bun versions

This commit is contained in:
Rudy Celekli authored and Mads Lorentzen committed 2026-10-27 16:14:35 +01:00
1 parent 4f2083bfbd
commit 2b8803f777
2 files changed
+98 -1

No files matched your search

+94
View File
@@ -0,0 +1,94 @@
"""Real Bun install against an offline npm registry: dependency hooks stay off.
The fixture has the same ordinary npm dependency shape as esbuild, whose
postinstall Bun 1.2 runs by default. It writes only a local marker. No public
registry, credentials or lifecycle-script runner is mocked.
"""
import base64
import hashlib
import http.server
import io
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tarfile
from tempfile import TemporaryDirectory
import threading
import unittest
ROOT = Path(__file__).resolve().parent.parent
BUN = os.environ.get("TEST_BUN") or shutil.which("bun")
@unittest.skipUnless(BUN, "requires Bun for the offline package-manager integration")
class DependencyHooks(unittest.TestCase):
def test_installing_dependency_does_not_execute_its_hook(self):
package = {"name": "esbuild", "version": "0.25.0", "scripts": {"postinstall": "bun run hook.js"}}
archive = io.BytesIO()
with tarfile.open(fileobj=archive, mode="w:gz") as tar:
for name, data in {
"package.json": json.dumps(package).encode(),
"hook.js": b'await Bun.write("hook-ran.txt", "ran");',
}.items():
entry = tarfile.TarInfo("package/" + name)
entry.size = len(data)
tar.addfile(entry, io.BytesIO(data))
blob = archive.getvalue()
class Registry(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path.endswith(".tgz"):
body = blob
else:
metadata = {**package, "dist": {
"tarball": f"http://127.0.0.1:{self.server.server_port}/esbuild.tgz",
"integrity": "sha512-" + base64.b64encode(hashlib.sha512(blob).digest()).decode(),
}}
body = json.dumps({"name": "esbuild", "dist-tags": {"latest": "0.25.0"},
"versions": {"0.25.0": metadata}}).encode()
self.send_response(200)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, *args):
pass
server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), Registry)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
with TemporaryDirectory() as directory:
root = Path(directory)
tools = root / "tools"
tools.mkdir()
for name in ("install_portal_clis.py", "security_guards.py"):
shutil.copyfile(ROOT / "tools" / name, tools / name)
cli = root / ".agents/skills/example-search/cli"
cli.mkdir(parents=True)
(cli / "package.json").write_text(json.dumps({
"name": "example-search", "private": True, "dependencies": {"esbuild": "0.25.0"},
}), encoding="utf-8")
(cli / ".npmrc").write_text(f"registry=http://127.0.0.1:{server.server_port}/\n", encoding="utf-8")
env = {"PATH": str(Path(BUN).absolute().parent) + os.pathsep + os.defpath,
"HOME": str(root), "TMPDIR": str(root), "TEMP": str(root), "TMP": str(root),
"BUN_INSTALL_CACHE_DIR": str(root / "cache")}
if "SYSTEMROOT" in os.environ:
env["SYSTEMROOT"] = os.environ["SYSTEMROOT"]
proc = subprocess.run([sys.executable, str(tools / "install_portal_clis.py")],
env=env, capture_output=True, text=True, timeout=30)
self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr)
dependency = cli / "node_modules/esbuild"
self.assertTrue((dependency / "hook.js").is_file(), "the dependency was actually installed")
self.assertFalse((dependency / "hook-ran.txt").exists(), "the dependency install hook ran")
finally:
server.shutdown()
server.server_close()
thread.join()
if __name__ == "__main__":
unittest.main()
+4 -1
View File
@@ -18,6 +18,9 @@ skipped and reported instead of installed: those are the hooks that run code
during `bun install`, and tools/security_guards.py forbids them in-tree. A during `bun install`, and tools/security_guards.py forbids them in-tree. A
borrowed portal that carries one gets read by a human before it runs. borrowed portal that carries one gets read by a human before it runs.
Every install also passes --ignore-scripts, including on older Bun releases
that run a built-in list of dependency hooks without trustedDependencies.
Each CLI installs independently; one failure does not stop the rest. Exit 0 Each CLI installs independently; one failure does not stop the rest. Exit 0
when every CLI installed, 1 when bun is missing, a manifest was skipped, or an when every CLI installed, 1 when bun is missing, a manifest was skipped, or an
install failed. install failed.
@@ -72,7 +75,7 @@ def install(cli_dirs: list[Path], bun: str, dry_run: bool, root: Path = ROOT, ru
print(f"would install {name}") print(f"would install {name}")
continue continue
print(f"installing {name}", flush=True) print(f"installing {name}", flush=True)
if run([bun, "install"], cwd=cli_dir).returncode != 0: if run([bun, "install", "--ignore-scripts"], cwd=cli_dir).returncode != 0:
print(f"FAIL {name}: bun install exited non-zero") print(f"FAIL {name}: bun install exited non-zero")
failures.append(name) failures.append(name)
if failures: if failures: