@sentry/cli 1.77.2 → 1.77.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/install.js ADDED
@@ -0,0 +1,305 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const http = require('http');
5
+ const os = require('os');
6
+ const path = require('path');
7
+ const crypto = require('crypto');
8
+ const zlib = require('zlib');
9
+ const stream = require('stream');
10
+ const process = require('process');
11
+
12
+ const HttpsProxyAgent = require('https-proxy-agent');
13
+ const fetch = require('node-fetch');
14
+ const ProgressBar = require('progress');
15
+ const Proxy = require('proxy-from-env');
16
+ // NOTE: Can be dropped in favor of `fs.mkdirSync(path, { recursive: true })` once we stop supporting Node 8.x
17
+ const mkdirp = require('mkdirp');
18
+ const which = require('which');
19
+
20
+ const helper = require('./helper');
21
+ const pkgInfo = require('../package.json');
22
+ const Logger = require('./logger');
23
+
24
+ function getLogStream(defaultStream) {
25
+ const logStream = process.env.SENTRYCLI_LOG_STREAM || defaultStream;
26
+
27
+ if (logStream === 'stdout') {
28
+ return process.stdout;
29
+ }
30
+
31
+ if (logStream === 'stderr') {
32
+ return process.stderr;
33
+ }
34
+
35
+ throw new Error(
36
+ `Incorrect SENTRYCLI_LOG_STREAM env variable. Possible values: 'stdout' | 'stderr'`
37
+ );
38
+ }
39
+
40
+ const ttyLogger = new Logger(getLogStream('stderr'));
41
+
42
+ const CDN_URL =
43
+ process.env.SENTRYCLI_LOCAL_CDNURL ||
44
+ process.env.npm_config_sentrycli_cdnurl ||
45
+ process.env.SENTRYCLI_CDNURL ||
46
+ 'https://downloads.sentry-cdn.com/sentry-cli';
47
+
48
+ function shouldRenderProgressBar() {
49
+ const silentFlag = process.argv.some((v) => v === '--silent');
50
+ const silentConfig = process.env.npm_config_loglevel === 'silent';
51
+ // Leave `SENTRY_NO_PROGRESS_BAR` for backwards compatibility
52
+ const silentEnv = process.env.SENTRYCLI_NO_PROGRESS_BAR || process.env.SENTRY_NO_PROGRESS_BAR;
53
+ const ciEnv = process.env.CI === 'true';
54
+ // If any of possible options is set, skip rendering of progress bar
55
+ return !(silentFlag || silentConfig || silentEnv || ciEnv);
56
+ }
57
+
58
+ function getDownloadUrl(platform, arch) {
59
+ const releasesUrl = `${CDN_URL}/${pkgInfo.version}/sentry-cli`;
60
+ let archString = '';
61
+ switch (arch) {
62
+ case 'x64':
63
+ archString = 'x86_64';
64
+ break;
65
+ case 'x86':
66
+ case 'ia32':
67
+ archString = 'i686';
68
+ break;
69
+ case 'arm64':
70
+ archString = 'aarch64';
71
+ break;
72
+ case 'arm':
73
+ archString = 'armv7';
74
+ break;
75
+ default:
76
+ archString = arch;
77
+ }
78
+ switch (platform) {
79
+ case 'darwin':
80
+ return `${releasesUrl}-Darwin-universal`;
81
+ case 'win32':
82
+ return `${releasesUrl}-Windows-${archString}.exe`;
83
+ case 'linux':
84
+ case 'freebsd':
85
+ return `${releasesUrl}-Linux-${archString}`;
86
+ default:
87
+ return null;
88
+ }
89
+ }
90
+
91
+ function createProgressBar(name, total) {
92
+ const incorrectTotal = typeof total !== 'number' || Number.isNaN(total);
93
+
94
+ if (incorrectTotal || !shouldRenderProgressBar()) {
95
+ return {
96
+ tick: () => {},
97
+ };
98
+ }
99
+
100
+ const logStream = getLogStream('stdout');
101
+
102
+ if (logStream.isTTY) {
103
+ return new ProgressBar(`fetching ${name} :bar :percent :etas`, {
104
+ complete: '█',
105
+ incomplete: '░',
106
+ width: 20,
107
+ total,
108
+ });
109
+ }
110
+
111
+ let pct = null;
112
+ let current = 0;
113
+ return {
114
+ tick: (length) => {
115
+ current += length;
116
+ const next = Math.round((current / total) * 100);
117
+ if (next > pct) {
118
+ pct = next;
119
+ logStream.write(`fetching ${name} ${pct}%\n`);
120
+ }
121
+ },
122
+ };
123
+ }
124
+
125
+ function npmCache() {
126
+ const env = process.env;
127
+ return (
128
+ env.npm_config_cache ||
129
+ env.npm_config_cache_folder ||
130
+ env.npm_config_yarn_offline_mirror ||
131
+ (env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'))
132
+ );
133
+ }
134
+
135
+ function getCachedPath(url) {
136
+ const digest = crypto.createHash('md5').update(url).digest('hex').slice(0, 6);
137
+
138
+ return path.join(
139
+ npmCache(),
140
+ 'sentry-cli',
141
+ `${digest}-${path.basename(url).replace(/[^a-zA-Z0-9.]+/g, '-')}`
142
+ );
143
+ }
144
+
145
+ function getTempFile(cached) {
146
+ return `${cached}.${process.pid}-${Math.random().toString(16).slice(2)}.tmp`;
147
+ }
148
+
149
+ function validateChecksum(tempPath, name, logger) {
150
+ let storedHash;
151
+ try {
152
+ const checksums = fs.readFileSync(path.join(__dirname, '../checksums.txt'), 'utf8');
153
+ const entries = checksums.split('\n');
154
+ for (let i = 0; i < entries.length; i++) {
155
+ const [key, value] = entries[i].split('=');
156
+ if (key === name) {
157
+ storedHash = value;
158
+ break;
159
+ }
160
+ }
161
+ } catch (e) {
162
+ logger.log(
163
+ 'Checksums are generated when the package is published to npm. They are not available directly in the source repository. Skipping validation.'
164
+ );
165
+ return;
166
+ }
167
+
168
+ if (!storedHash) {
169
+ logger.log(`Checksum for ${name} not found, skipping validation.`);
170
+ return;
171
+ }
172
+
173
+ const currentHash = crypto.createHash('sha256').update(fs.readFileSync(tempPath)).digest('hex');
174
+
175
+ if (storedHash !== currentHash) {
176
+ fs.unlinkSync(tempPath);
177
+ throw new Error(
178
+ `Checksum validation for ${name} failed.\nExpected: ${storedHash}\nReceived: ${currentHash}`
179
+ );
180
+ } else {
181
+ logger.log('Checksum validation passed.');
182
+ }
183
+ }
184
+
185
+ function checkVersion() {
186
+ return helper.execute(['--version']).then((output) => {
187
+ const version = output.replace('sentry-cli ', '').trim();
188
+ const expected = process.env.SENTRYCLI_LOCAL_CDNURL ? 'DEV' : pkgInfo.version;
189
+ if (version !== expected) {
190
+ throw new Error(`Unexpected sentry-cli version "${version}", expected "${expected}"`);
191
+ }
192
+ });
193
+ }
194
+
195
+ function downloadBinary(logger = ttyLogger) {
196
+ if (process.env.SENTRYCLI_SKIP_DOWNLOAD === '1') {
197
+ logger.log(`Skipping download because SENTRYCLI_SKIP_DOWNLOAD=1 detected.`);
198
+ return Promise.resolve();
199
+ }
200
+
201
+ const arch = os.arch();
202
+ const platform = os.platform();
203
+ const outputPath = helper.getPath();
204
+
205
+ if (process.env.SENTRYCLI_USE_LOCAL === '1') {
206
+ try {
207
+ const binPath = which.sync('sentry-cli');
208
+ logger.log(`Using local binary: ${binPath}`);
209
+ fs.copyFileSync(binPath, outputPath);
210
+ return Promise.resolve();
211
+ } catch (e) {
212
+ throw new Error(
213
+ 'Configured installation of local binary, but it was not found.' +
214
+ 'Make sure that `sentry-cli` executable is available in your $PATH or disable SENTRYCLI_USE_LOCAL env variable.'
215
+ );
216
+ }
217
+ }
218
+
219
+ const downloadUrl = getDownloadUrl(platform, arch);
220
+ if (!downloadUrl) {
221
+ return Promise.reject(new Error(`Unsupported target ${platform}-${arch}`));
222
+ }
223
+
224
+ const cachedPath = getCachedPath(downloadUrl);
225
+ if (fs.existsSync(cachedPath)) {
226
+ logger.log(`Using cached binary: ${cachedPath}`);
227
+ fs.copyFileSync(cachedPath, outputPath);
228
+ return Promise.resolve();
229
+ }
230
+
231
+ const proxyUrl = Proxy.getProxyForUrl(downloadUrl);
232
+ const agent = proxyUrl ? new HttpsProxyAgent(proxyUrl) : null;
233
+
234
+ logger.log(`Downloading from ${downloadUrl}`);
235
+
236
+ if (proxyUrl) {
237
+ logger.log(`Using proxy URL: ${proxyUrl}`);
238
+ }
239
+
240
+ return fetch(downloadUrl, {
241
+ agent,
242
+ compress: false,
243
+ headers: {
244
+ 'accept-encoding': 'gzip, deflate, br',
245
+ },
246
+ redirect: 'follow',
247
+ })
248
+ .then((response) => {
249
+ if (!response.ok) {
250
+ throw new Error(
251
+ `Unable to download sentry-cli binary from ${downloadUrl}.\nServer returned ${response.status}: ${response.statusText}.`
252
+ );
253
+ }
254
+
255
+ const contentEncoding = response.headers.get('content-encoding');
256
+ let decompressor;
257
+ if (/\bgzip\b/.test(contentEncoding)) {
258
+ decompressor = zlib.createGunzip();
259
+ } else if (/\bdeflate\b/.test(contentEncoding)) {
260
+ decompressor = zlib.createInflate();
261
+ } else if (/\bbr\b/.test(contentEncoding)) {
262
+ decompressor = zlib.createBrotliDecompress();
263
+ } else {
264
+ decompressor = new stream.PassThrough();
265
+ }
266
+ const name = downloadUrl.match(/.*\/(.*?)$/)[1];
267
+ const total = parseInt(response.headers.get('content-length'), 10);
268
+ const progressBar = createProgressBar(name, total);
269
+ const tempPath = getTempFile(cachedPath);
270
+ mkdirp.sync(path.dirname(tempPath));
271
+
272
+ return new Promise((resolve, reject) => {
273
+ response.body
274
+ .on('error', (e) => reject(e))
275
+ .on('data', (chunk) => progressBar.tick(chunk.length))
276
+ .pipe(decompressor)
277
+ .pipe(fs.createWriteStream(tempPath, { mode: '0755' }))
278
+ .on('error', (e) => reject(e))
279
+ .on('close', () => resolve());
280
+ }).then(() => {
281
+ if (process.env.SENTRYCLI_SKIP_CHECKSUM_VALIDATION !== '1') {
282
+ validateChecksum(tempPath, name, logger);
283
+ }
284
+ fs.copyFileSync(tempPath, cachedPath);
285
+ fs.copyFileSync(tempPath, outputPath);
286
+ fs.unlinkSync(tempPath);
287
+ });
288
+ })
289
+ .then(() => {
290
+ return checkVersion();
291
+ })
292
+ .catch((error) => {
293
+ if (error instanceof fetch.FetchError) {
294
+ throw new Error(
295
+ `Unable to download sentry-cli binary from ${downloadUrl}.\nError code: ${error.code}`
296
+ );
297
+ } else {
298
+ throw error;
299
+ }
300
+ });
301
+ }
302
+
303
+ module.exports = {
304
+ downloadBinary,
305
+ };
@@ -50,7 +50,7 @@ class Releases {
50
50
  * @returns {Promise} A promise that resolves when the release has been created.
51
51
  * @memberof SentryReleases
52
52
  */
53
- async new(release, options) {
53
+ new(release, options) {
54
54
  const args = ['releases', 'new', release].concat(helper.getProjectFlagsFromOptions(options));
55
55
  return this.execute(args, null);
56
56
  }
@@ -76,7 +76,7 @@ class Releases {
76
76
  * @returns {Promise} A promise that resolves when the commits have been associated
77
77
  * @memberof SentryReleases
78
78
  */
79
- async setCommits(release, options) {
79
+ setCommits(release, options) {
80
80
  if (!options || (!options.auto && (!options.repo || !options.commit))) {
81
81
  throw new Error('options.auto, or options.repo and options.commit must be specified');
82
82
  }
@@ -95,6 +95,10 @@ class Releases {
95
95
  commitFlags.push('--ignore-missing');
96
96
  }
97
97
 
98
+ if (options.ignoreEmpty) {
99
+ commitFlags.push('--ignore-empty');
100
+ }
101
+
98
102
  return this.execute(['releases', 'set-commits', release].concat(commitFlags));
99
103
  }
100
104
 
@@ -106,7 +110,7 @@ class Releases {
106
110
  * @returns {Promise} A promise that resolves when the release has been finalized.
107
111
  * @memberof SentryReleases
108
112
  */
109
- async finalize(release) {
113
+ finalize(release) {
110
114
  return this.execute(['releases', 'finalize', release], null);
111
115
  }
112
116
 
@@ -117,9 +121,10 @@ class Releases {
117
121
  * @returns {Promise.<string>} A promise that resolves to the version string.
118
122
  * @memberof SentryReleases
119
123
  */
120
- async proposeVersion() {
121
- const version = await this.execute(['releases', 'propose-version'], null);
122
- return version.trim();
124
+ proposeVersion() {
125
+ return this.execute(['releases', 'propose-version'], null).then(
126
+ version => version && version.trim()
127
+ );
123
128
  }
124
129
 
125
130
  /**
@@ -140,15 +145,13 @@ class Releases {
140
145
  * ignoreFile: null, // path to a file with ignore rules
141
146
  * rewrite: false, // preprocess sourcemaps before uploading
142
147
  * sourceMapReference: true, // add a source map reference to source files
143
- * dedupe: true, // deduplicate already uploaded files
144
148
  * stripPrefix: [], // remove certain prefices from filenames
145
149
  * stripCommonPrefix: false, // guess common prefices to remove from filenames
146
150
  * validate: false, // validate source maps and cancel the upload on error
147
151
  * urlPrefix: '', // add a prefix source map urls after stripping them
148
152
  * urlSuffix: '', // add a suffix source map urls after stripping them
149
153
  * ext: ['js', 'map', 'jsbundle', 'bundle'], // override file extensions to scan for
150
- * projects: ['node'], // provide a list of projects
151
- * decompress: false // decompress gzip files before uploading
154
+ * projects: ['node'] // provide a list of projects
152
155
  * });
153
156
  *
154
157
  * @param {string} release Unique name of the release.
@@ -156,7 +159,7 @@ class Releases {
156
159
  * @returns {Promise} A promise that resolves when the upload has completed successfully.
157
160
  * @memberof SentryReleases
158
161
  */
159
- async uploadSourceMaps(release, options) {
162
+ uploadSourceMaps(release, options) {
160
163
  if (!options || !options.include || !Array.isArray(options.include)) {
161
164
  throw new Error(
162
165
  '`options.include` must be a vaild array of paths and/or path descriptor objects.'
@@ -166,7 +169,7 @@ class Releases {
166
169
  // Each entry in the `include` array will map to an array of promises, which
167
170
  // will in turn contain one promise per literal path value. Thus `uploads`
168
171
  // will be an array of Promise arrays, which we'll flatten later.
169
- const uploads = options.include.map((includeEntry) => {
172
+ const uploads = options.include.map(includeEntry => {
170
173
  let pathOptions;
171
174
  let uploadPaths;
172
175
 
@@ -197,7 +200,7 @@ class Releases {
197
200
  .concat(helper.getProjectFlagsFromOptions(options))
198
201
  .concat(['files', release, 'upload-sourcemaps']);
199
202
 
200
- return uploadPaths.map((path) =>
203
+ return uploadPaths.map(path =>
201
204
  // `execute()` is async and thus we're returning a promise here
202
205
  this.execute(helper.prepareCommand([...args, path], SOURCEMAPS_SCHEMA, newOptions), true)
203
206
  );
@@ -218,7 +221,7 @@ class Releases {
218
221
  * @returns {Promise} A promise that resolves when the list comes back from the server.
219
222
  * @memberof SentryReleases
220
223
  */
221
- async listDeploys(release) {
224
+ listDeploys(release) {
222
225
  return this.execute(['releases', 'deploys', release, 'list'], null);
223
226
  }
224
227
 
@@ -244,7 +247,7 @@ class Releases {
244
247
  * @returns {Promise} A promise that resolves when the deploy has been created.
245
248
  * @memberof SentryReleases
246
249
  */
247
- async newDeploy(release, options) {
250
+ newDeploy(release, options) {
248
251
  if (!options || !options.env) {
249
252
  throw new Error('options.env must be a vaild name');
250
253
  }
@@ -258,7 +261,7 @@ class Releases {
258
261
  * @param {boolean} live We inherit stdio to display `sentry-cli` output directly.
259
262
  * @returns {Promise.<string>} A promise that resolves to the standard output.
260
263
  */
261
- async execute(args, live) {
264
+ execute(args, live) {
262
265
  return helper.execute(args, live, this.options.silent, this.configFile, this.options);
263
266
  }
264
267
  }
@@ -11,10 +11,6 @@ module.exports = {
11
11
  param: '--dist',
12
12
  type: 'string',
13
13
  },
14
- decompress: {
15
- param: '--decompress',
16
- type: 'boolean',
17
- },
18
14
  rewrite: {
19
15
  param: '--rewrite',
20
16
  invertedParam: '--no-rewrite',
@@ -24,10 +20,6 @@ module.exports = {
24
20
  invertedParam: '--no-sourcemap-reference',
25
21
  type: 'boolean',
26
22
  },
27
- dedupe: {
28
- invertedParam: '--no-dedupe',
29
- type: 'boolean',
30
- },
31
23
  stripPrefix: {
32
24
  param: '--strip-prefix',
33
25
  type: 'array',
@@ -52,8 +44,4 @@ module.exports = {
52
44
  param: '--ext',
53
45
  type: 'array',
54
46
  },
55
- useArtifactBundle: {
56
- param: '--use-artifact-bundle',
57
- type: 'boolean',
58
- },
59
47
  };
package/package.json CHANGED
@@ -1,45 +1,30 @@
1
1
  {
2
2
  "name": "@sentry/cli",
3
- "version": "1.77.2",
3
+ "version": "1.77.3",
4
4
  "description": "A command line utility to work with Sentry. https://docs.sentry.io/hosted/learn/cli/",
5
- "repository": "git://github.com/getsentry/sentry-cli.git",
6
5
  "homepage": "https://docs.sentry.io/hosted/learn/cli/",
7
- "author": "Sentry",
8
6
  "license": "BSD-3-Clause",
7
+ "keywords": [
8
+ "sentry",
9
+ "sentry-cli",
10
+ "cli"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/getsentry/sentry-cli"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/getsentry/sentry-cli/issues"
18
+ },
9
19
  "engines": {
10
- "node": ">= 10"
20
+ "node": ">= 8"
11
21
  },
12
22
  "main": "js/index.js",
13
- "types": "js/index.d.ts",
14
23
  "bin": {
15
24
  "sentry-cli": "bin/sentry-cli"
16
25
  },
17
- "dependencies": {
18
- "https-proxy-agent": "^5.0.0",
19
- "node-fetch": "^2.6.7",
20
- "progress": "^2.0.3",
21
- "proxy-from-env": "^1.1.0",
22
- "which": "^2.0.2"
23
- },
24
- "devDependencies": {
25
- "@vercel/nft": "^0.22.1",
26
- "eslint": "^7.32.0",
27
- "eslint-config-prettier": "^8.5.0",
28
- "jest": "^27.5.1",
29
- "npm-run-all": "^4.1.5",
30
- "prettier": "2.8.8"
31
- },
32
- "optionalDependencies": {
33
- "@sentry/cli-darwin": "1.77.2",
34
- "@sentry/cli-linux-arm": "1.77.2",
35
- "@sentry/cli-linux-arm64": "1.77.2",
36
- "@sentry/cli-linux-i686": "1.77.2",
37
- "@sentry/cli-linux-x64": "1.77.2",
38
- "@sentry/cli-win32-i686": "1.77.2",
39
- "@sentry/cli-win32-x64": "1.77.2"
40
- },
41
26
  "scripts": {
42
- "postinstall": "node ./scripts/install.js",
27
+ "install": "node ./scripts/install.js",
43
28
  "fix": "npm-run-all fix:eslint fix:prettier",
44
29
  "fix:eslint": "eslint --fix bin/* scripts/**/*.js js/**/*.js",
45
30
  "fix:prettier": "prettier --write bin/* scripts/**/*.js js/**/*.js",
@@ -50,15 +35,36 @@
50
35
  "test:prettier": "prettier --check bin/* scripts/**/*.js js/**/*.js",
51
36
  "test:vercel-nft": "node scripts/test-vercel-nft.js"
52
37
  },
38
+ "dependencies": {
39
+ "https-proxy-agent": "^5.0.0",
40
+ "mkdirp": "^0.5.5",
41
+ "node-fetch": "^2.6.7",
42
+ "progress": "^2.0.3",
43
+ "proxy-from-env": "^1.1.0",
44
+ "which": "^2.0.2"
45
+ },
46
+ "devDependencies": {
47
+ "@vercel/nft": "^0.22.1",
48
+ "eslint": "^6.8.0",
49
+ "eslint-config-airbnb-base": "^14.1.0",
50
+ "eslint-config-prettier": "^6.10.1",
51
+ "eslint-plugin-import": "^2.20.2",
52
+ "jest": "^25.3.0",
53
+ "npm-run-all": "^4.1.5",
54
+ "prettier": "^1.19.1"
55
+ },
53
56
  "jest": {
54
- "collectCoverage": true,
57
+ "collectCoverage": false,
55
58
  "testEnvironment": "node",
56
59
  "testPathIgnorePatterns": [
57
- "<rootDir>/src"
60
+ "src/utils"
58
61
  ]
59
62
  },
60
63
  "volta": {
61
- "node": "20.10.0",
64
+ "node": "10.24.1",
62
65
  "yarn": "1.22.19"
66
+ },
67
+ "publishConfig": {
68
+ "tag": "v1"
63
69
  }
64
70
  }
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
  set -eux
3
3
 
4
- DOCKER_IMAGE="messense/rust-musl-cross:${DOCKER_TAG}"
4
+ DOCKER_IMAGE="getsentry/rust-musl-cross:${DOCKER_TAG}"
5
5
  BUILD_DIR="/work"
6
6
 
7
7
  DOCKER_RUN_OPTS="
@@ -9,6 +9,7 @@ DOCKER_RUN_OPTS="
9
9
  -v $(pwd):${BUILD_DIR}:ro
10
10
  -v $(pwd)/target:${BUILD_DIR}/target
11
11
  -v $HOME/.cargo/registry:/root/.cargo/registry
12
+ -e ARMV7_UNKNOWN_LINUX_MUSLEABI_OPENSSL_NO_VENDOR=1
12
13
  ${DOCKER_IMAGE}
13
14
  "
14
15
 
@@ -21,17 +21,4 @@ cargo update -p sentry-cli
21
21
 
22
22
  # Do not tag and commit changes made by "npm version"
23
23
  export npm_config_git_tag_version=false
24
-
25
- # Bump main sentry cli npm package
26
24
  npm version "${TARGET}"
27
-
28
- # Bump the binary npm distributions
29
- for dir in $SCRIPT_DIR/../npm-binary-distributions/*; do
30
- cd $dir
31
- npm version "${TARGET}"
32
- cd -
33
- done
34
-
35
- # Update the optional deps in the main cli npm package
36
- # Requires jq to be installed - should be installed ootb on github runners
37
- jq '.optionalDependencies |= map_values("'"${TARGET}"'")' $SCRIPT_DIR/../package.json > package.json.tmp && mv package.json.tmp $SCRIPT_DIR/../package.json