@drovp/run
Version:
Execute one or multiple console commands on dropped items.
246 lines (245 loc) • 9.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.commonPathsRoot = void 0;
const OS = require("os");
const Path = require("path");
const CP = require("child_process");
const util_1 = require("util");
const fs_1 = require("fs");
const dayjs = require("dayjs");
const platform_paths_1 = require("platform-paths");
const expand_template_literal_1 = require("expand-template-literal");
const prettyMs = require("pretty-ms");
const exec = (0, util_1.promisify)(CP.exec);
async function default_1(payload, { stage, output }) {
var _a, _b, _c;
const { id, input, options } = payload;
const stdouts = [];
const stderrs = [];
const commonVariables = {
starttime: Date.now(),
Path,
Time: dayjs,
uid,
};
const { commands, outputs, outputMode } = options;
const inputs = [];
for (const input of payload.inputs) {
switch (input.kind) {
case 'directory':
case 'file': {
inputs.push({
type: input.kind,
payload: input.path,
...extractPathVariables(input.path),
});
break;
}
case 'string':
inputs.push({
type: input.kind,
contents: input.contents,
payload: input.contents,
});
break;
case 'url': {
const url = new URL(input.url);
inputs.push({
type: input.kind,
url: input.url,
hostname: url.hostname,
pathname: url.pathname,
username: url.username,
password: url.password,
payload: input.url,
});
break;
}
}
}
if (options.bulk) {
commonVariables.inputs = inputs;
}
else if (inputs.length > 0) {
Object.assign(commonVariables, inputs[0]);
commonVariables.type = input.kind;
}
let onlyFiles = inputs.filter((input) => input.type === 'file' || input.type === 'directory');
let commondir = ((_a = onlyFiles[0]) === null || _a === void 0 ? void 0 : _a.dirname) || 'undefined';
for (let i = 1; i < onlyFiles.length; i++)
commondir = commonPathsRoot(commondir, onlyFiles[i].path);
commonVariables.commondir = commondir;
const allTemplates = [
...commands.map(({ template }) => template),
...commands.map(({ cwd }) => cwd),
...outputs.map(({ template }) => template),
].join(';');
for (const name of Object.keys(platform_paths_1.platformPaths)) {
if (allTemplates.includes(name))
commonVariables[name] = await platform_paths_1.platformPaths[name]();
}
if (!options.parallelMode) {
commonVariables.stdouts = stdouts;
commonVariables.stderrs = stderrs;
}
const tmpDir = Path.join(OS.tmpdir(), `drovp-run-operation-${id}`);
const tmpDirIsNeeded = commands.find(({ cwd }) => cwd.trim().length === 0) != null;
if (tmpDirIsNeeded) {
console.log(`creating temporary working directory`);
console.log(`path: "${tmpDir}"`);
await fs_1.promises.mkdir(tmpDir, { recursive: true });
}
const cleanup = async () => {
if (!tmpDirIsNeeded)
return;
console.log(`deleting temporary working directory`);
await fs_1.promises.rm(tmpDir, { recursive: true });
};
const parallelPromises = [];
for (let i = 0; i < commands.length; i++) {
let { template, cwd, ignoreErrors } = commands[i];
const variables = { ...commonVariables, cwd };
if (options.parallelMode) {
variables.stdout = stdouts[stdouts.length - 1];
variables.stderr = stderrs[stderrs.length - 1];
}
else {
stage(`${i + 1}/${options.commands.length}`);
}
cwd = `${cwd}`.trim();
let cwdPath;
if (cwd.length > 0) {
try {
cwdPath = await (0, expand_template_literal_1.expandTemplateLiteral)(cwd.replace(/\r?\n/g, '').trim(), commonVariables);
}
catch (error) {
output.error(`command[${i}] cwd template error: ${eem(error)}`);
return;
}
try {
const stat = await pathExists(cwdPath);
if (!stat || stat.isDirectory())
await fs_1.promises.mkdir(cwdPath, { recursive: true });
}
catch (error) {
output.error(`command[${i}]: Error creating cwd "${cwd}"${cwd !== cwdPath ? `, expanded into "${cwdPath}":` : ''}: ${eem(error)}`);
return;
}
}
else {
cwdPath = tmpDir;
}
variables.cwd = cwdPath;
console.log(`===== COMMAND[${i}]: ==========\n${template}`);
let command;
try {
command = await (0, expand_template_literal_1.expandTemplateLiteral)(template.replace(/\s*(\^|\\)?\n\s*/g, ' ').trim(), variables).trim();
}
catch (error) {
console.log(`----- TEMPLATE ERROR: ------`);
output.error(eem(error));
console.log(`============================`);
return;
}
console.log(`----- FILLED: --------------\n${command}\n----- CWD: -----------------\n"${cwdPath}"\n============================`);
if (!command) {
output.error(`command[${i}]: template produced an empty command`);
return;
}
const commandStartTime = Date.now();
const reportTime = () => console.log(`command[${i}] time: ${prettyMs(Date.now() - commandStartTime)}`);
const resolve = ({ stdout, stderr }) => {
stdouts[i] = `${stdout}`;
stderrs[i] = `${stderr}`;
};
const reject = (error) => {
output.error(`COMMAND[${i}] failed with exit code ${error.code}.`);
};
try {
const process = exec(command, { cwd: cwdPath });
(_b = process.child.stdout) === null || _b === void 0 ? void 0 : _b.on('data', (buffer) => console.log(buffer.toString()));
(_c = process.child.stderr) === null || _c === void 0 ? void 0 : _c.on('data', (buffer) => console.log(buffer.toString()));
if (options.parallelMode) {
parallelPromises.push(process.then(resolve).catch(reject).finally(reportTime));
}
else {
const result = await process;
reportTime();
resolve(result);
}
}
catch (error) {
reject(error);
if (!ignoreErrors)
break;
}
}
if (parallelPromises.length > 0)
await Promise.all(parallelPromises);
await cleanup();
commonVariables.stdouts = stdouts;
for (let i = 0; i < options.outputs.length; i++) {
let { template, type } = outputs[i];
let payload;
try {
payload = await (0, expand_template_literal_1.expandTemplateLiteral)(template.replace(/\s*(\^|\\)?\n\s*/g, ' ').trim(), commonVariables);
}
catch (error) {
throw new Error(`output[${i}] template error: ${eem(error)}`);
}
if (payload) {
output[type](payload || '');
if (outputMode === 'first')
break;
}
else {
if (outputMode === 'all')
output.error(`output[${i}] template produced an empty string.`);
}
}
}
exports.default = default_1;
function eem(error, preferStack = false) {
return error instanceof Error ? (preferStack ? error.stack || error.message : error.message) : `${error}`;
}
const uid = (size = 10) => Array(size)
.fill(0)
.map(() => Math.floor(Math.random() * 36).toString(36))
.join('');
async function pathExists(path) {
try {
return await fs_1.promises.stat(path);
}
catch (error) {
if ((error === null || error === void 0 ? void 0 : error.code) === 'ENOENT')
return false;
throw error;
}
}
function extractPathVariables(path) {
const extname = Path.extname(path);
const dirname = Path.dirname(path);
return {
path,
extname,
ext: extname.slice(1),
dirname,
basename: Path.basename(path),
filename: Path.basename(path, extname),
dirbasename: Path.basename(dirname),
};
}
function commonPathsRoot(a, b) {
let sameParts = [];
const aParts = a.split(/[\\\/]+/);
const bParts = b.split(/[\\\/]+/);
const loopSize = Math.min(aParts.length, bParts.length);
for (let i = 0; i < loopSize; i++) {
if (aParts[i] === bParts[i])
sameParts.push(aParts[i]);
else
break;
}
return sameParts.join(Path.sep);
}
exports.commonPathsRoot = commonPathsRoot;