123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- 'use strict';
- const path = require('path');
- const childProcess = require('child_process');
- /**
- * This convoluted function resolves the path to the `sentry-cli` binary in a
- * way that can't be analysed by @vercel/nft.
- *
- * Without this, the binary can be detected as an asset and included by bundlers
- * that use @vercel/nft.
- * @returns {string} The path to the sentry-cli binary
- */
- function getBinaryPath() {
- const parts = [];
- parts.push(__dirname);
- parts.push('..');
- parts.push(`sentry-cli${process.platform === 'win32' ? '.exe' : ''}`);
- return path.resolve(...parts);
- }
- /**
- * Absolute path to the sentry-cli binary (platform dependent).
- * @type {string}
- */
- let binaryPath = getBinaryPath();
- /**
- * Overrides the default binary path with a mock value, useful for testing.
- *
- * @param {string} mockPath The new path to the mock sentry-cli binary
- */
- function mockBinaryPath(mockPath) {
- binaryPath = mockPath;
- }
- /**
- * The javascript type of a command line option.
- * @typedef {'array'|'string'|'boolean'|'inverted-boolean'} OptionType
- */
- /**
- * Schema definition of a command line option.
- * @typedef {object} OptionSchema
- * @prop {string} param The flag of the command line option including dashes.
- * @prop {OptionType} type The value type of the command line option.
- */
- /**
- * Schema definition for a command.
- * @typedef {Object.<string, OptionSchema>} OptionsSchema
- */
- /**
- * Serializes command line options into an arguments array.
- *
- * @param {OptionsSchema} schema An options schema required by the command.
- * @param {object} options An options object according to the schema.
- * @returns {string[]} An arguments array that can be passed via command line.
- */
- function serializeOptions(schema, options) {
- return Object.keys(schema).reduce((newOptions, option) => {
- const paramValue = options[option];
- if (paramValue === undefined) {
- return newOptions;
- }
- const paramType = schema[option].type;
- const paramName = schema[option].param;
- if (paramType === 'array') {
- if (!Array.isArray(paramValue)) {
- throw new Error(`${option} should be an array`);
- }
- return newOptions.concat(
- paramValue.reduce((acc, value) => acc.concat([paramName, String(value)]), [])
- );
- }
- if (paramType === 'boolean') {
- if (typeof paramValue !== 'boolean') {
- throw new Error(`${option} should be a bool`);
- }
- const invertedParamName = schema[option].invertedParam;
- if (paramValue && paramName !== undefined) {
- return newOptions.concat([paramName]);
- }
- if (!paramValue && invertedParamName !== undefined) {
- return newOptions.concat([invertedParamName]);
- }
- return newOptions;
- }
- return newOptions.concat(paramName, paramValue);
- }, []);
- }
- /**
- * Serializes the command and its options into an arguments array.
- *
- * @param {string} command The literal name of the command.
- * @param {OptionsSchema} [schema] An options schema required by the command.
- * @param {object} [options] An options object according to the schema.
- * @returns {string[]} An arguments array that can be passed via command line.
- */
- function prepareCommand(command, schema, options) {
- return command.concat(serializeOptions(schema || {}, options || {}));
- }
- /**
- * Returns the absolute path to the `sentry-cli` binary.
- * @returns {string}
- */
- function getPath() {
- return binaryPath;
- }
- /**
- * Runs `sentry-cli` with the given command line arguments.
- *
- * Use {@link prepareCommand} to specify the command and add arguments for command-
- * specific options. For top-level options, use {@link serializeOptions} directly.
- *
- * The returned promise resolves with the standard output of the command invocation
- * including all newlines. In order to parse this output, be sure to trim the output
- * first.
- *
- * If the command failed to execute, the Promise rejects with the error returned by the
- * CLI. This error includes a `code` property with the process exit status.
- *
- * @example
- * const output = await execute(['--version']);
- * expect(output.trim()).toBe('sentry-cli x.y.z');
- *
- * @param {string[]} args Command line arguments passed to `sentry-cli`.
- * @param {boolean} live We inherit stdio to display `sentry-cli` output directly.
- * @param {boolean} silent Disable stdout for silents build (CI/Webpack Stats, ...)
- * @param {string} [configFile] Relative or absolute path to the configuration file.
- * @param {Object} [config] More configuration to pass to the CLI
- * @returns {Promise.<string>} A promise that resolves to the standard output.
- */
- function execute(args, live, silent, configFile, config = {}) {
- const env = { ...process.env };
- if (configFile) {
- env.SENTRY_PROPERTIES = configFile;
- }
- if (config.url) {
- env.SENTRY_URL = config.url;
- }
- if (config.authToken) {
- env.SENTRY_AUTH_TOKEN = config.authToken;
- }
- if (config.apiKey) {
- env.SENTRY_API_KEY = config.apiKey;
- }
- if (config.dsn) {
- env.SENTRY_DSN = config.dsn;
- }
- if (config.org) {
- env.SENTRY_ORG = config.org;
- }
- if (config.project) {
- env.SENTRY_PROJECT = config.project;
- }
- if (config.vcsRemote) {
- env.SENTRY_VCS_REMOTE = config.vcsRemote;
- }
- if (config.customHeader) {
- env.CUSTOM_HEADER = config.customHeader;
- }
- return new Promise((resolve, reject) => {
- if (live === true) {
- const output = silent ? 'ignore' : 'inherit';
- const pid = childProcess.spawn(getPath(), args, {
- env,
- // stdin, stdout, stderr
- stdio: ['ignore', output, output],
- });
- pid.on('exit', () => {
- resolve();
- });
- } else {
- childProcess.execFile(getPath(), args, { env }, (err, stdout) => {
- if (err) {
- reject(err);
- } else {
- resolve(stdout);
- }
- });
- }
- });
- }
- function getProjectFlagsFromOptions({ projects = [] } = {}) {
- return projects.reduce((flags, project) => flags.concat('-p', project), []);
- }
- module.exports = {
- execute,
- getPath,
- getProjectFlagsFromOptions,
- mockBinaryPath,
- prepareCommand,
- serializeOptions,
- };
|