mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-02 13:12:50 +00:00
fix(installer): disable dependency hooks explicitly across Bun versions
This commit is contained in:
1 parent
4f2083bfbd
commit
2b8803f777
2 files changed
+98
-1
No files matched your search
@@ -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()
|
||||||
@@ -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:
|
||||||
|
|||||||
Reference in new issue
Block a user