UNPKG

6.03 kBJavaScriptView Raw
1'use strict';
2
3const childProcess = require('child_process');
4
5/**
6 * Absolute path to the sentry-cli binary (platform dependent).
7 * @type {string}
8 */
9let binaryPath = eval(
10 "require('path').resolve(__dirname, require('os').platform() === 'win32' ? '..\\sentry-cli.exe' : '../sentry-cli')"
11);
12
13/**
14 * NOTE: `eval` usage is a workaround for @vercel/nft detecting the binary itself as the hard dependency
15 * and effectively always including it in the bundle, which is not what we want.
16 * ref: https://github.com/getsentry/sentry-javascript/issues/3865
17 * ref: https://github.com/vercel/nft/issues/203
18 */
19
20/**
21 * Overrides the default binary path with a mock value, useful for testing.
22 *
23 * @param {string} mockPath The new path to the mock sentry-cli binary
24 */
25function mockBinaryPath(mockPath) {
26 binaryPath = mockPath;
27}
28
29/**
30 * The javascript type of a command line option.
31 * @typedef {'array'|'string'|'boolean'|'inverted-boolean'} OptionType
32 */
33
34/**
35 * Schema definition of a command line option.
36 * @typedef {object} OptionSchema
37 * @prop {string} param The flag of the command line option including dashes.
38 * @prop {OptionType} type The value type of the command line option.
39 */
40
41/**
42 * Schema definition for a command.
43 * @typedef {Object.<string, OptionSchema>} OptionsSchema
44 */
45
46/**
47 * Serializes command line options into an arguments array.
48 *
49 * @param {OptionsSchema} schema An options schema required by the command.
50 * @param {object} options An options object according to the schema.
51 * @returns {string[]} An arguments array that can be passed via command line.
52 */
53function serializeOptions(schema, options) {
54 return Object.keys(schema).reduce((newOptions, option) => {
55 const paramValue = options[option];
56 if (paramValue === undefined) {
57 return newOptions;
58 }
59
60 const paramType = schema[option].type;
61 const paramName = schema[option].param;
62
63 if (paramType === 'array') {
64 if (!Array.isArray(paramValue)) {
65 throw new Error(`${option} should be an array`);
66 }
67
68 return newOptions.concat(
69 paramValue.reduce((acc, value) => acc.concat([paramName, String(value)]), [])
70 );
71 }
72
73 if (paramType === 'boolean') {
74 if (typeof paramValue !== 'boolean') {
75 throw new Error(`${option} should be a bool`);
76 }
77
78 const invertedParamName = schema[option].invertedParam;
79
80 if (paramValue && paramName !== undefined) {
81 return newOptions.concat([paramName]);
82 }
83
84 if (!paramValue && invertedParamName !== undefined) {
85 return newOptions.concat([invertedParamName]);
86 }
87
88 return newOptions;
89 }
90
91 return newOptions.concat(paramName, paramValue);
92 }, []);
93}
94
95/**
96 * Serializes the command and its options into an arguments array.
97 *
98 * @param {string} command The literal name of the command.
99 * @param {OptionsSchema} [schema] An options schema required by the command.
100 * @param {object} [options] An options object according to the schema.
101 * @returns {string[]} An arguments array that can be passed via command line.
102 */
103function prepareCommand(command, schema, options) {
104 return command.concat(serializeOptions(schema || {}, options || {}));
105}
106
107/**
108 * Returns the absolute path to the `sentry-cli` binary.
109 * @returns {string}
110 */
111function getPath() {
112 return binaryPath;
113}
114
115/**
116 * Runs `sentry-cli` with the given command line arguments.
117 *
118 * Use {@link prepareCommand} to specify the command and add arguments for command-
119 * specific options. For top-level options, use {@link serializeOptions} directly.
120 *
121 * The returned promise resolves with the standard output of the command invocation
122 * including all newlines. In order to parse this output, be sure to trim the output
123 * first.
124 *
125 * If the command failed to execute, the Promise rejects with the error returned by the
126 * CLI. This error includes a `code` property with the process exit status.
127 *
128 * @example
129 * const output = await execute(['--version']);
130 * expect(output.trim()).toBe('sentry-cli x.y.z');
131 *
132 * @param {string[]} args Command line arguments passed to `sentry-cli`.
133 * @param {boolean} live We inherit stdio to display `sentry-cli` output directly.
134 * @param {boolean} silent Disable stdout for silents build (CI/Webpack Stats, ...)
135 * @param {string} [configFile] Relative or absolute path to the configuration file.
136 * @param {Object} [config] More configuration to pass to the CLI
137 * @returns {Promise.<string>} A promise that resolves to the standard output.
138 */
139async function execute(args, live, silent, configFile, config = {}) {
140 const env = { ...process.env };
141 if (configFile) {
142 env.SENTRY_PROPERTIES = configFile;
143 }
144 if (config.url) {
145 env.SENTRY_URL = config.url;
146 }
147 if (config.authToken) {
148 env.SENTRY_AUTH_TOKEN = config.authToken;
149 }
150 if (config.apiKey) {
151 env.SENTRY_API_KEY = config.apiKey;
152 }
153 if (config.dsn) {
154 env.SENTRY_DSN = config.dsn;
155 }
156 if (config.org) {
157 env.SENTRY_ORG = config.org;
158 }
159 if (config.project) {
160 env.SENTRY_PROJECT = config.project;
161 }
162 if (config.vcsRemote) {
163 env.SENTRY_VCS_REMOTE = config.vcsRemote;
164 }
165 if (config.customHeader) {
166 env.CUSTOM_HEADER = config.customHeader;
167 }
168 return new Promise((resolve, reject) => {
169 if (live === true) {
170 const output = silent ? 'ignore' : 'inherit';
171 const pid = childProcess.spawn(getPath(), args, {
172 env,
173 // stdin, stdout, stderr
174 stdio: ['ignore', output, output],
175 });
176 pid.on('exit', () => {
177 resolve();
178 });
179 } else {
180 childProcess.execFile(getPath(), args, { env }, (err, stdout) => {
181 if (err) {
182 reject(err);
183 } else {
184 resolve(stdout);
185 }
186 });
187 }
188 });
189}
190
191function getProjectFlagsFromOptions({ projects = [] } = {}) {
192 return projects.reduce((flags, project) => flags.concat('-p', project), []);
193}
194
195module.exports = {
196 execute,
197 getPath,
198 getProjectFlagsFromOptions,
199 mockBinaryPath,
200 prepareCommand,
201 serializeOptions,
202};