@adp-psych/container-tools
Version:
Tools for using containers for psychology experiments
141 lines (130 loc) • 4.19 kB
JavaScript
/*
* Copyright (C) 2021 Anthony Di Pietro
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* @file Executes a system command and sends stdout/stderr to stdout/stderr.
*
* @module module:console-exec
* @author Anthony Di Pietro <anthony.dipietro@research.uwa.edu.au>
* @copyright © 2021 Anthony Di Pietro
* @license AGPL-3.0-or-later
*/
/* eslint-disable promise/avoid-new -- Promisifying Node.js streams. */
const {
'exec': callbackExec,
'execFile': callbackExecFile,
spawn,
} = require('node:child_process');
const {promisify} = require('node:util');
const exec = promisify(callbackExec);
const execFile = promisify(callbackExecFile);
/**
* Tests whether a string is `undefined`, `null`, or empty.
*
* @static
* @access protected
* @param {String} x - The string.
* @returns {Boolean} Whether the string is `undefined`, `null`, or empty.
* @example
* nilOrEmpty(''); // true
*/
const nilOrEmpty = (x) => typeof x === 'undefined' || x === null || x === '';
/**
* Strips a newline from the end of a string.
*
* If thes string does not end in a newline, this does nothing and
* simply returns the string.
*
* @static
* @access protected
* @param {String} x - The string.
* @returns {String} The string with a newline stripped from the end.
* @example
* stripNewline('abc\n'); // 'abc'
*/
const stripNewline = (x) => x.replace(/\n$/u, '');
/**
* Runs a command via the system shell and sends stdout/stderr to stdout/stderr.
*
* This uses
* [`child_process.exec()`](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback).
*
* @static
* @param {String} command - The command.
* @example
* consoleExec('ls -al'); // Executes the system command 'ls -al'.
*/
const consoleExec = async (command) => {
const {stdout, stderr} = await exec(command);
if (!nilOrEmpty(stdout)) {
console.log(stripNewline(stdout));
}
if (!nilOrEmpty(stderr)) {
console.error(stripNewline(stderr));
}
};
/**
* Runs an executable file and sends stdout/stderr to stdout/stderr.
*
* This uses
* [`child_process.execFile()`](https://nodejs.org/api/child_process.html#child_process_child_process_execfile_file_args_options_callback).
*
* @static
* @param {String} file - The executable file.
* @param {Array.<String>} args - The command arguments.
* @example
* consoleExecFile(['ls', '-al']); // Executes the system command 'ls -al'.
*/
const consoleExecFile = async (file, args) => {
const {stdout, stderr} = await execFile(file, args);
if (!nilOrEmpty(stdout)) {
console.log(stripNewline(stdout));
}
if (!nilOrEmpty(stderr)) {
console.error(stripNewline(stderr));
}
};
/**
* Runs an executable file and sends stdout/stderr to stdout/stderr.
*
* This uses
* [`child_process.spawn()`](https://nodejs.org/api/all.html#child_process_child_process_spawn_command_args_options).
*
* @async
* @static
* @param {String} file - The executable file.
* @param {Array.<String>} args - The command arguments.
* @returns {Number} The exit code of the subprocess.
* @example
* consoleSpawn(['ls', '-al']); // Executes the system command 'ls -al'.
*/
const consoleSpawn = (file, args) => new Promise((resolve, reject) => {
const subprocess = spawn(file, args);
subprocess.stdout.on('data', (data) => {
process.stdout.write(data);
});
subprocess.stderr.on('data', (data) => {
process.stderr.write(data);
});
subprocess.on('close', (code) => (
code === 0 ? resolve(code) : reject(code)
));
});
module.exports = {
consoleExec,
consoleExecFile,
consoleSpawn,
};