Release Helper Tools 0.1.0
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env node
|
||||
import { createHash } from "node:crypto";
|
||||
import { execFile } from "node:child_process";
|
||||
import {
|
||||
access,
|
||||
copyFile,
|
||||
mkdir,
|
||||
mkdtemp,
|
||||
readFile,
|
||||
rename,
|
||||
rm,
|
||||
writeFile,
|
||||
} from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const execute = promisify(execFile);
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const details = JSON.parse(
|
||||
await readFile(path.join(root, "package.json"), "utf8"),
|
||||
);
|
||||
const filename = `add-ideas-toolbox-helpers-${details.version}.tgz`;
|
||||
const output = path.join(root, "release", filename);
|
||||
const checksumOutput = `${output}.sha256`;
|
||||
const force = process.argv.includes("--force");
|
||||
const exists = (file) =>
|
||||
access(file).then(
|
||||
() => true,
|
||||
() => false,
|
||||
);
|
||||
if (!force && ((await exists(output)) || (await exists(checksumOutput))))
|
||||
throw new Error(
|
||||
"Library package output already exists; use --force to replace it",
|
||||
);
|
||||
for (const required of [
|
||||
"lib-dist/index.js",
|
||||
"lib-dist/types/index.d.ts",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"SOURCE.md",
|
||||
])
|
||||
await access(path.join(root, required));
|
||||
|
||||
await mkdir(path.dirname(output), { recursive: true });
|
||||
const staging = await mkdtemp(path.join(os.tmpdir(), "helper-npm-pack-"));
|
||||
try {
|
||||
const { stdout } = await execute(
|
||||
"npm",
|
||||
["pack", "--json", "--pack-destination", staging],
|
||||
{
|
||||
cwd: root,
|
||||
env: { ...process.env, TZ: "UTC", SOURCE_DATE_EPOCH: "315532800" },
|
||||
maxBuffer: 4 * 1024 * 1024,
|
||||
},
|
||||
);
|
||||
const packed = JSON.parse(stdout)[0];
|
||||
if (packed?.filename !== filename)
|
||||
throw new Error(`Unexpected npm package name: ${packed?.filename}`);
|
||||
const packedPaths = new Set(
|
||||
Array.isArray(packed?.files)
|
||||
? packed.files.map((entry) => entry?.path).filter(Boolean)
|
||||
: [],
|
||||
);
|
||||
for (const required of [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"SOURCE.md",
|
||||
"SECURITY.md",
|
||||
"THIRD_PARTY_NOTICES.md",
|
||||
"docs/API.md",
|
||||
"lib-dist/index.js",
|
||||
"lib-dist/types/index.d.ts",
|
||||
]) {
|
||||
if (!packedPaths.has(required))
|
||||
throw new Error(`npm package is missing required file: ${required}`);
|
||||
}
|
||||
const staged = path.join(staging, filename);
|
||||
const archive = await readFile(staged);
|
||||
const digest = createHash("sha256").update(archive).digest("hex");
|
||||
const tempOutput = `${output}.new`;
|
||||
const tempChecksum = `${checksumOutput}.new`;
|
||||
await copyFile(staged, tempOutput);
|
||||
await writeFile(tempChecksum, `${digest} ${filename}\n`, { mode: 0o644 });
|
||||
if (force) {
|
||||
await rm(output, { force: true });
|
||||
await rm(checksumOutput, { force: true });
|
||||
}
|
||||
await rename(tempOutput, output);
|
||||
await rename(tempChecksum, checksumOutput);
|
||||
console.log(
|
||||
`Created release/${filename} (${archive.byteLength} bytes)\nSHA-256 ${digest}`,
|
||||
);
|
||||
} finally {
|
||||
await rm(staging, { recursive: true, force: true });
|
||||
}
|
||||
Reference in New Issue
Block a user