1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
import { getRelease, uploadAsset } from "../src/github";
import { fetch } from "../src/fetch";
import { spawn } from "../src/spawn";
import { confirm, exit, log, stdin, warn } from "../src/console";
import { hash, join, rm, tmp, write, basename, blob } from "../src/fs";
const [tag, ...paths] = process.argv.slice(2);
if (!tag) {
exit("Invalid arguments: [tag] [...assets]");
}
const { tag_name, assets } = await getRelease(tag);
log("Release:", tag_name, "\n");
log("Existing assets:\n", ...assets.map(({ name }) => `- ${name}\n`));
log("Updating assets:\n", ...paths.map((path) => `+ ${basename(path)}\n`));
await confirm();
log("Hashing assets...\n");
const existing: Map<string, string> = new Map();
for (const { name, browser_download_url } of assets) {
if (name.startsWith("SHASUMS256.txt")) {
continue;
}
const response = await fetch(browser_download_url);
const buffer = Buffer.from(await response.arrayBuffer());
existing.set(name, await hash(buffer));
}
const updated: Map<string, string> = new Map();
for (const path of paths) {
const name = basename(path);
updated.set(name, await hash(path));
}
log(
"Unchanged hashes:\n",
...Array.from(existing.entries())
.filter(([name]) => !updated.has(name))
.map(([name, sha256]) => ` - ${sha256} => ${name}\n`),
);
log(
"Changed hashes:\n",
...Array.from(updated.entries()).map(
([name, sha256]) => ` + ${sha256} => ${name}\n`,
),
);
await confirm();
log("Signing assets...\n");
const cwd = tmp();
const path = join(cwd, "SHASUMS256.txt");
const signedPath = `${path}.asc`;
write(
path,
[
...Array.from(updated.entries()),
...Array.from(existing.entries()).filter(([name]) => !updated.has(name)),
]
.sort(([a], [b]) => a.localeCompare(b))
.map(([name, sha256]) => `${sha256} ${name}`)
.join("\n"),
);
const { stdout: keys } = spawn("gpg", [
"--list-secret-keys",
"--keyid-format",
"long",
]);
const verifiedKeys = [
"F3DCC08A8572C0749B3E18888EAB4D40A7B22B59", // robobun@oven.sh
];
if (!verifiedKeys.find((key) => keys.includes(key))) {
warn("Signature is probably wrong, key not found: robobun@oven.sh");
}
const passphrase = await stdin("Passphrase:");
log();
const { exitCode, stdout, stderr } = spawn(
"gpg",
[
"--pinentry-mode",
"loopback",
"--passphrase-fd",
"0",
"--clearsign",
"--output",
signedPath,
path,
],
{
// @ts-ignore
input: passphrase,
stdout: "inherit",
stderr: "inherit",
},
);
if (exitCode !== 0) {
exit(stdout || stderr);
}
const uploads = [...paths, path, signedPath];
log("Uploading assets:\n", ...uploads.map((path) => ` + ${basename(path)}\n`));
await confirm();
for (const path of uploads) {
const name = basename(path);
await uploadAsset(tag_name, name, blob(path));
}
try {
rm(cwd);
} catch {
warn("Failed to cleanup:", cwd, "\n");
}
log("Done");
process.exit(0); // FIXME
|