Skip to content

Commit c3b5f4d

Browse files
committed
feat(release): publish Windows prebuilt artifacts
1 parent 1587115 commit c3b5f4d

8 files changed

Lines changed: 71 additions & 33 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ jobs:
126126
os:
127127
- ubuntu-latest
128128
- macos-latest
129+
- windows-latest
129130
steps:
130131
- name: Check out repository
131132
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

.github/workflows/release-prebuilt-npm.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ jobs:
3636
runner: ubuntu-24.04-arm
3737
- package_name: hunkdiff-linux-x64
3838
runner: ubuntu-latest
39+
- package_name: hunkdiff-windows-x64
40+
runner: windows-latest
3941
- package_name: hunkdiff-darwin-x64
4042
runner: macos-15-intel
4143
- package_name: hunkdiff-darwin-arm64

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ All notable user-visible changes to Hunk are documented in this file.
66

77
### Added
88

9+
- Added Windows x64 prebuilt artifact publishing to the release workflow.
10+
911
### Changed
1012

1113
### Fixed

bin/hunk.cjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ function hostCandidates() {
6565
if (arch === "x64") return [{ packageName: "hunkdiff-linux-x64", binary }];
6666
}
6767

68+
if (platform === "windows") {
69+
if (arch === "x64") return [{ packageName: "hunkdiff-windows-x64", binary }];
70+
}
71+
6872
return [];
6973
}
7074

scripts/build-prebuilt-artifact.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ const repoRoot = path.resolve(import.meta.dir, "..");
3636
const options = parseArgs(process.argv.slice(2));
3737
const spec = getHostPlatformPackageSpec();
3838
const binaryName = binaryFilenameForSpec(spec);
39-
const compiledBinary = path.join(repoRoot, "dist", "hunk");
39+
const compiledBinaryCandidates = [
40+
path.join(repoRoot, "dist", binaryName),
41+
path.join(repoRoot, "dist", "hunk"),
42+
];
43+
const compiledBinary = compiledBinaryCandidates.find((candidate) => existsSync(candidate));
4044
const outputRoot = path.resolve(options.outputRoot ?? releaseArtifactsDir(repoRoot));
4145
const outputDir = path.join(outputRoot, spec.packageName);
4246

@@ -46,8 +50,10 @@ if (options.expectedPackage && options.expectedPackage !== spec.packageName) {
4650
);
4751
}
4852

49-
if (!existsSync(compiledBinary)) {
50-
throw new Error(`Missing compiled binary at ${compiledBinary}. Run \`bun run build:bin\` first.`);
53+
if (!compiledBinary) {
54+
throw new Error(
55+
`Missing compiled binary at ${compiledBinaryCandidates.join(" or ")}. Run \`bun run build:bin\` first.`,
56+
);
5157
}
5258

5359
rmSync(outputDir, { recursive: true, force: true });

scripts/prebuilt-package-helpers.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ describe("prebuilt package helpers", () => {
7575
expect(getPlatformPackageSpecForHost("linux", "arm64").packageName).toBe(
7676
"hunkdiff-linux-arm64",
7777
);
78+
expect(getPlatformPackageSpecForHost("win32", "x64").packageName).toBe("hunkdiff-windows-x64");
7879
});
7980

8081
test("getHostPlatformPackageSpec resolves the current machine", () => {
@@ -102,6 +103,24 @@ describe("prebuilt package helpers", () => {
102103
expect(manifest.cpu).toEqual(["x64"]);
103104
});
104105

106+
test("buildPlatformPackageManifest maps Windows packages to npm win32", () => {
107+
const manifest = buildPlatformPackageManifest(
108+
{
109+
version: "1.2.3",
110+
description: "Desktop diff viewer",
111+
license: "MIT",
112+
},
113+
getPlatformPackageSpecForHost("win32", "x64"),
114+
);
115+
116+
expect(manifest.name).toBe("hunkdiff-windows-x64");
117+
expect(manifest.bin).toEqual({
118+
hunk: "./bin/hunk.exe",
119+
});
120+
expect(manifest.os).toEqual(["win32"]);
121+
expect(manifest.cpu).toEqual(["x64"]);
122+
});
123+
105124
test("sortPlatformPackageSpecs keeps package publish order stable", () => {
106125
const reversed = [...PLATFORM_PACKAGE_MATRIX].reverse();
107126
expect(sortPlatformPackageSpecs(reversed).map((spec) => spec.packageName)).toEqual([

scripts/prebuilt-package-helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const ARCH_NAME_MAP: Partial<Record<NodeJS.Architecture, SupportedArch>> = {
2525
arm64: "arm64",
2626
};
2727

28-
/** Platforms we actually plan to publish in the first prebuilt-binary rollout. */
28+
/** Platforms published as optional prebuilt binary packages. */
2929
export const PLATFORM_PACKAGE_MATRIX: PlatformPackageSpec[] = [
3030
{
3131
packageName: "hunkdiff-darwin-arm64",

scripts/smoke-prebuilt-install.ts

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,25 @@ function run(command: string[], options?: { cwd?: string; env?: NodeJS.ProcessEn
3838
return { stdout, stderr };
3939
}
4040

41+
/** Resolve a command directory for a sanitized PATH that still works cross-platform. */
42+
function commandDirectory(command: string) {
43+
const proc = Bun.spawnSync(
44+
process.platform === "win32" ? ["where", command] : ["bash", "-lc", `command -v ${command}`],
45+
{
46+
stdin: "ignore",
47+
stdout: "pipe",
48+
stderr: "pipe",
49+
env: process.env,
50+
},
51+
);
52+
const resolved = Buffer.from(proc.stdout).toString("utf8").split(/\r?\n/, 1)[0]?.trim();
53+
if (proc.exitCode !== 0 || !resolved) {
54+
throw new Error(`Could not resolve ${command} on PATH for the prebuilt install smoke test.`);
55+
}
56+
57+
return path.dirname(resolved);
58+
}
59+
4160
const repoRoot = path.resolve(import.meta.dir, "..");
4261
const packageVersion = JSON.parse(await Bun.file(path.join(repoRoot, "package.json")).text())
4362
.version as string;
@@ -54,28 +73,8 @@ try {
5473
installDir = mkdtempSync(path.join(tempRoot, "hunk-prebuilt-install-"));
5574
smokeMetaDir = mkdtempSync(path.join(tempRoot, "hunk-prebuilt-meta-"));
5675

57-
const nodeBinary = Bun.spawnSync(["bash", "-lc", "command -v node"], {
58-
stdin: "ignore",
59-
stdout: "pipe",
60-
stderr: "pipe",
61-
env: process.env,
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);
76+
const nodeDir = path.dirname(process.execPath);
77+
const bashDir = commandDirectory("bash");
7978

8079
run(["npm", "pack", "--pack-destination", packageDir], {
8180
cwd: path.join(releaseRoot, hostSpec.packageName),
@@ -104,13 +103,18 @@ try {
104103

105104
run(["npm", "install", "-g", "--prefix", installDir, metaTarball]);
106105

107-
const sanitizedPath = [path.join(installDir, "bin"), nodeDir, bashDir].join(":");
108-
const installedHunk = path.join(installDir, "bin", "hunk");
106+
const installedBinDir = process.platform === "win32" ? installDir : path.join(installDir, "bin");
107+
const installedPackageRoot =
108+
process.platform === "win32"
109+
? path.join(installDir, "node_modules", "hunkdiff")
110+
: path.join(installDir, "lib", "node_modules", "hunkdiff");
111+
const sanitizedPath = [installedBinDir, nodeDir, bashDir].join(path.delimiter);
112+
const installedHunk = path.join(
113+
installedBinDir,
114+
process.platform === "win32" ? "hunk.cmd" : "hunk",
115+
);
109116
const installedPlatformBinary = path.join(
110-
installDir,
111-
"lib",
112-
"node_modules",
113-
"hunkdiff",
117+
installedPackageRoot,
114118
"node_modules",
115119
hostSpec.packageName,
116120
"bin",
@@ -161,7 +165,7 @@ try {
161165

162166
const bunCheck = Bun.spawnSync(
163167
[
164-
resolvedNode,
168+
process.execPath,
165169
"-e",
166170
"const {spawnSync}=require('node:child_process'); process.exit(spawnSync('bun',['--version'],{stdio:'ignore'}).status===0?1:0);",
167171
],

0 commit comments

Comments
 (0)