mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
fix(build): prepare CLI and TUI build output with Node instead of POSIX tools (originally #956 by @dajiaohuang) (#1033)
The CLI and TUI builds prepare their output with Node instead of POSIX shell tools, so building works in a plain Windows shell. Originally contributed by @dajiaohuang in #956.
This commit is contained in:
1 parent
dc234f0de0
commit
c502584b18
5 files changed
+126
-2
No files matched your search
@@ -38,7 +38,7 @@
|
||||
"scripts"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc && chmod +x dist/bin-wrapper.js && mkdir -p dist/schemas && cp src/schemas/*.json dist/schemas/ && mkdir -p dist/lib/scope-templates && cp src/lib/scope-templates/*.md dist/lib/scope-templates/ && cp ../../LICENSE LICENSE && cp ../../README.md README.md",
|
||||
"build": "tsc && node ../../scripts/prepare-workspace-build.mjs cli",
|
||||
"postinstall": "node scripts/check-abi.mjs",
|
||||
"prepublishOnly": "bash ../../scripts/build-package.sh",
|
||||
"test": "vitest run",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"openrig-tui": "dist/main.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc && chmod +x dist/main.js",
|
||||
"build": "tsc && node ../../scripts/prepare-workspace-build.mjs tui",
|
||||
"pretest": "npm run build",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
|
||||
@@ -14,6 +14,8 @@ test("the CLI build carries the unchanged repository LICENSE and README into npm
|
||||
copyFileSync("LICENSE", join(root, "LICENSE"));
|
||||
copyFileSync("README.md", join(root, "README.md"));
|
||||
copyFileSync("packages/cli/package.json", join(cli, "package.json"));
|
||||
mkdirSync(join(root, "scripts"), { recursive: true });
|
||||
copyFileSync("scripts/prepare-workspace-build.mjs", join(root, "scripts/prepare-workspace-build.mjs"));
|
||||
writeFileSync(join(cli, "src/schemas/fixture.json"), "{}\n");
|
||||
writeFileSync(join(cli, "src/lib/scope-templates/fixture.md"), "fixture\n");
|
||||
// Compilation is outside this packaging regression. Run the actual build
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { chmodSync, copyFileSync, mkdirSync, readdirSync, statSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
|
||||
/** Finish a compiled workspace with the same assets/modes on every host. */
|
||||
export function prepareWorkspaceBuild(root, workspace, platform = process.platform) {
|
||||
if (workspace !== "cli" && workspace !== "tui") throw new Error(`Unknown workspace: ${workspace}`);
|
||||
const dir = path.join(root, "packages", workspace);
|
||||
const entry = path.join(dir, "dist", workspace === "cli" ? "bin-wrapper.js" : "main.js");
|
||||
const mode = statSync(entry).mode;
|
||||
if (platform !== "win32") chmodSync(entry, mode | 0o111);
|
||||
if (workspace === "tui") return;
|
||||
for (const [source, target, extension] of [
|
||||
["src/schemas", "dist/schemas", ".json"],
|
||||
["src/lib/scope-templates", "dist/lib/scope-templates", ".md"],
|
||||
]) {
|
||||
const destination = path.join(dir, target);
|
||||
mkdirSync(destination, { recursive: true });
|
||||
let copied = 0;
|
||||
for (const item of readdirSync(path.join(dir, source), { withFileTypes: true })) {
|
||||
if (item.name.startsWith(".") || !item.name.endsWith(extension)) continue;
|
||||
const file = path.join(dir, source, item.name);
|
||||
if (item.isFile() || (item.isSymbolicLink() && statSync(file).isFile())) {
|
||||
copyFileSync(file, path.join(destination, item.name));
|
||||
copied++;
|
||||
}
|
||||
}
|
||||
if (!copied) throw new Error(`No ${extension} files matched in ${source}`);
|
||||
}
|
||||
for (const name of ["LICENSE", "README.md"]) copyFileSync(path.join(root, name), path.join(dir, name));
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href)
|
||||
prepareWorkspaceBuild(path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."), process.argv[2]);
|
||||
@@ -0,0 +1,88 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, statSync, rmSync, symlinkSync, existsSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { test } from "node:test";
|
||||
import { prepareWorkspaceBuild } from "./prepare-workspace-build.mjs";
|
||||
|
||||
test("CLI build copies assets and root documents without shell utilities", () => {
|
||||
const root = mkdtempSync(path.join(os.tmpdir(), "openrig-build-assets-"));
|
||||
try {
|
||||
for (const dir of ["packages/cli/dist", "packages/cli/src/schemas", "packages/cli/src/lib/scope-templates"])
|
||||
mkdirSync(path.join(root, dir), { recursive: true });
|
||||
const files = {
|
||||
"LICENSE": "license", "README.md": "readme", "packages/cli/dist/bin-wrapper.js": "entry",
|
||||
"packages/cli/src/schemas/spec.json": "{}", "packages/cli/src/schemas/ignored.txt": "ignore",
|
||||
"packages/cli/src/lib/scope-templates/team.md": "team",
|
||||
"packages/cli/src/schemas/.hidden.json": "hidden schema",
|
||||
"packages/cli/src/lib/scope-templates/.hidden.md": "hidden template",
|
||||
};
|
||||
for (const [name, content] of Object.entries(files)) writeFileSync(path.join(root, name), content);
|
||||
prepareWorkspaceBuild(root, "cli", process.platform);
|
||||
for (const [name, content] of [["dist/schemas/spec.json", "{}"], ["dist/lib/scope-templates/team.md", "team"], ["LICENSE", "license"], ["README.md", "readme"]])
|
||||
assert.equal(readFileSync(path.join(root, "packages/cli", name), "utf8"), content);
|
||||
assert.throws(() => readFileSync(path.join(root, "packages/cli/dist/schemas/ignored.txt")));
|
||||
assert.equal(existsSync(path.join(root, "packages/cli/dist/schemas/.hidden.json")), false);
|
||||
assert.equal(existsSync(path.join(root, "packages/cli/dist/lib/scope-templates/.hidden.md")), false);
|
||||
if (process.platform !== "win32") assert.equal(statSync(path.join(root, "packages/cli/dist/bin-wrapper.js")).mode & 0o111, 0o111);
|
||||
writeFileSync(path.join(root, "packages/cli/src/schemas/spec.json"), "updated");
|
||||
prepareWorkspaceBuild(root, "cli", process.platform);
|
||||
assert.equal(readFileSync(path.join(root, "packages/cli/dist/schemas/spec.json"), "utf8"), "updated");
|
||||
} finally { rmSync(root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
function cliFixture() {
|
||||
const root = mkdtempSync(path.join(os.tmpdir(), "openrig-build-selection-"));
|
||||
for (const dir of ["packages/cli/dist", "packages/cli/src/schemas", "packages/cli/src/lib/scope-templates"])
|
||||
mkdirSync(path.join(root, dir), { recursive: true });
|
||||
for (const name of ["LICENSE", "README.md", "packages/cli/dist/bin-wrapper.js"])
|
||||
writeFileSync(path.join(root, name), "fixture");
|
||||
return root;
|
||||
}
|
||||
|
||||
test("CLI preparation follows file symlinks like the old cp glob", t => {
|
||||
const root = cliFixture();
|
||||
try {
|
||||
writeFileSync(path.join(root, "schema-source.txt"), "linked schema");
|
||||
writeFileSync(path.join(root, "template-source.txt"), "linked template");
|
||||
try {
|
||||
symlinkSync(path.join(root, "schema-source.txt"), path.join(root, "packages/cli/src/schemas/linked.json"), "file");
|
||||
symlinkSync(path.join(root, "template-source.txt"), path.join(root, "packages/cli/src/lib/scope-templates/linked.md"), "file");
|
||||
} catch (error) {
|
||||
if (process.platform === "win32" && ["EPERM", "EACCES"].includes(error.code)) {
|
||||
t.skip("Windows file-symlink privilege unavailable; requires POSIX validation"); return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
prepareWorkspaceBuild(root, "cli");
|
||||
assert.equal(readFileSync(path.join(root, "packages/cli/dist/schemas/linked.json"), "utf8"), "linked schema");
|
||||
assert.equal(readFileSync(path.join(root, "packages/cli/dist/lib/scope-templates/linked.md"), "utf8"), "linked template");
|
||||
assert.equal(statSync(path.join(root, "packages/cli/dist/schemas/linked.json")).isFile(), true);
|
||||
} finally { rmSync(root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
test("CLI preparation fails when a source glob would have no matches", () => {
|
||||
for (const [source, other, extension] of [
|
||||
["src/schemas", "src/lib/scope-templates/team.md", ".json"],
|
||||
["src/lib/scope-templates", "src/schemas/spec.json", ".md"],
|
||||
]) {
|
||||
const root = cliFixture();
|
||||
try {
|
||||
writeFileSync(path.join(root, "packages/cli", other), "fixture");
|
||||
writeFileSync(path.join(root, "packages/cli", source, `.hidden${extension}`), "not a glob match");
|
||||
assert.throws(() => prepareWorkspaceBuild(root, "cli"), /No .* files matched/);
|
||||
} finally { rmSync(root, { recursive: true, force: true }); }
|
||||
}
|
||||
});
|
||||
|
||||
test("TUI preparation requires the compiled entry and rejects unknown workspaces", () => {
|
||||
const root = mkdtempSync(path.join(os.tmpdir(), "openrig-build-tui-"));
|
||||
try {
|
||||
assert.throws(() => prepareWorkspaceBuild(root, "tui"), /ENOENT/);
|
||||
mkdirSync(path.join(root, "packages/tui/dist"), { recursive: true });
|
||||
writeFileSync(path.join(root, "packages/tui/dist/main.js"), "entry");
|
||||
prepareWorkspaceBuild(root, "tui", process.platform);
|
||||
if (process.platform !== "win32") assert.equal(statSync(path.join(root, "packages/tui/dist/main.js")).mode & 0o111, 0o111);
|
||||
assert.throws(() => prepareWorkspaceBuild(root, "other"), /Unknown workspace/);
|
||||
} finally { rmSync(root, { recursive: true, force: true }); }
|
||||
});
|
||||
Reference in new issue
Block a user