///
import { SHA256, which, write, spawnSync } from "bun";
import { isatty } from "node:tty";
import { createInterface } from "node:readline";
const tag = process.argv[2];
const url = tag
? `https://api.github.com/repos/oven-sh/bun/releases/tags/${tag}`
: "https://api.github.com/repos/oven-sh/bun/releases/latest";
const response = await fetch(url);
if (response.status === 404) {
throw new Error(`Release not found: ${tag}`);
}
if (!response.ok) {
throw new Error(`Failed to find release: ${tag} [status: ${response.status}]`);
}
const release: any = await response.json();
if (release.assets.find(({ name }) => name === "SHA256SUMS.txt.asc")) {
throw new Error(`Release already signed: ${tag}`);
}
const sha256s = await Promise.all(
release.assets.map(async ({ name, browser_download_url }) => {
return `${await sha256(browser_download_url)} ${name}`;
}),
);
await write("SHASUMS256.txt", sha256s.join("\n"));
await sign("SHASUMS256.txt");
async function sha256(url: string): Promise {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to find asset: ${url} [status: ${response.status}]`);
}
const body = await response.arrayBuffer();
const sha256 = SHA256.hash(body);
return Buffer.from(sha256).toString("hex");
}
async function sign(path: string): Promise {
// https://www.gnupg.org/gph/en/manual/x135.html
if (!which("gpg")) {
throw new Error("Command not found: gpg");
}
const { stdout } = spawnSync(
[
"gpg",
"--list-secret-keys",
"--keyid-format",
"long"
],
{
stdout: "pipe",
stderr: "pipe",
}
);
if (!stdout.includes("F3DCC08A8572C0749B3E18888EAB4D40A7B22B59")) {
console.warn("Signature is likely wrong, key not found: robobun@oven.sh");
}
const passphrase = await prompt("Passphrase:");
spawnSync(
[
"gpg",
"--batch",
"--yes",
"--clearsign",
"--output",
`${path}.asc`,
path
],
{
stdin: new TextEncoder().encode(passphrase),
stdout: "inherit",
stderr: "inherit",
}
);
}
async function prompt(question: string): Promise {
if (isatty(process.stdout.fd)) {
return globalThis.prompt(question) || "";
}
const reader = createInterface({
input: process.stdin,
terminal: false
});
let buffer = "";
reader.on("line", (line) => {
buffer += line;
});
return new Promise((resolve) => {
reader.once("close", () => resolve(buffer));
});
}