oclif
Version:
oclif: create your own CLI
115 lines (114 loc) • 5.78 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@oclif/core");
const fs = require("fs-extra");
const _ = require("lodash");
const path = require("path");
const Tarballs = require("../../tarballs");
const upload_util_1 = require("../../upload-util");
const child_process_1 = require("child_process");
const node_util_1 = require("node:util");
const exec = (0, node_util_1.promisify)(child_process_1.exec);
const scripts = {
/* eslint-disable no-useless-escape */
bin: (config) => `#!/usr/bin/env bash
set -e
echoerr() { echo "$@" 1>&2; }
get_script_dir () {
SOURCE="\${BASH_SOURCE[0]}"
# While \$SOURCE is a symlink, resolve it
while [ -h "\$SOURCE" ]; do
DIR="\$( cd -P "\$( dirname "\$SOURCE" )" && pwd )"
SOURCE="\$( readlink "\$SOURCE" )"
# If \$SOURCE was a relative symlink (so no "/" as prefix, need to resolve it relative to the symlink base directory
[[ \$SOURCE != /* ]] && SOURCE="\$DIR/\$SOURCE"
done
DIR="\$( cd -P "\$( dirname "\$SOURCE" )" && pwd )"
echo "\$DIR"
}
DIR=\$(get_script_dir)
export ${config.scopedEnvVarKey('UPDATE_INSTRUCTIONS')}="update with \\"sudo apt update && sudo apt install ${config.bin}\\""
\$DIR/node \$DIR/run "\$@"
`,
/* eslint-enable no-useless-escape */
control: (config, arch) => `Package: ${config.config.bin}
Version: ${(0, upload_util_1.debVersion)(config)}
Section: main
Priority: standard
Architecture: ${arch}
Maintainer: ${config.config.scopedEnvVar('AUTHOR') || config.config.pjson.author}
Description: ${config.config.pjson.description}
`,
ftparchive: (config) => `
APT::FTPArchive::Release {
Origin "${config.scopedEnvVar('AUTHOR') || config.pjson.author}";
Suite "stable";
`,
};
class PackDeb extends core_1.Command {
async run() {
if (process.platform !== 'linux')
throw new Error('debian packing must be run on linux');
const { flags } = await this.parse(PackDeb);
const buildConfig = await Tarballs.buildConfig(flags.root);
const { config } = buildConfig;
await Tarballs.build(buildConfig, { platform: 'linux', pack: false, tarball: flags.tarball, parallel: true });
const dist = buildConfig.dist('deb');
await fs.emptyDir(dist);
const build = async (arch) => {
this.log(`building debian / ${arch}`);
const target = { platform: 'linux', arch };
const versionedDebBase = (0, upload_util_1.templateShortKey)('deb', { bin: config.bin, versionShaRevision: (0, upload_util_1.debVersion)(buildConfig), arch: (0, upload_util_1.debArch)(arch) });
const workspace = path.join(buildConfig.tmp, 'apt', versionedDebBase.replace('.deb', '.apt'));
await fs.remove(workspace);
await Promise.all([
fs.promises.mkdir(path.join(workspace, 'DEBIAN'), { recursive: true }),
fs.promises.mkdir(path.join(workspace, 'usr', 'bin'), { recursive: true }),
]);
await fs.copy(buildConfig.workspace(target), path.join(workspace, 'usr', 'lib', config.dirname));
await Promise.all([
// usr/lib/oclif/bin/oclif (the executable)
fs.promises.writeFile(path.join(workspace, 'usr', 'lib', config.dirname, 'bin', config.bin), scripts.bin(config), { mode: 0o755 }),
fs.promises.writeFile(path.join(workspace, 'DEBIAN', 'control'), scripts.control(buildConfig, (0, upload_util_1.debArch)(arch))),
]);
// symlink usr/bin/oclif points to usr/lib/oclif/bin/oclif
await exec(`ln -s "${path.join('..', 'lib', config.dirname, 'bin', config.bin)}" "${config.bin}"`, { cwd: path.join(workspace, 'usr', 'bin') });
await exec(`sudo chown -R root "${workspace}"`);
await exec(`sudo chgrp -R root "${workspace}"`);
await exec(`dpkg --build "${workspace}" "${path.join(dist, versionedDebBase)}"`);
this.log(`finished building debian / ${arch}`);
};
const arches = _.uniq(buildConfig.targets
.filter(t => t.platform === 'linux')
.map(t => t.arch));
await Promise.all(arches.map(a => build(a)));
await exec('apt-ftparchive packages . > Packages', { cwd: dist });
this.log('debian packages created');
await Promise.all([
exec('gzip -c Packages > Packages.gz', { cwd: dist }),
exec('bzip2 -k Packages', { cwd: dist }),
exec('xz -k Packages', { cwd: dist }),
packForFTP(buildConfig, config, dist),
]);
this.log('debian packages archived');
const gpgKey = config.scopedEnvVar('DEB_KEY');
if (gpgKey) {
this.log('adding gpg signatures to Release');
await exec(`gpg --digest-algo SHA512 --clearsign -u ${gpgKey} -o InRelease Release`, { cwd: dist });
await exec(`gpg --digest-algo SHA512 -abs -u ${gpgKey} -o Release.gpg Release`, { cwd: dist });
}
this.log('debian packing complete');
}
}
exports.default = PackDeb;
PackDeb.description = 'pack CLI into debian package';
PackDeb.flags = {
root: core_1.Flags.string({ char: 'r', description: 'path to oclif CLI root', default: '.', required: true }),
tarball: core_1.Flags.string({ char: 't', description: 'optionally specify a path to a tarball already generated by NPM', required: false }),
};
async function packForFTP(buildConfig, config, dist) {
const ftparchive = path.join(buildConfig.tmp, 'apt', 'apt-ftparchive.conf');
await fs.promises.mkdir(path.basename(ftparchive), { recursive: true });
await fs.writeFile(ftparchive, scripts.ftparchive(config));
await exec(`apt-ftparchive -c "${ftparchive}" release . > Release`, { cwd: dist });
}