|
1 | 1 | #!/usr/bin/env bun |
2 | 2 |
|
3 | | -import { existsSync, mkdtempSync, mkdirSync, rmSync } from "node:fs"; |
| 3 | +import { |
| 4 | + cpSync, |
| 5 | + existsSync, |
| 6 | + mkdtempSync, |
| 7 | + mkdirSync, |
| 8 | + readFileSync, |
| 9 | + rmSync, |
| 10 | + statSync, |
| 11 | + writeFileSync, |
| 12 | +} from "node:fs"; |
4 | 13 | import path from "node:path"; |
5 | | -import { getHostPlatformPackageSpec, releaseNpmDir } from "./prebuilt-package-helpers"; |
| 14 | +import { |
| 15 | + binaryFilenameForSpec, |
| 16 | + getHostPlatformPackageSpec, |
| 17 | + releaseNpmDir, |
| 18 | +} from "./prebuilt-package-helpers"; |
6 | 19 |
|
7 | 20 | function run(command: string[], options?: { cwd?: string; env?: NodeJS.ProcessEnv }) { |
8 | 21 | const proc = Bun.spawnSync(command, { |
@@ -32,40 +45,91 @@ const releaseRoot = releaseNpmDir(repoRoot); |
32 | 45 | const hostSpec = getHostPlatformPackageSpec(); |
33 | 46 | const tempRoot = path.join(repoRoot, "tmp"); |
34 | 47 | mkdirSync(tempRoot, { recursive: true }); |
35 | | -const packageDir = mkdtempSync(path.join(tempRoot, "hunk-prebuilt-pack-")); |
36 | | -const installDir = mkdtempSync(path.join(tempRoot, "hunk-prebuilt-install-")); |
37 | | -const nodeBinary = Bun.spawnSync(["bash", "-lc", "command -v node"], { |
38 | | - stdin: "ignore", |
39 | | - stdout: "pipe", |
40 | | - stderr: "pipe", |
41 | | - env: process.env, |
42 | | -}); |
43 | | -const resolvedNode = Buffer.from(nodeBinary.stdout).toString("utf8").trim(); |
44 | | -if (nodeBinary.exitCode !== 0 || resolvedNode.length === 0) { |
45 | | - throw new Error("Could not resolve node on PATH for the prebuilt install smoke test."); |
46 | | -} |
47 | | -const nodeDir = path.dirname(resolvedNode); |
| 48 | +let packageDir: string | undefined; |
| 49 | +let installDir: string | undefined; |
| 50 | +let smokeMetaDir: string | undefined; |
48 | 51 |
|
49 | 52 | try { |
50 | | - run(["npm", "pack", "--pack-destination", packageDir], { |
51 | | - cwd: path.join(releaseRoot, hostSpec.packageName), |
| 53 | + packageDir = mkdtempSync(path.join(tempRoot, "hunk-prebuilt-pack-")); |
| 54 | + installDir = mkdtempSync(path.join(tempRoot, "hunk-prebuilt-install-")); |
| 55 | + smokeMetaDir = mkdtempSync(path.join(tempRoot, "hunk-prebuilt-meta-")); |
| 56 | + |
| 57 | + const nodeBinary = Bun.spawnSync(["bash", "-lc", "command -v node"], { |
| 58 | + stdin: "ignore", |
| 59 | + stdout: "pipe", |
| 60 | + stderr: "pipe", |
| 61 | + env: process.env, |
52 | 62 | }); |
| 63 | + const resolvedNode = Buffer.from(nodeBinary.stdout).toString("utf8").trim(); |
| 64 | + if (nodeBinary.exitCode !== 0 || resolvedNode.length === 0) { |
| 65 | + throw new Error("Could not resolve node on PATH for the prebuilt install smoke test."); |
| 66 | + } |
| 67 | + const bashBinary = Bun.spawnSync(["bash", "-lc", "command -v bash"], { |
| 68 | + stdin: "ignore", |
| 69 | + stdout: "pipe", |
| 70 | + stderr: "pipe", |
| 71 | + env: process.env, |
| 72 | + }); |
| 73 | + const resolvedBash = Buffer.from(bashBinary.stdout).toString("utf8").trim(); |
| 74 | + if (bashBinary.exitCode !== 0 || resolvedBash.length === 0) { |
| 75 | + throw new Error("Could not resolve bash on PATH for the prebuilt install smoke test."); |
| 76 | + } |
| 77 | + const nodeDir = path.dirname(resolvedNode); |
| 78 | + const bashDir = path.dirname(resolvedBash); |
| 79 | + |
53 | 80 | run(["npm", "pack", "--pack-destination", packageDir], { |
54 | | - cwd: path.join(releaseRoot, "hunkdiff"), |
| 81 | + cwd: path.join(releaseRoot, hostSpec.packageName), |
55 | 82 | }); |
56 | 83 |
|
57 | 84 | const platformTarball = path.join(packageDir, `${hostSpec.packageName}-${packageVersion}.tgz`); |
| 85 | + |
| 86 | + // Point a temp copy of the staged meta package at the local platform tarball. |
| 87 | + // The real manifest uses semver ranges, but this smoke test runs before publish. |
| 88 | + const smokePackageDir = path.join(smokeMetaDir, "hunkdiff"); |
| 89 | + cpSync(path.join(releaseRoot, "hunkdiff"), smokePackageDir, { recursive: true }); |
| 90 | + const smokeManifestPath = path.join(smokePackageDir, "package.json"); |
| 91 | + const smokeManifest = JSON.parse(readFileSync(smokeManifestPath, "utf8")) as { |
| 92 | + optionalDependencies?: Record<string, string>; |
| 93 | + }; |
| 94 | + smokeManifest.optionalDependencies = { |
| 95 | + ...smokeManifest.optionalDependencies, |
| 96 | + [hostSpec.packageName]: `file:${platformTarball}`, |
| 97 | + }; |
| 98 | + writeFileSync(smokeManifestPath, `${JSON.stringify(smokeManifest, null, 2)}\n`); |
| 99 | + |
| 100 | + run(["npm", "pack", "--pack-destination", packageDir], { |
| 101 | + cwd: smokePackageDir, |
| 102 | + }); |
58 | 103 | const metaTarball = path.join(packageDir, `hunkdiff-${packageVersion}.tgz`); |
59 | 104 |
|
60 | | - run(["npm", "install", "-g", "--prefix", installDir, platformTarball]); |
61 | 105 | run(["npm", "install", "-g", "--prefix", installDir, metaTarball]); |
62 | 106 |
|
63 | | - const sanitizedPath = `${path.join(installDir, "bin")}:${nodeDir}`; |
| 107 | + const sanitizedPath = [path.join(installDir, "bin"), nodeDir, bashDir].join(":"); |
64 | 108 | const installedHunk = path.join(installDir, "bin", "hunk"); |
| 109 | + const installedPlatformBinary = path.join( |
| 110 | + installDir, |
| 111 | + "lib", |
| 112 | + "node_modules", |
| 113 | + "hunkdiff", |
| 114 | + "node_modules", |
| 115 | + hostSpec.packageName, |
| 116 | + "bin", |
| 117 | + binaryFilenameForSpec(hostSpec), |
| 118 | + ); |
65 | 119 | const commandEnv = { |
66 | 120 | ...process.env, |
67 | 121 | PATH: sanitizedPath, |
68 | 122 | }; |
| 123 | + |
| 124 | + if (process.platform !== "win32") { |
| 125 | + const installedBinaryMode = statSync(installedPlatformBinary).mode & 0o777; |
| 126 | + if ((installedBinaryMode & 0o111) === 0) { |
| 127 | + throw new Error( |
| 128 | + `Expected installed platform binary to keep execute bits, got mode ${installedBinaryMode.toString(8)} at ${installedPlatformBinary}`, |
| 129 | + ); |
| 130 | + } |
| 131 | + } |
| 132 | + |
69 | 133 | const help = run([installedHunk, "--help"], { |
70 | 134 | env: commandEnv, |
71 | 135 | }); |
@@ -112,6 +176,13 @@ try { |
112 | 176 |
|
113 | 177 | console.log(`Verified prebuilt npm install smoke test with ${hostSpec.packageName}`); |
114 | 178 | } finally { |
115 | | - rmSync(packageDir, { recursive: true, force: true }); |
116 | | - rmSync(installDir, { recursive: true, force: true }); |
| 179 | + if (packageDir) { |
| 180 | + rmSync(packageDir, { recursive: true, force: true }); |
| 181 | + } |
| 182 | + if (installDir) { |
| 183 | + rmSync(installDir, { recursive: true, force: true }); |
| 184 | + } |
| 185 | + if (smokeMetaDir) { |
| 186 | + rmSync(smokeMetaDir, { recursive: true, force: true }); |
| 187 | + } |
117 | 188 | } |
0 commit comments