2022-12-05 12:50:49 +01:00
|
|
|
#!/usr/bin/env -S npx ts-node --resolveJsonModule
|
2019-12-06 19:20:31 +01:00
|
|
|
|
2022-12-05 12:50:49 +01:00
|
|
|
import * as path from "path";
|
|
|
|
import { createWriteStream, promises as fs } from "fs";
|
|
|
|
import * as childProcess from "child_process";
|
2024-10-30 11:12:30 +01:00
|
|
|
import * as tar from "tar";
|
2023-04-06 14:41:38 +02:00
|
|
|
import * as asar from "@electron/asar";
|
2022-12-05 12:50:49 +01:00
|
|
|
import fetch from "node-fetch";
|
|
|
|
import { promises as stream } from "stream";
|
2019-12-06 19:20:31 +01:00
|
|
|
|
2022-12-05 12:50:49 +01:00
|
|
|
import riotDesktopPackageJson from "../package.json";
|
|
|
|
import { setPackageVersion } from "./set-version";
|
2019-12-06 19:20:31 +01:00
|
|
|
|
2020-11-24 13:48:37 +01:00
|
|
|
const PUB_KEY_URL = "https://packages.riot.im/element-release-key.asc";
|
2023-12-13 11:02:45 +01:00
|
|
|
const PACKAGE_URL_PREFIX = "https://github.com/element-hq/element-web/releases/download/";
|
2022-11-11 15:23:42 +01:00
|
|
|
const DEVELOP_TGZ_URL = "https://develop.element.io/develop.tar.gz";
|
2022-12-15 12:00:58 +01:00
|
|
|
const ASAR_PATH = "webapp.asar";
|
2019-12-06 19:20:31 +01:00
|
|
|
|
2022-12-05 12:50:49 +01:00
|
|
|
async function downloadToFile(url: string, filename: string): Promise<void> {
|
2019-12-06 19:20:31 +01:00
|
|
|
console.log("Downloading " + url + "...");
|
|
|
|
|
2020-02-21 19:17:31 +01:00
|
|
|
try {
|
2022-12-05 12:50:49 +01:00
|
|
|
const resp = await fetch(url);
|
|
|
|
if (!resp.ok) throw new Error(`unexpected response ${resp.statusText}`);
|
|
|
|
if (!resp.body) throw new Error(`unexpected response has no body ${resp.statusText}`);
|
|
|
|
await stream.pipeline(resp.body, createWriteStream(filename));
|
2020-02-21 19:17:31 +01:00
|
|
|
} catch (e) {
|
2022-12-05 12:50:49 +01:00
|
|
|
console.error(e);
|
2020-02-21 19:17:31 +01:00
|
|
|
try {
|
2022-12-05 12:50:49 +01:00
|
|
|
await fs.unlink(filename);
|
2024-10-16 16:59:12 +02:00
|
|
|
} catch {}
|
2019-12-06 19:20:31 +01:00
|
|
|
throw e;
|
2020-02-21 19:17:31 +01:00
|
|
|
}
|
2019-12-06 19:20:31 +01:00
|
|
|
}
|
|
|
|
|
2022-12-05 12:50:49 +01:00
|
|
|
async function verifyFile(filename: string): Promise<void> {
|
|
|
|
return new Promise<void>((resolve, reject) => {
|
2022-12-15 12:00:58 +01:00
|
|
|
childProcess.execFile("gpg", ["--verify", filename + ".asc", filename], (error) => {
|
2019-12-06 19:20:31 +01:00
|
|
|
if (error) {
|
|
|
|
reject(error);
|
|
|
|
} else {
|
|
|
|
resolve();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-12-05 12:50:49 +01:00
|
|
|
async function main(): Promise<number | undefined> {
|
2019-12-06 19:20:31 +01:00
|
|
|
let verify = true;
|
|
|
|
let importkey = false;
|
2022-12-15 12:00:58 +01:00
|
|
|
let pkgDir = "packages";
|
|
|
|
let deployDir = "deploys";
|
2022-12-05 12:50:49 +01:00
|
|
|
let cfgDir: string | undefined;
|
|
|
|
let targetVersion: string | undefined;
|
|
|
|
let filename: string | undefined;
|
|
|
|
let url: string | undefined;
|
2020-02-27 18:30:00 +01:00
|
|
|
let setVersion = false;
|
2019-12-06 19:20:31 +01:00
|
|
|
|
|
|
|
while (process.argv.length > 2) {
|
|
|
|
switch (process.argv[2]) {
|
2022-12-15 12:00:58 +01:00
|
|
|
case "--noverify":
|
2019-12-06 19:20:31 +01:00
|
|
|
verify = false;
|
|
|
|
break;
|
2022-12-15 12:00:58 +01:00
|
|
|
case "--importkey":
|
2019-12-06 19:20:31 +01:00
|
|
|
importkey = true;
|
|
|
|
break;
|
2022-12-15 12:00:58 +01:00
|
|
|
case "--packages":
|
2019-12-06 19:20:31 +01:00
|
|
|
process.argv.shift();
|
|
|
|
pkgDir = process.argv[2];
|
|
|
|
break;
|
2022-12-15 12:00:58 +01:00
|
|
|
case "--deploys":
|
2019-12-06 19:20:31 +01:00
|
|
|
process.argv.shift();
|
|
|
|
deployDir = process.argv[2];
|
|
|
|
break;
|
2022-12-15 12:00:58 +01:00
|
|
|
case "--cfgdir":
|
|
|
|
case "-d":
|
2019-12-13 12:13:44 +01:00
|
|
|
process.argv.shift();
|
|
|
|
cfgDir = process.argv[2];
|
|
|
|
break;
|
2019-12-06 19:20:31 +01:00
|
|
|
default:
|
|
|
|
targetVersion = process.argv[2];
|
|
|
|
}
|
|
|
|
process.argv.shift();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (targetVersion === undefined) {
|
2022-12-15 12:00:58 +01:00
|
|
|
targetVersion = "v" + riotDesktopPackageJson.version;
|
|
|
|
} else if (targetVersion !== "develop") {
|
2022-09-05 11:49:39 +02:00
|
|
|
setVersion = true; // version was specified
|
|
|
|
}
|
|
|
|
|
2022-12-15 12:00:58 +01:00
|
|
|
if (targetVersion === "develop") {
|
|
|
|
filename = "develop.tar.gz";
|
2022-09-05 11:49:39 +02:00
|
|
|
url = DEVELOP_TGZ_URL;
|
2020-02-21 19:17:31 +01:00
|
|
|
verify = false; // develop builds aren't signed
|
2022-09-23 13:14:09 +02:00
|
|
|
} else if (targetVersion.includes("://")) {
|
|
|
|
filename = targetVersion.substring(targetVersion.lastIndexOf("/") + 1);
|
|
|
|
url = targetVersion;
|
|
|
|
verify = false; // manually verified
|
2020-02-21 19:17:31 +01:00
|
|
|
} else {
|
2022-09-23 13:14:09 +02:00
|
|
|
filename = `element-${targetVersion}.tar.gz`;
|
2022-12-15 12:00:58 +01:00
|
|
|
url = PACKAGE_URL_PREFIX + targetVersion + "/" + filename;
|
2019-12-06 19:20:31 +01:00
|
|
|
}
|
|
|
|
|
2022-12-05 12:50:49 +01:00
|
|
|
const haveGpg = await new Promise<boolean>((resolve) => {
|
2022-12-15 12:00:58 +01:00
|
|
|
childProcess.execFile("gpg", ["--version"], (error) => {
|
2019-12-06 19:20:31 +01:00
|
|
|
resolve(!error);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
if (importkey) {
|
|
|
|
if (!haveGpg) {
|
|
|
|
console.log("Can't import key without working GPG binary: install GPG and try again");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2024-06-12 18:17:24 +02:00
|
|
|
await new Promise<boolean>((resolve, reject) => {
|
2022-12-15 12:00:58 +01:00
|
|
|
const gpgProc = childProcess.execFile("gpg", ["--import"], (error) => {
|
2019-12-06 19:20:31 +01:00
|
|
|
if (error) {
|
|
|
|
console.log("Failed to import key", error);
|
|
|
|
} else {
|
|
|
|
console.log("Key imported!");
|
|
|
|
}
|
|
|
|
resolve(!error);
|
|
|
|
});
|
2024-06-12 18:17:24 +02:00
|
|
|
fetch(PUB_KEY_URL)
|
|
|
|
.then((resp) => {
|
|
|
|
stream.pipeline(resp.body, gpgProc.stdin!).catch(reject);
|
|
|
|
})
|
|
|
|
.catch(reject);
|
2019-12-06 19:20:31 +01:00
|
|
|
});
|
2019-12-10 15:50:57 +01:00
|
|
|
return 0;
|
2019-12-06 19:20:31 +01:00
|
|
|
}
|
2019-12-13 12:13:44 +01:00
|
|
|
|
|
|
|
if (cfgDir === undefined) {
|
|
|
|
console.log("No config directory set");
|
|
|
|
console.log("Specify a config directory with --cfgdir or -d");
|
|
|
|
console.log("To build with no config (and no auto-update), pass the empty string (-d '')");
|
|
|
|
return 1;
|
|
|
|
}
|
2020-02-17 15:49:26 +01:00
|
|
|
|
2019-12-06 19:20:31 +01:00
|
|
|
if (verify && !haveGpg) {
|
|
|
|
console.log("No working GPG binary: install GPG or pass --noverify to skip verification");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-02-17 15:49:26 +01:00
|
|
|
let haveDeploy = false;
|
2022-12-15 12:00:58 +01:00
|
|
|
let expectedDeployDir = path.join(deployDir, path.basename(filename).replace(/\.tar\.gz/, ""));
|
2019-12-06 19:20:31 +01:00
|
|
|
try {
|
2020-02-17 15:49:26 +01:00
|
|
|
await fs.opendir(expectedDeployDir);
|
2019-12-06 19:20:31 +01:00
|
|
|
console.log(expectedDeployDir + "already exists");
|
|
|
|
haveDeploy = true;
|
2024-10-16 16:59:12 +02:00
|
|
|
} catch {}
|
2019-12-06 19:20:31 +01:00
|
|
|
|
|
|
|
if (!haveDeploy) {
|
|
|
|
const outPath = path.join(pkgDir, filename);
|
|
|
|
try {
|
2022-12-05 12:50:49 +01:00
|
|
|
await fs.stat(outPath);
|
2019-12-06 19:20:31 +01:00
|
|
|
console.log("Already have " + filename + ": not redownloading");
|
2024-10-16 16:59:12 +02:00
|
|
|
} catch {
|
2019-12-06 19:20:31 +01:00
|
|
|
try {
|
|
|
|
await downloadToFile(url, outPath);
|
|
|
|
} catch (e) {
|
|
|
|
console.log("Failed to download " + url, e);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verify) {
|
|
|
|
try {
|
2022-12-15 12:00:58 +01:00
|
|
|
await fs.stat(outPath + ".asc");
|
2019-12-06 19:20:31 +01:00
|
|
|
console.log("Already have " + filename + ".asc: not redownloading");
|
2024-10-16 16:59:12 +02:00
|
|
|
} catch {
|
2019-12-06 19:20:31 +01:00
|
|
|
try {
|
2022-12-15 12:00:58 +01:00
|
|
|
await downloadToFile(url + ".asc", outPath + ".asc");
|
2019-12-06 19:20:31 +01:00
|
|
|
} catch (e) {
|
|
|
|
console.log("Failed to download " + url, e);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
await verifyFile(outPath);
|
|
|
|
console.log(outPath + " downloaded and verified");
|
|
|
|
} catch (e) {
|
|
|
|
console.log("Signature verification failed!", e);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
console.log(outPath + " downloaded but NOT verified");
|
|
|
|
}
|
|
|
|
|
|
|
|
await tar.x({
|
|
|
|
file: outPath,
|
|
|
|
cwd: deployDir,
|
2022-12-15 12:00:58 +01:00
|
|
|
onentry: (entry) => {
|
2022-09-05 11:49:39 +02:00
|
|
|
// Find the appropriate extraction path, only needed for `develop` where the dir name is unknown
|
|
|
|
if (entry.type === "Directory" && !path.join(deployDir, entry.path).startsWith(expectedDeployDir)) {
|
|
|
|
expectedDeployDir = path.join(deployDir, entry.path);
|
|
|
|
}
|
|
|
|
},
|
2019-12-06 19:20:31 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-12-10 16:54:17 +01:00
|
|
|
try {
|
2022-12-05 12:50:49 +01:00
|
|
|
await fs.stat(ASAR_PATH);
|
2019-12-10 16:54:17 +01:00
|
|
|
console.log(ASAR_PATH + " already present: removing");
|
2022-12-05 12:50:49 +01:00
|
|
|
await fs.unlink(ASAR_PATH);
|
2024-10-16 16:59:12 +02:00
|
|
|
} catch {}
|
2019-12-10 16:54:17 +01:00
|
|
|
|
2019-12-13 12:13:44 +01:00
|
|
|
if (cfgDir.length) {
|
2022-12-15 12:00:58 +01:00
|
|
|
const configJsonSource = path.join(cfgDir, "config.json");
|
|
|
|
const configJsonDest = path.join(expectedDeployDir, "config.json");
|
|
|
|
console.log(configJsonSource + " -> " + configJsonDest);
|
2022-12-05 12:50:49 +01:00
|
|
|
await fs.copyFile(configJsonSource, configJsonDest);
|
2019-12-13 12:13:44 +01:00
|
|
|
} else {
|
|
|
|
console.log("Skipping config file");
|
|
|
|
}
|
|
|
|
|
2019-12-10 16:54:17 +01:00
|
|
|
console.log("Pack " + expectedDeployDir + " -> " + ASAR_PATH);
|
|
|
|
await asar.createPackage(expectedDeployDir, ASAR_PATH);
|
2020-02-26 12:56:59 +01:00
|
|
|
|
2020-02-27 18:30:00 +01:00
|
|
|
if (setVersion) {
|
2022-12-05 12:50:49 +01:00
|
|
|
const semVer = (await fs.readFile(path.join(expectedDeployDir, "version"), "utf-8")).trim();
|
2020-02-27 18:30:00 +01:00
|
|
|
console.log("Updating version to " + semVer);
|
2020-02-26 12:56:59 +01:00
|
|
|
await setPackageVersion(semVer);
|
|
|
|
}
|
|
|
|
|
2019-12-10 16:54:17 +01:00
|
|
|
console.log("Done!");
|
2019-12-06 19:20:31 +01:00
|
|
|
}
|
|
|
|
|
2022-12-15 12:00:58 +01:00
|
|
|
main()
|
|
|
|
.then((ret) => {
|
|
|
|
process.exit(ret);
|
|
|
|
})
|
|
|
|
.catch((e) => {
|
|
|
|
console.error(e);
|
|
|
|
process.exit(1);
|
|
|
|
});
|