History

compat with legacy dir

Rhys Sullivan committed 8 months ago

Commit a802f4e1 parent ed7a3486 files · +377 −25Browse files

Showing the changes to apps (3 of 6 files). Show all

Unified · Split

3 files
  1. apps/executor/package.json +2 −1
  2. apps/executor/src/distribution/build-release.ts +137 −0
  3. apps/executor/src/distribution/portable.ts +2 −0
apps/executor/package.json+2−1
@@ -33,7 +33,8 @@ · 32 unmodified lines
3333 "pack:dist": "bun src/distribution/build-package.ts && npm pack ./dist/npm",
3434 "publish:dist:dry-run": "bun src/distribution/build-package.ts && npm publish ./dist/npm --dry-run",
3535 "publish:dist": "bun src/distribution/build-package.ts && npm publish ./dist/npm",
36− "build:portable": "bun src/distribution/build-portable.ts"
36+ "build:portable": "bun src/distribution/build-portable.ts",
37+ "build:release": "bun src/distribution/build-release.ts"
3738 },
3839 "dependencies": {
3940 "@effect/cli": "catalog:",
apps/executor/src/distribution/build-release.ts+137−0
@@ -0,0 +1,137 @@
1+import { createHash } from "node:crypto";
2+import { spawn } from "node:child_process";
3+import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
4+import { join, relative, resolve } from "node:path";
5+
6+import { buildDistributionPackage } from "./artifact";
7+import { repoRoot } from "./metadata";
8+import { buildPortableDistribution, portableTargetIds } from "./portable";
9+
10+const defaultReleaseDir = resolve(repoRoot, "apps/executor/dist/release");
11+
12+type CommandInput = {
13+ command: string;
14+ args: ReadonlyArray<string>;
15+ cwd: string;
16+};
17+
18+type CommandOutput = {
19+ stdout: string;
20+ stderr: string;
21+};
22+
23+type PackResult = {
24+ filename?: string;
25+};
26+
27+const runCommand = async (input: CommandInput): Promise<CommandOutput> => {
28+ const child = spawn(input.command, [...input.args], {
29+ cwd: input.cwd,
30+ env: process.env,
31+ stdio: ["ignore", "pipe", "pipe"],
32+ });
33+
34+ let stdout = "";
35+ let stderr = "";
36+
37+ child.stdout?.setEncoding("utf8");
38+ child.stdout?.on("data", (chunk) => {
39+ stdout += chunk;
40+ });
41+
42+ child.stderr?.setEncoding("utf8");
43+ child.stderr?.on("data", (chunk) => {
44+ stderr += chunk;
45+ });
46+
47+ const exitCode = await new Promise<number>((resolveExitCode, reject) => {
48+ child.once("error", reject);
49+ child.once("close", (code) => {
50+ resolveExitCode(code ?? -1);
51+ });
52+ });
53+
54+ if (exitCode !== 0) {
55+ throw new Error(
56+ [
57+ `${input.command} ${input.args.join(" ")} exited with code ${exitCode}`,
58+ stdout.trim().length > 0 ? `stdout:\n${stdout.trim()}` : null,
59+ stderr.trim().length > 0 ? `stderr:\n${stderr.trim()}` : null,
60+ ]
61+ .filter((part) => part !== null)
62+ .join("\n\n"),
63+ );
64+ }
65+
66+ return {
67+ stdout: stdout.trim(),
68+ stderr: stderr.trim(),
69+ };
70+};
71+
72+const computeSha256 = (contents: Uint8Array): string =>
73+ createHash("sha256").update(contents).digest("hex");
74+
75+const createChecksumsFile = async (
76+ releaseDir: string,
77+ assetPaths: ReadonlyArray<string>,
78+): Promise<string> => {
79+ const entries = await Promise.all(
80+ [...assetPaths]
81+ .sort((left, right) => left.localeCompare(right))
82+ .map(async (assetPath) => {
83+ const contents = await readFile(assetPath);
84+ const relativePath = relative(releaseDir, assetPath).replaceAll("\\", "/");
85+ return `${computeSha256(contents)} ${relativePath}`;
86+ }),
87+ );
88+
89+ const checksumsPath = join(releaseDir, "checksums.txt");
90+ await writeFile(checksumsPath, `${entries.join("\n")}\n`);
91+ return checksumsPath;
92+};
93+
94+const packDistributionPackage = async (releaseDir: string): Promise<string> => {
95+ const output = await runCommand({
96+ command: "npm",
97+ args: ["pack", "./apps/executor/dist/npm", "--pack-destination", releaseDir, "--json"],
98+ cwd: repoRoot,
99+ });
100+ const [result] = JSON.parse(output.stdout) as ReadonlyArray<PackResult>;
101+ const filename = result?.filename;
102+
103+ if (!filename) {
104+ throw new Error(`npm pack did not report an output filename. stdout:\n${output.stdout}`);
105+ }
106+
107+ return join(releaseDir, filename);
108+};
109+
110+const main = async () => {
111+ const releaseDir = defaultReleaseDir;
112+ const portableDir = join(releaseDir, "portable");
113+
114+ await rm(releaseDir, { recursive: true, force: true });
115+ await mkdir(releaseDir, { recursive: true });
116+
117+ await buildDistributionPackage();
118+ const packageArchivePath = await packDistributionPackage(releaseDir);
119+ const portableArtifact = await buildPortableDistribution({
120+ outputDir: portableDir,
121+ buildWeb: false,
122+ targets: portableTargetIds,
123+ });
124+ const portableArchivePaths = portableArtifact.artifacts
125+ .map((artifact) => artifact.archivePath)
126+ .filter((archivePath): archivePath is string => archivePath !== null);
127+ const checksumsPath = await createChecksumsFile(releaseDir, [
128+ packageArchivePath,
129+ ...portableArchivePaths,
130+ ]);
131+
132+ for (const assetPath of [packageArchivePath, ...portableArchivePaths, checksumsPath]) {
133+ process.stdout.write(`${assetPath}\n`);
134+ }
135+};
136+
137+await main();
apps/executor/src/distribution/portable.ts+2−0
@@ -74,6 +74,8 @@ const portableTargets: Record<string, PortableTarget> = { · 73 unmodified lines
7474 },
7575 };
7676
77+export const portableTargetIds = Object.freeze(Object.keys(portableTargets));
78+
7779 export type BuildPortableDistributionOptions = {
7880 outputDir?: string;
7981 packageName?: string;