Replace minimist, convert build scripts to ES modules + misc refactoring

This commit is contained in:
hensm
2022-08-19 03:09:59 +01:00
parent 170b80283d
commit a186570dc8
17 changed files with 702 additions and 505 deletions

View File

@@ -1,34 +1,44 @@
"use strict";
// @ts-check
const fs = require("fs-extra");
const os = require("os");
const path = require("path");
import fs from "fs-extra";
import os from "os";
import path from "path";
import url from "url";
import { spawnSync } from "child_process";
const minimist = require("minimist");
const mustache = require("mustache");
const pkg = require("pkg");
import mustache from "mustache";
import pkg from "pkg";
import yargs from "yargs";
const { spawnSync } = require("child_process");
import config from "../config.json" assert { type: "json" };
import * as paths from "./lib/paths.js";
const meta = require("../package.json");
const paths = require("./lib/paths");
const { author, homepage } = require("../../package.json");
const EXTENSION_ID = "fx_cast@matt.tf";
// Command line args
const argv = minimist(process.argv.slice(2), {
boolean: ["usePkg", "package"],
string: ["arch", "packageType", "nodeVersion"],
default: {
arch: os.arch(),
package: false,
// Linux package type (deb/rpm)
packageType: "deb",
nodeVersion: "16"
}
});
const argv = await yargs()
.help()
.version(false)
.option("package", {
describe: "Create installer package",
type: "boolean"
})
.option("package-type", {
describe: "Linux package type",
choices: ["deb", "rpm"],
default: "deb"
})
.option("use-pkg", {
describe: "Create single binary with pkg",
type: "boolean"
})
.option("arch", {
describe: "Set build architecture",
default: os.arch()
})
.option("node-version", {
describe: "Node.js version to target",
default: "16"
})
.conflicts("use-pkg", "package")
.parse(process.argv);
const supportedTargets = {
win32: ["x86", "x64"],
@@ -45,6 +55,8 @@ if (!supportedTargets[process.platform]?.includes(argv.arch)) {
process.exit(1);
}
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const ROOT_PATH = path.join(__dirname, "..");
const BUILD_PATH = path.join(ROOT_PATH, "build");
@@ -57,10 +69,10 @@ const spawnOptions = {
* Shouldn't exist, but cleanup and re-create any existing
* build directories, just in case.
*/
fs.removeSync(BUILD_PATH);
fs.removeSync(paths.DIST_PATH, { recursive: true });
fs.ensureDirSync(BUILD_PATH);
fs.ensureDirSync(paths.DIST_PATH, { recursive: true });
fs.rmSync(BUILD_PATH, { force: true, recursive: true });
fs.rmSync(paths.DIST_PATH, { force: true, recursive: true });
fs.mkdirSync(BUILD_PATH, { recursive: true });
fs.mkdirSync(paths.DIST_PATH, { recursive: true });
const MDNS_BINDING_PATH = path.join(
__dirname,
@@ -81,10 +93,10 @@ async function build() {
* https://mdn.io/Native_manifests#Native_messaging_manifests
*/
const manifest = {
name: meta.__applicationName,
name: config.applicationName,
description: "",
type: "stdio",
allowed_extensions: [EXTENSION_ID]
allowed_extensions: [config.extensionId]
};
/**
@@ -105,7 +117,7 @@ async function build() {
};
const executableName = paths.getExecutableName(process.platform);
const executablePath = paths.getExecutablePath(
const executablePath = paths.getExecutableDirectory(
process.platform,
argv.arch
);
@@ -132,7 +144,7 @@ async function build() {
path.join(BUILD_PATH, MDNS_BINDING_NAME)
);
fs.removeSync(path.join(BUILD_PATH, "src"));
fs.rmSync(path.join(BUILD_PATH, "src"));
manifest.path =
!argv.package && argv.usePkg
@@ -141,7 +153,7 @@ async function build() {
} else {
let launcherPath = path.join(
BUILD_PATH,
meta.__applicationExecutableName
config.applicationExecutableName
);
const modulesDir = path.join(ROOT_PATH, "node_modules");
@@ -209,36 +221,42 @@ NODE_PATH="${modulesDir}" node $(dirname $0)/src/main.js --__name $(basename $0)
}
// Remove build directory
fs.removeSync(BUILD_PATH);
fs.rmSync(BUILD_PATH, { force: true, recursive: true });
}
/**
* Takes a platform and returns the path of the created
* installer package.
*
* @param {string} platform
* @param {string} arch
*/
async function packageApp(platform, arch) {
const packageFunctionArgs = [
/** @type {[ string, string, string, string ]} */
const packageFnArgs = [
arch,
// platformExecutableName
paths.getExecutableName(platform, arch),
// platformExecutablePath
paths.getExecutablePath(platform, arch),
// platformManifestPath
paths.getManifestPath(platform, arch, argv.packageType)
paths.getExecutableName(platform),
paths.getExecutableDirectory(platform, arch),
paths.getManifestDirectory(platform, arch, argv.packageType)
];
switch (platform) {
case "win32":
return packageWin32(...packageFunctionArgs);
// Pass without manifest
return packageWin32(
packageFnArgs[0],
packageFnArgs[1],
packageFnArgs[2]
);
case "darwin":
return packageDarwin(...packageFunctionArgs);
return packageDarwin(...packageFnArgs);
case "linux": {
switch (argv.packageType) {
case "deb":
return packageLinuxDeb(...packageFunctionArgs);
return packageLinuxDeb(...packageFnArgs);
case "rpm":
return packageLinuxRpm(...packageFunctionArgs);
return packageLinuxRpm(...packageFnArgs);
}
break;
@@ -258,52 +276,63 @@ async function packageApp(platform, arch) {
*
* Requires the pkgbuild and productbuild command line
* utilities. Only possible on macOS.
*
* @param {string} arch
* @param {string} platformExecutableName
* @param {string} platformExecutableDirectory
* @param {string} platformManifestDirectory
*/
function packageDarwin(
arch,
platformExecutableName,
platformExecutablePath,
platformManifestPath
platformExecutableDirectory,
platformManifestDirectory
) {
const outputName = `${meta.__applicationName}-${meta.__applicationVersion}-${arch}.pkg`;
const componentName = `${meta.__applicationName}_component.pkg`;
const outputName = `${config.applicationName}-${config.applicationVersion}-${arch}.pkg`;
const componentName = `${config.applicationName}_component.pkg`;
const packagingDir = path.join(__dirname, "../packaging/mac/");
const packagingOutputDir = path.join(BUILD_PATH, "packaging");
// Create pkgbuild root
const rootPath = path.join(BUILD_PATH, "root");
const rootExecutablePath = path.join(rootPath, platformExecutablePath);
const rootManifestPath = path.join(rootPath, platformManifestPath);
const rootExecutableDirectory = path.join(
rootPath,
platformExecutableDirectory
);
const rootManifestDirectory = path.join(
rootPath,
platformManifestDirectory
);
// Create install locations
fs.ensureDirSync(rootExecutablePath, { recursive: true });
fs.ensureDirSync(rootManifestPath, { recursive: true });
fs.mkdirSync(rootExecutableDirectory, { recursive: true });
fs.mkdirSync(rootManifestDirectory, { recursive: true });
// Move files to root
fs.moveSync(
path.join(BUILD_PATH, platformExecutableName),
path.join(rootExecutablePath, platformExecutableName)
path.join(rootExecutableDirectory, platformExecutableName)
);
fs.moveSync(
path.join(BUILD_PATH, MDNS_BINDING_NAME),
path.join(rootExecutablePath, MDNS_BINDING_NAME)
path.join(rootExecutableDirectory, MDNS_BINDING_NAME)
);
fs.moveSync(
path.join(BUILD_PATH, paths.MANIFEST_NAME),
path.join(rootManifestPath, paths.MANIFEST_NAME)
path.join(rootManifestDirectory, paths.MANIFEST_NAME)
);
// Copy static files to be processed
fs.copySync(packagingDir, packagingOutputDir);
const view = {
applicationName: meta.__applicationName,
applicationName: config.applicationName,
manifestName: paths.MANIFEST_NAME,
componentName,
packageId: `tf.matt.${meta.__applicationName}`,
executablePath: platformExecutablePath,
manifestPath: platformManifestPath
packageId: `tf.matt.${config.applicationName}`,
executablePath: platformExecutableDirectory,
manifestPath: platformManifestDirectory
};
// Template paths
@@ -321,8 +350,8 @@ function packageDarwin(
// Build component package
spawnSync(
`pkgbuild --root ${rootPath} \
--identifier "tf.matt.${meta.__applicationName}" \
--version "${meta.__applicationVersion}" \
--identifier "tf.matt.${config.applicationName}" \
--version "${config.applicationVersion}" \
--scripts ${path.join(packagingOutputDir, "scripts")} \
${path.join(BUILD_PATH, componentName)}`,
spawnOptions
@@ -350,35 +379,46 @@ function packageDarwin(
* (packaging/linux/deb/DEBIAN/control) to root, then builds
* package from root.
* Requires the dpkg-deb command line utility.
*
* @param {string} arch
* @param {string} platformExecutableName
* @param {string} platformExecutableDirectory
* @param {string} platformManifestDirectory
*/
function packageLinuxDeb(
arch,
platformExecutableName,
platformExecutablePath,
platformManifestPath
platformExecutableDirectory,
platformManifestDirectory
) {
const outputName = `${meta.__applicationName}-${meta.__applicationVersion}-${arch}.deb`;
const outputName = `${config.applicationName}-${config.applicationVersion}-${arch}.deb`;
// Create root
const rootPath = path.join(BUILD_PATH, "root");
const rootExecutablePath = path.join(rootPath, platformExecutablePath);
const rootManifestPath = path.join(rootPath, platformManifestPath);
const rootExecutableDirectory = path.join(
rootPath,
platformExecutableDirectory
);
const rootManifestDirectory = path.join(
rootPath,
platformManifestDirectory
);
fs.ensureDirSync(rootExecutablePath, { recursive: true });
fs.ensureDirSync(rootManifestPath, { recursive: true });
fs.mkdirSync(rootExecutableDirectory, { recursive: true });
fs.mkdirSync(rootManifestDirectory, { recursive: true });
// Move files to root
fs.moveSync(
path.join(BUILD_PATH, platformExecutableName),
path.join(rootExecutablePath, platformExecutableName)
path.join(rootExecutableDirectory, platformExecutableName)
);
fs.moveSync(
path.join(BUILD_PATH, MDNS_BINDING_NAME),
path.join(rootExecutablePath, MDNS_BINDING_NAME)
path.join(rootExecutableDirectory, MDNS_BINDING_NAME)
);
fs.moveSync(
path.join(BUILD_PATH, paths.MANIFEST_NAME),
path.join(rootManifestPath, paths.MANIFEST_NAME)
path.join(rootManifestDirectory, paths.MANIFEST_NAME)
);
const controlDir = path.join(__dirname, "../packaging/linux/deb/DEBIAN/");
@@ -390,10 +430,10 @@ function packageLinuxDeb(
const view = {
// Debian package names can't contain underscores
packageName: meta.__applicationName.replace(/_/g, "-"),
applicationName: meta.__applicationName,
applicationVersion: meta.__applicationVersion,
author
packageName: config.applicationName.replace(/_/g, "-"),
applicationName: config.applicationName,
applicationVersion: config.applicationVersion,
author: config.author
};
// Do templating on control file
@@ -418,14 +458,19 @@ function packageLinuxDeb(
* Templates and uses the spec file
* (packaging/linux/rpm/package.spec) to build the package.
* Requires the rpmbuild command line utility.
*
* @param {string} arch
* @param {string} platformExecutableName
* @param {string} platformExecutableDirectory
* @param {string} platformManifestDirectory
*/
function packageLinuxRpm(
arch,
platformExecutableName,
platformExecutablePath,
platformManifestPath
platformExecutableDirectory,
platformManifestDirectory
) {
const outputName = `${meta.__applicationName}-${meta.__applicationVersion}-${arch}.rpm`;
const outputName = `${config.applicationName}-${config.applicationVersion}-${arch}.rpm`;
const specPath = path.join(
__dirname,
@@ -435,11 +480,11 @@ function packageLinuxRpm(
const specOutputPath = path.join(BUILD_PATH, path.basename(specPath));
const view = {
packageName: meta.__applicationName,
applicationName: meta.__applicationName,
applicationVersion: meta.__applicationVersion,
executablePath: platformExecutablePath,
manifestPath: platformManifestPath,
packageName: config.applicationName,
applicationName: config.applicationName,
applicationVersion: config.applicationVersion,
executablePath: platformExecutableDirectory,
manifestPath: platformManifestDirectory,
executableName: platformExecutableName,
manifestName: paths.MANIFEST_NAME,
bindingName: MDNS_BINDING_NAME
@@ -470,18 +515,26 @@ function packageLinuxRpm(
* Uses NSIS to create a GUI installer with an installer
* script (packaging/win/installer.nsi). Requires the
* makensis command line utility.
*
* @param {string} arch
* @param {string} platformExecutableName
* @param {string} platformExecutableDirectory
*/
function packageWin32(arch, platformExecutableName, platformExecutablePath) {
const outputName = `${meta.__applicationName}-${meta.__applicationVersion}-${arch}.exe`;
function packageWin32(
arch,
platformExecutableName,
platformExecutableDirectory
) {
const outputName = `${config.applicationName}-${config.applicationVersion}-${arch}.exe`;
const scriptPath = path.join(__dirname, "../packaging/win/installer.nsi");
const scriptOutputPath = path.join(BUILD_PATH, path.basename(scriptPath));
const view = {
applicationName: meta.__applicationName,
applicationVersion: meta.__applicationVersion,
applicationName: config.applicationName,
applicationVersion: config.applicationVersion,
executableName: platformExecutableName,
executablePath: platformExecutablePath,
executablePath: platformExecutableDirectory,
manifestName: paths.MANIFEST_NAME,
bindingName: MDNS_BINDING_NAME,
winRegistryKey: paths.REGISTRY_KEY,
@@ -489,8 +542,8 @@ function packageWin32(arch, platformExecutableName, platformExecutablePath) {
licensePath: paths.LICENSE_PATH,
// Uninstaller keys
registryPublisher: author,
registryUrlInfoAbout: homepage
registryPublisher: config.author,
registryUrlInfoAbout: config.homepageUrl
};
// Write templated script to build dir

View File

@@ -1,69 +1,77 @@
const fs = require("fs-extra");
const os = require("os");
const path = require("path");
const minimist = require("minimist");
// @ts-check
const paths = require("./lib/paths");
import fs from "fs-extra";
import os from "os";
import path from "path";
import { spawnSync } from "child_process";
const argv = minimist(process.argv.slice(2), {
boolean: ["remove"],
default: {
remove: false
}
});
import yargs from "yargs";
const CURRENT_MANIFEST_PATH = path.join(paths.DIST_PATH, paths.MANIFEST_NAME);
import * as paths from "./lib/paths.js";
if (!fs.existsSync(CURRENT_MANIFEST_PATH) && !argv.remove) {
console.error("No manifest in dist/app/ to install.");
const argv = yargs()
.help()
.version(false)
.option("remove", {
describe: "Uninstall manifest",
type: "boolean"
})
.parseSync(process.argv);
// Path to newly-built manifest
const newManifestPath = path.join(paths.DIST_PATH, paths.MANIFEST_NAME);
if (!fs.existsSync(newManifestPath) && !argv.remove) {
console.error("Error: No manifest to install!");
process.exit(1);
}
const platform = os.platform();
const arch = os.arch();
console.info(`${argv.remove ? "Uninstalling" : "Installing"} manifest... `);
const platform = os.platform();
switch (platform) {
// File-based manifests
case "darwin":
case "linux": {
// Manifest location within home directory
const destination = path.join(
// User-specific manifest within home directory
const manifestDirectory = path.join(
os.homedir(),
platform === "linux"
? ".mozilla/native-messaging-hosts/"
: paths.getManifestPath(platform, arch)
? ".mozilla/native-messaging-hosts"
: paths.getManifestDirectory(platform, os.arch())
);
const manifestPath = path.join(manifestDirectory, paths.MANIFEST_NAME);
if (argv.remove) {
fs.remove(path.join(destination, paths.MANIFEST_NAME));
break;
// Uninstall manifest
fs.rmSync(manifestPath);
} else {
// Install manifest
fs.mkdirSync(manifestDirectory, { recursive: true });
fs.copyFileSync(newManifestPath, manifestPath);
}
// Install manifest
fs.ensureDirSync(destination);
fs.copyFileSync(
CURRENT_MANIFEST_PATH,
path.join(destination, paths.MANIFEST_NAME)
);
break;
}
case "win32": {
const { Registry } = require("rage-edit");
const REGISTRY_PATH = `HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\${paths.REGISTRY_KEY}`;
const registryKey = `HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\${paths.REGISTRY_KEY}`;
if (argv.remove) {
Registry.delete(REGISTRY_PATH);
break;
}
Registry.set(REGISTRY_PATH, "", CURRENT_MANIFEST_PATH, "REG_SZ");
// Call reg command
spawnSync(
argv.remove
? `reg delete ${registryKey} /f`
: `reg add ${registryKey} /ve /d "${newManifestPath}" /f`,
{
shell: true,
stdio: [process.stdin, process.stdout, process.stderr]
}
);
break;
}
default: {
console.error("Sorry, this installer does not yet support your OS");
default:
console.error("Error: Unsupported platform!");
process.exit(1);
}
}

View File

@@ -1,85 +1,98 @@
"use strict";
// @ts-check
const path = require("path");
import path from "path";
import url from "url";
const {
__applicationName,
__applicationDirectoryName,
__applicationExecutableName
} = require("../../package.json");
import config from "../../config.json" assert { type: "json" };
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const rootPath = path.join(__dirname, "../../../");
exports.DIST_PATH = path.join(rootPath, "dist/app");
exports.LICENSE_PATH = path.join(rootPath, "LICENSE");
export const DIST_PATH = path.join(rootPath, "dist/app");
export const LICENSE_PATH = path.join(rootPath, "LICENSE");
exports.REGISTRY_KEY = __applicationName;
export const REGISTRY_KEY = config.applicationName;
exports.pkgPlatformMap = {
export const pkgPlatformMap = {
win32: "win",
darwin: "macos",
linux: "linux"
};
exports.MANIFEST_NAME = `${__applicationName}.json`;
export const MANIFEST_NAME = `${config.applicationName}.json`;
exports.getExecutableName = platform => {
/**
* @param {string} platform
* @returns {string}
*/
export function getExecutableName(platform) {
switch (platform) {
case "win32":
return `${__applicationExecutableName}.exe`;
return `${config.applicationExecutableName}.exe`;
case "darwin":
case "linux":
return __applicationExecutableName;
return config.applicationExecutableName;
}
};
exports.getExecutablePath = (platform, arch) => {
const EXECUTABLE_PATH_WIN32_X64 = `C:\\Program Files\\${__applicationDirectoryName}\\`;
const EXECUTABLE_PATH_WIN32_X86 = `C:\\Program Files (x86)\\${__applicationDirectoryName}\\`;
const EXECUTABLE_PATH_DARWIN = `/Library/Application Support/${__applicationDirectoryName}/`;
const EXECUTABLE_PATH_LINUX = `/opt/${__applicationDirectoryName}/`;
throw new Error("No executable name for specified platform!");
}
/**
* @param {string} platform
* @param {string} arch
* @returns {string}
*/
export function getExecutableDirectory(platform, arch) {
const EXECUTABLE_DIR_WIN32_X64 = `C:\\Program Files\\${config.applicationDirectoryName}\\`;
const EXECUTABLE_DIR_WIN32_X86 = `C:\\Program Files (x86)\\${config.applicationDirectoryName}\\`;
const EXECUTABLE_DIR_DARWIN = `/Library/Application Support/${config.applicationDirectoryName}/`;
const EXECUTABLE_DIR_LINUX = `/opt/${config.applicationDirectoryName}/`;
switch (platform) {
case "win32":
switch (arch) {
case "x86":
return EXECUTABLE_PATH_WIN32_X86;
return EXECUTABLE_DIR_WIN32_X86;
case "x64":
return EXECUTABLE_PATH_WIN32_X64;
return EXECUTABLE_DIR_WIN32_X64;
}
break;
case "darwin":
return EXECUTABLE_PATH_DARWIN;
return EXECUTABLE_DIR_DARWIN;
case "linux":
return EXECUTABLE_PATH_LINUX;
return EXECUTABLE_DIR_LINUX;
}
};
exports.getManifestPath = (platform, arch, linuxPackageType) => {
const MANIFEST_PATH_DARWIN =
throw new Error("No executable directory for specified platform!");
}
/**
* @param {string} platform
* @param {string} arch
* @param {string} [linuxPackageType]
* @returns {string}
*/
export function getManifestDirectory(platform, arch, linuxPackageType) {
const MANIFEST_DIR_DARWIN =
"/Library/Application Support/Mozilla/NativeMessagingHosts/";
const MANIFEST_PATH_LINUX_DEB = "/usr/lib/mozilla/native-messaging-hosts/";
const MANIFEST_PATH_LINUX_RPM =
"/usr/lib64/mozilla/native-messaging-hosts/";
const MANIFEST_DIR_LINUX_DEB = "/usr/lib/mozilla/native-messaging-hosts/";
const MANIFEST_DIR_LINUX_RPM = "/usr/lib64/mozilla/native-messaging-hosts/";
switch (platform) {
case "win32":
switch (arch) {
case "x86":
case "x64":
return exports.getExecutablePath(platform, arch);
}
break;
return getExecutableDirectory(platform, arch);
case "darwin":
return MANIFEST_PATH_DARWIN;
return MANIFEST_DIR_DARWIN;
case "linux":
switch (linuxPackageType) {
case "deb":
return MANIFEST_PATH_LINUX_DEB;
return MANIFEST_DIR_LINUX_DEB;
case "rpm":
return MANIFEST_PATH_LINUX_RPM;
return MANIFEST_DIR_LINUX_RPM;
}
break;
}
};
throw new Error("No manifest directory for specified platform!");
}