Improve use of Typescript (#474)
* Switch out needle with node-fetch * Iterate * Update asar package and switch to canonical name * Use ts-node for scripts * Iterate * Update yarn.lock * Use node:stream.promises * Remove logfile * Fix types * Fix types
This commit is contained in:
committed by
GitHub
parent
b52787a49e
commit
56370de568
237
scripts/fetch-package.ts
Normal file
237
scripts/fetch-package.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env -S npx ts-node --resolveJsonModule
|
||||
|
||||
import * as path from "path";
|
||||
import { createWriteStream, promises as fs } from "fs";
|
||||
import * as childProcess from "child_process";
|
||||
import tar from "tar";
|
||||
import * as asar from "asar";
|
||||
import fetch from "node-fetch";
|
||||
import { promises as stream } from "stream";
|
||||
|
||||
import riotDesktopPackageJson from "../package.json";
|
||||
import { setPackageVersion } from "./set-version";
|
||||
|
||||
const PUB_KEY_URL = "https://packages.riot.im/element-release-key.asc";
|
||||
const PACKAGE_URL_PREFIX = "https://github.com/vector-im/element-web/releases/download/";
|
||||
const DEVELOP_TGZ_URL = "https://develop.element.io/develop.tar.gz";
|
||||
const ASAR_PATH = 'webapp.asar';
|
||||
|
||||
async function downloadToFile(url: string, filename: string): Promise<void> {
|
||||
console.log("Downloading " + url + "...");
|
||||
|
||||
try {
|
||||
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));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
try {
|
||||
await fs.unlink(filename);
|
||||
} catch (_) {}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyFile(filename: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
childProcess.execFile('gpg', ['--verify', filename + '.asc', filename], (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main(): Promise<number | undefined> {
|
||||
let verify = true;
|
||||
let importkey = false;
|
||||
let pkgDir = 'packages';
|
||||
let deployDir = 'deploys';
|
||||
let cfgDir: string | undefined;
|
||||
let targetVersion: string | undefined;
|
||||
let filename: string | undefined;
|
||||
let url: string | undefined;
|
||||
let setVersion = false;
|
||||
|
||||
while (process.argv.length > 2) {
|
||||
switch (process.argv[2]) {
|
||||
case '--noverify':
|
||||
verify = false;
|
||||
break;
|
||||
case '--importkey':
|
||||
importkey = true;
|
||||
break;
|
||||
case '--packages':
|
||||
process.argv.shift();
|
||||
pkgDir = process.argv[2];
|
||||
break;
|
||||
case '--deploys':
|
||||
process.argv.shift();
|
||||
deployDir = process.argv[2];
|
||||
break;
|
||||
case '--cfgdir':
|
||||
case '-d':
|
||||
process.argv.shift();
|
||||
cfgDir = process.argv[2];
|
||||
break;
|
||||
default:
|
||||
targetVersion = process.argv[2];
|
||||
}
|
||||
process.argv.shift();
|
||||
}
|
||||
|
||||
if (targetVersion === undefined) {
|
||||
targetVersion = 'v' + riotDesktopPackageJson.version;
|
||||
} else if (targetVersion !== 'develop') {
|
||||
setVersion = true; // version was specified
|
||||
}
|
||||
|
||||
if (targetVersion === 'develop') {
|
||||
filename = 'develop.tar.gz';
|
||||
url = DEVELOP_TGZ_URL;
|
||||
verify = false; // develop builds aren't signed
|
||||
} else if (targetVersion.includes("://")) {
|
||||
filename = targetVersion.substring(targetVersion.lastIndexOf("/") + 1);
|
||||
url = targetVersion;
|
||||
verify = false; // manually verified
|
||||
} else {
|
||||
filename = `element-${targetVersion}.tar.gz`;
|
||||
url = PACKAGE_URL_PREFIX + targetVersion + '/' + filename;
|
||||
}
|
||||
|
||||
const haveGpg = await new Promise<boolean>((resolve) => {
|
||||
childProcess.execFile('gpg', ['--version'], (error) => {
|
||||
resolve(!error);
|
||||
});
|
||||
});
|
||||
|
||||
if (importkey) {
|
||||
if (!haveGpg) {
|
||||
console.log("Can't import key without working GPG binary: install GPG and try again");
|
||||
return 1;
|
||||
}
|
||||
|
||||
await new Promise<boolean>((resolve) => {
|
||||
const gpgProc = childProcess.execFile('gpg', ['--import'], (error) => {
|
||||
if (error) {
|
||||
console.log("Failed to import key", error);
|
||||
} else {
|
||||
console.log("Key imported!");
|
||||
}
|
||||
resolve(!error);
|
||||
});
|
||||
fetch(PUB_KEY_URL).then(resp => {
|
||||
stream.pipeline(resp.body, gpgProc.stdin!);
|
||||
});
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (verify && !haveGpg) {
|
||||
console.log("No working GPG binary: install GPG or pass --noverify to skip verification");
|
||||
return 1;
|
||||
}
|
||||
|
||||
let haveDeploy = false;
|
||||
let expectedDeployDir = path.join(deployDir, path.basename(filename).replace(/\.tar\.gz/, ''));
|
||||
try {
|
||||
await fs.opendir(expectedDeployDir);
|
||||
console.log(expectedDeployDir + "already exists");
|
||||
haveDeploy = true;
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
if (!haveDeploy) {
|
||||
const outPath = path.join(pkgDir, filename);
|
||||
try {
|
||||
await fs.stat(outPath);
|
||||
console.log("Already have " + filename + ": not redownloading");
|
||||
} catch (e) {
|
||||
try {
|
||||
await downloadToFile(url, outPath);
|
||||
} catch (e) {
|
||||
console.log("Failed to download " + url, e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (verify) {
|
||||
try {
|
||||
await fs.stat(outPath+'.asc');
|
||||
console.log("Already have " + filename + ".asc: not redownloading");
|
||||
} catch (e) {
|
||||
try {
|
||||
await downloadToFile(url + '.asc', outPath + '.asc');
|
||||
} 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,
|
||||
onentry: entry => {
|
||||
// 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);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await fs.stat(ASAR_PATH);
|
||||
console.log(ASAR_PATH + " already present: removing");
|
||||
await fs.unlink(ASAR_PATH);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
if (cfgDir.length) {
|
||||
const configJsonSource = path.join(cfgDir, 'config.json');
|
||||
const configJsonDest = path.join(expectedDeployDir, 'config.json');
|
||||
console.log(configJsonSource + ' -> ' + configJsonDest);
|
||||
await fs.copyFile(configJsonSource, configJsonDest);
|
||||
} else {
|
||||
console.log("Skipping config file");
|
||||
}
|
||||
|
||||
console.log("Pack " + expectedDeployDir + " -> " + ASAR_PATH);
|
||||
await asar.createPackage(expectedDeployDir, ASAR_PATH);
|
||||
|
||||
if (setVersion) {
|
||||
const semVer = (await fs.readFile(path.join(expectedDeployDir, "version"), "utf-8")).trim();
|
||||
console.log("Updating version to " + semVer);
|
||||
await setPackageVersion(semVer);
|
||||
}
|
||||
|
||||
console.log("Done!");
|
||||
}
|
||||
|
||||
main().then((ret) => {
|
||||
process.exit(ret);
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user