@sentry/cli
Advanced tools
Comparing version 1.77.2 to 1.77.3
@@ -1,9 +0,16 @@ | ||
sentry-cli-Darwin-arm64=a69b907a4984a5d69fdb8fa018d08e45254e57a7038839a39deee09545137c9c | ||
sentry-cli-Darwin-universal=88648343df5e7e6bbf7e95fb8c67681b7949fdc919c00c0acaedf2ac8e258acc | ||
sentry-cli-Darwin-x86_64=26593639d98aa5e853951d1a4303ab578f1964545984b22e7f51feafcbe56152 | ||
sentry-cli-Linux-aarch64=62f023ef7741facc7d4faead1717840aa83572991f9bcf42172b0ad82f27a820 | ||
sentry-cli-Linux-armv7=23570b65e0c31428914d388b8e5e7ffb499627af810f60965517148cce0ac6fc | ||
sentry-cli-Linux-i686=a8e77f18ddadb5035d1c54b5978d7528b87fda62804a19a272f21761cdd0c889 | ||
sentry-cli-Linux-x86_64=088ac6568445c4ebb5b82652190f51c711e13d40d5bd94a62cc37f2f1865412d | ||
sentry-cli-Windows-i686.exe=1d8a960afa3e6ef6f08125974d2ae2e79e8a7b38ae30f7c0b7a32db26f8b8e8b | ||
sentry-cli-Windows-x86_64.exe=6c30d85138bac891ba490a222870067c82c0617226bcd8b32768f11b7a5c9585 | ||
sentry-cli-Darwin-arm64=940be414d1c4a49d36ada2893f66feabe358d4d386d42069abd13adec40b89da | ||
sentry-cli-Darwin-universal=6d88b76f393e2e7d28fdd47e38b967b120cffd63c23f9bcf957bd9649249d819 | ||
sentry-cli-Darwin-x86_64=ed0a4656c3748c71b409f6c232ae63c119c8d303cade1ae004e9ca1a4750da7a | ||
sentry-cli-Linux-aarch64=f36e03cfcf93eebc73c93ebf9772afc34bca30ca70e808d964d0e44b7a63c425 | ||
sentry-cli-Linux-armv7=ab1d464c92713b06f9156014ac0c075d179aa227416e99a2602ecadce7350ab7 | ||
sentry-cli-Linux-i686=ba52347c977fb6d2124fd188da5d3b6c54de59dc98036c27a24f1e7666fe6606 | ||
sentry-cli-Linux-x86_64=3188668e55aad1fec58d13ca695d2c31131ba90f30ea39885778cfba64429288 | ||
sentry-cli-Windows-i686.exe=42142f6477b09c5ea4a65bc93648ae4897196bf478f3aeb561a97ddabc525567 | ||
sentry-cli-Windows-x86_64.exe=c6365ff2bd6b067cdd3d24d36ba62c8aefaf1dce7f23b2cdc3c70b8f7e9058d2 | ||
sentry-cli-darwin-0.placeholder.tgz=a4bd6f16aa88eba11edf12887810f6e59b264c13f68fc4588a586c1518a7de5d | ||
sentry-cli-linux-arm-0.placeholder.tgz=a4bd6f16aa88eba11edf12887810f6e59b264c13f68fc4588a586c1518a7de5d | ||
sentry-cli-linux-arm64-0.placeholder.tgz=a4bd6f16aa88eba11edf12887810f6e59b264c13f68fc4588a586c1518a7de5d | ||
sentry-cli-linux-i686-0.placeholder.tgz=a4bd6f16aa88eba11edf12887810f6e59b264c13f68fc4588a586c1518a7de5d | ||
sentry-cli-linux-x64-0.placeholder.tgz=a4bd6f16aa88eba11edf12887810f6e59b264c13f68fc4588a586c1518a7de5d | ||
sentry-cli-win32-i686-0.placeholder.tgz=a4bd6f16aa88eba11edf12887810f6e59b264c13f68fc4588a586c1518a7de5d | ||
sentry-cli-win32-x64-0.placeholder.tgz=a4bd6f16aa88eba11edf12887810f6e59b264c13f68fc4588a586c1518a7de5d |
166
js/helper.js
'use strict'; | ||
const os = require('os'); | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const childProcess = require('child_process'); | ||
const BINARY_DISTRIBUTIONS = [ | ||
{ packageName: '@sentry/cli-darwin', subpath: 'bin/sentry-cli' }, | ||
{ packageName: '@sentry/cli-linux-x64', subpath: 'bin/sentry-cli' }, | ||
{ packageName: '@sentry/cli-linux-i686', subpath: 'bin/sentry-cli' }, | ||
{ packageName: '@sentry/cli-linux-arm64', subpath: 'bin/sentry-cli' }, | ||
{ packageName: '@sentry/cli-linux-arm', subpath: 'bin/sentry-cli' }, | ||
{ packageName: '@sentry/cli-win32-x64', subpath: 'bin/sentry-cli.exe' }, | ||
{ packageName: '@sentry/cli-win32-i686', subpath: 'bin/sentry-cli.exe' }, | ||
]; | ||
/** | ||
* This convoluted function resolves the path to the manually downloaded fallback | ||
* `sentry-cli` binary in a way that can't be analysed by @vercel/nft. | ||
* 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 getFallbackBinaryPath() { | ||
function getBinaryPath() { | ||
const parts = []; | ||
@@ -35,139 +22,15 @@ parts.push(__dirname); | ||
function getDistributionForThisPlatform() { | ||
const arch = os.arch(); | ||
const platform = os.platform(); | ||
let packageName = undefined; | ||
if (platform === 'darwin') { | ||
packageName = '@sentry/cli-darwin'; | ||
} else if (platform === 'linux' || platform === 'freebsd') { | ||
switch (arch) { | ||
case 'x64': | ||
packageName = '@sentry/cli-linux-x64'; | ||
break; | ||
case 'x86': | ||
case 'ia32': | ||
packageName = '@sentry/cli-linux-i686'; | ||
break; | ||
case 'arm64': | ||
packageName = '@sentry/cli-linux-arm64'; | ||
break; | ||
case 'arm': | ||
packageName = '@sentry/cli-linux-arm'; | ||
break; | ||
} | ||
} else if (platform === 'win32') { | ||
switch (arch) { | ||
case 'x64': | ||
packageName = '@sentry/cli-win32-x64'; | ||
break; | ||
case 'x86': | ||
case 'ia32': | ||
packageName = '@sentry/cli-win32-i686'; | ||
break; | ||
} | ||
} | ||
let subpath = undefined; | ||
switch (platform) { | ||
case 'win32': | ||
subpath = 'bin/sentry-cli.exe'; | ||
break; | ||
case 'darwin': | ||
case 'linux': | ||
case 'freebsd': | ||
subpath = 'bin/sentry-cli'; | ||
break; | ||
default: | ||
subpath = 'bin/sentry-cli'; | ||
break; | ||
} | ||
return { packageName, subpath }; | ||
} | ||
/** | ||
* Throws an error with a message stating that Sentry CLI doesn't support the current platform. | ||
* | ||
* @returns {never} nothing. It throws. | ||
* Absolute path to the sentry-cli binary (platform dependent). | ||
* @type {string} | ||
*/ | ||
function throwUnsupportedPlatformError() { | ||
throw new Error( | ||
`Unsupported operating system or architecture! Sentry CLI does not work on this architecture. | ||
let binaryPath = getBinaryPath(); | ||
Sentry CLI supports: | ||
- Darwin (macOS) | ||
- Linux and FreeBSD on x64, x86, ia32, arm64, and arm architectures | ||
- Windows x64, x86, and ia32 architectures` | ||
); | ||
} | ||
/** | ||
* Tries to find the installed Sentry CLI binary - either by looking into the relevant | ||
* optional dependencies or by trying to resolve the fallback binary. | ||
* | ||
* @returns {string} The path to the sentry-cli binary | ||
*/ | ||
function getBinaryPath() { | ||
if (process.env.SENTRY_BINARY_PATH) { | ||
return process.env.SENTRY_BINARY_PATH; | ||
} | ||
const { packageName, subpath } = getDistributionForThisPlatform(); | ||
if (packageName === undefined) { | ||
throwUnsupportedPlatformError(); | ||
} | ||
let fallbackBinaryPath = getFallbackBinaryPath(); | ||
if (fs.existsSync(fallbackBinaryPath)) { | ||
// Since the fallback got installed, the optional dependencies likely didn't get installed, so we just default to the fallback. | ||
return fallbackBinaryPath; | ||
} | ||
let compatibleBinaryPath; | ||
try { | ||
compatibleBinaryPath = require.resolve(`${packageName}/${subpath}`); | ||
} catch (e) { | ||
const otherInstalledDistribution = BINARY_DISTRIBUTIONS.find(({ packageName, subpath }) => { | ||
try { | ||
require.resolve(`${packageName}/${subpath}`); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
}); | ||
// These error messages are heavily inspired by esbuild's error messages: https://github.com/evanw/esbuild/blob/f3d535262e3998d845d0f102b944ecd5a9efda57/lib/npm/node-platform.ts#L150 | ||
if (otherInstalledDistribution) { | ||
throw new Error(`Sentry CLI binary for this platform/architecture not found! | ||
The "${otherInstalledDistribution.packageName}" package is installed, but for the current platform, you should have the "${packageName}" package installed instead. This usually happens if the "@sentry/cli" package is installed on one platform (for example Windows or MacOS) and then the "node_modules" folder is reused on another operating system (for example Linux in Docker). | ||
To fix this, avoid copying the "node_modules" folder, and instead freshly install your dependencies on the target system. You can also configure your package manager to install the right package. For example, yarn has the "supportedArchitectures" feature: https://yarnpkg.com/configuration/yarnrc/#supportedArchitecture.`); | ||
} else { | ||
throw new Error(`Sentry CLI binary for this platform/architecture not found! | ||
It seems like none of the "@sentry/cli" package's optional dependencies got installed. Please make sure your package manager is configured to install optional dependencies. If you are using npm to install your dependencies, please don't set the "--no-optional", "--ignore-optional", or "--omit=optional" flags. Sentry CLI needs the "optionalDependencies" feature in order to install its binary.`); | ||
} | ||
} | ||
return compatibleBinaryPath; | ||
} | ||
/** | ||
* Will be used as the binary path when defined with `mockBinaryPath`. | ||
* @type {string | undefined} | ||
*/ | ||
let mockedBinaryPath; | ||
/** | ||
* Overrides the default binary path with a mock value, useful for testing. | ||
* | ||
* @param {string} mockPath The new path to the mock sentry-cli binary | ||
* @deprecated This was used in tests internally and will be removed in the next major version. | ||
*/ | ||
// TODO(v3): Remove this function | ||
function mockBinaryPath(mockPath) { | ||
mockedBinaryPath = mockPath; | ||
binaryPath = mockPath; | ||
} | ||
@@ -202,3 +65,3 @@ | ||
const paramValue = options[option]; | ||
if (paramValue === undefined || paramValue === null) { | ||
if (paramValue === undefined) { | ||
return newOptions; | ||
@@ -259,3 +122,3 @@ } | ||
function getPath() { | ||
return mockedBinaryPath !== undefined ? mockedBinaryPath : getBinaryPath(); | ||
return binaryPath; | ||
} | ||
@@ -287,3 +150,3 @@ | ||
*/ | ||
async function execute(args, live, silent, configFile, config = {}) { | ||
function execute(args, live, silent, configFile, config = {}) { | ||
const env = { ...process.env }; | ||
@@ -316,8 +179,2 @@ if (configFile) { | ||
env.CUSTOM_HEADER = config.customHeader; | ||
} else if (config.headers) { | ||
const headers = Object.entries(config.headers).flatMap(([key, value]) => [ | ||
'--header', | ||
`${key}:${value}`, | ||
]); | ||
args = [...headers, ...args]; | ||
} | ||
@@ -358,5 +215,2 @@ return new Promise((resolve, reject) => { | ||
serializeOptions, | ||
getDistributionForThisPlatform, | ||
throwUnsupportedPlatformError, | ||
getFallbackBinaryPath, | ||
}; |
@@ -42,2 +42,7 @@ /** | ||
/** | ||
* Unique identifier for the distribution, used to further segment your release. | ||
* Usually your build number. | ||
*/ | ||
dist?: string; | ||
/** | ||
* If true, all logs are suppressed. | ||
@@ -51,7 +56,2 @@ */ | ||
customHeader?: string; | ||
/** | ||
* Headers added to every outgoing network request. | ||
* This value does not set any env variable, and is overridden by `customHeader`. | ||
*/ | ||
headers?: Record<string, string>; | ||
} | ||
@@ -64,5 +64,3 @@ | ||
*/ | ||
export type SourceMapsPathDescriptor = Omit<SentryCliUploadSourceMapsOptions, 'include'> & { | ||
paths: string[]; | ||
}; | ||
export type SourceMapsPathDescriptor = Omit<SentryCliUploadSourceMapsOptions, 'include'> & { paths: string[] } | ||
@@ -94,11 +92,2 @@ export interface SentryCliUploadSourceMapsOptions { | ||
/** | ||
* Enables files gzip decompression prior to uploading. Defaults to `false`. | ||
*/ | ||
decompress?: boolean; | ||
/** | ||
* Enable artifacts deduplication prior to uploading. This will skip uploading | ||
* any artifacts that are already present on the server. Defaults to `true`. | ||
*/ | ||
dedupe?: boolean; | ||
/** | ||
* When paired with the rewrite option this will remove a prefix from uploaded files. | ||
@@ -134,11 +123,2 @@ * For instance you can use this to remove a path that is build machine specific. | ||
ext?: string[]; | ||
/** | ||
* Unique identifier for the distribution, used to further segment your release. | ||
* Usually your build number. | ||
*/ | ||
dist?: string; | ||
/** | ||
* Use new Artifact Bundles upload, that enables use of Debug ID for Source Maps discovery. | ||
*/ | ||
useArtifactBundle?: boolean; | ||
} | ||
@@ -204,15 +184,27 @@ | ||
export interface SentryCliReleases { | ||
['new'](release: string, options?: { projects: string[] } | string[]): Promise<string>; | ||
['new']( | ||
release: string, | ||
options?: { projects: string[] } | string[] | ||
): Promise<string>; | ||
setCommits(release: string, options: SentryCliCommitsOptions): Promise<string>; | ||
setCommits( | ||
release: string, | ||
options: SentryCliCommitsOptions | ||
): Promise<string>; | ||
finalize(release: string): Promise<string>; | ||
finalize(release: string): Promise<string> | ||
proposeVersion(): Promise<string>; | ||
proposeVersion(): Promise<string> | ||
uploadSourceMaps(release: string, options: SentryCliUploadSourceMapsOptions): Promise<string>; | ||
uploadSourceMaps( | ||
release: string, | ||
options: SentryCliUploadSourceMapsOptions | ||
): Promise<string> | ||
listDeploys(release: string): Promise<string>; | ||
newDeploy(release: string, options: SentryCliNewDeployOptions): Promise<string>; | ||
newDeploy( | ||
release: string, | ||
options: SentryCliNewDeployOptions | ||
): Promise<string> | ||
@@ -231,12 +223,19 @@ execute(args: string[], live: boolean): Promise<string>; | ||
*/ | ||
constructor(configFile?: string | null, options?: SentryCliOptions); | ||
constructor(configFile?: string | null, options?: SentryCliOptions) | ||
public configFile?: string; | ||
public options?: SentryCliOptions; | ||
public releases: SentryCliReleases; | ||
public releases: SentryCliReleases | ||
public static getVersion(): string; | ||
public static getPath(): string; | ||
public execute(args: string[], live: boolean): Promise<string>; | ||
public static getVersion(): string | ||
public static getPath(): string | ||
/** | ||
* Downloads the CLI binary. | ||
* @returns {Promise<void>} | ||
*/ | ||
static downloadBinary(logger: { log(...args: unknown[]): void }): Promise<void>; | ||
public execute(args: string[], live: boolean): Promise<string> | ||
} | ||
} |
@@ -6,2 +6,3 @@ 'use strict'; | ||
const Releases = require('./releases'); | ||
const install = require('./install'); | ||
@@ -59,2 +60,11 @@ /** | ||
/** | ||
* Downloads the CLI binary. | ||
* @param {any} [configFile] Optional logger to log installation information. Defaults to printing to the terminal. | ||
* @returns {Promise<void>} | ||
*/ | ||
static downloadBinary(logger) { | ||
return install.downloadBinary(logger); | ||
} | ||
/** | ||
* See {helper.execute} docs. | ||
@@ -61,0 +71,0 @@ * @param {string[]} args Command line arguments passed to `sentry-cli`. |
@@ -53,3 +53,3 @@ 'use strict'; | ||
*/ | ||
async new(release, options) { | ||
new(release, options) { | ||
const args = ['releases', 'new', release].concat(helper.getProjectFlagsFromOptions(options)); | ||
@@ -80,3 +80,3 @@ return this.execute(args, null); | ||
*/ | ||
async setCommits(release, options) { | ||
setCommits(release, options) { | ||
if (!options || (!options.auto && (!options.repo || !options.commit))) { | ||
@@ -100,2 +100,6 @@ throw new Error('options.auto, or options.repo and options.commit must be specified'); | ||
if (options.ignoreEmpty) { | ||
commitFlags.push('--ignore-empty'); | ||
} | ||
return this.execute(['releases', 'set-commits', release].concat(commitFlags)); | ||
@@ -112,3 +116,3 @@ } | ||
*/ | ||
async finalize(release) { | ||
finalize(release) { | ||
return this.execute(['releases', 'finalize', release], null); | ||
@@ -124,5 +128,6 @@ } | ||
*/ | ||
async proposeVersion() { | ||
const version = await this.execute(['releases', 'propose-version'], null); | ||
return version.trim(); | ||
proposeVersion() { | ||
return this.execute(['releases', 'propose-version'], null).then( | ||
version => version && version.trim() | ||
); | ||
} | ||
@@ -148,3 +153,2 @@ | ||
* sourceMapReference: true, // add a source map reference to source files | ||
* dedupe: true, // deduplicate already uploaded files | ||
* stripPrefix: [], // remove certain prefices from filenames | ||
@@ -156,4 +160,3 @@ * stripCommonPrefix: false, // guess common prefices to remove from filenames | ||
* ext: ['js', 'map', 'jsbundle', 'bundle'], // override file extensions to scan for | ||
* projects: ['node'], // provide a list of projects | ||
* decompress: false // decompress gzip files before uploading | ||
* projects: ['node'] // provide a list of projects | ||
* }); | ||
@@ -166,3 +169,3 @@ * | ||
*/ | ||
async uploadSourceMaps(release, options) { | ||
uploadSourceMaps(release, options) { | ||
if (!options || !options.include || !Array.isArray(options.include)) { | ||
@@ -177,3 +180,3 @@ throw new Error( | ||
// will be an array of Promise arrays, which we'll flatten later. | ||
const uploads = options.include.map((includeEntry) => { | ||
const uploads = options.include.map(includeEntry => { | ||
let pathOptions; | ||
@@ -209,3 +212,3 @@ let uploadPaths; | ||
return uploadPaths.map((path) => | ||
return uploadPaths.map(path => | ||
// `execute()` is async and thus we're returning a promise here | ||
@@ -231,3 +234,3 @@ this.execute(helper.prepareCommand([...args, path], SOURCEMAPS_SCHEMA, newOptions), true) | ||
*/ | ||
async listDeploys(release) { | ||
listDeploys(release) { | ||
return this.execute(['releases', 'deploys', release, 'list'], null); | ||
@@ -258,3 +261,3 @@ } | ||
*/ | ||
async newDeploy(release, options) { | ||
newDeploy(release, options) { | ||
if (!options || !options.env) { | ||
@@ -273,3 +276,3 @@ throw new Error('options.env must be a vaild name'); | ||
*/ | ||
async execute(args, live) { | ||
execute(args, live) { | ||
return helper.execute(args, live, this.options.silent, this.configFile, this.options); | ||
@@ -276,0 +279,0 @@ } |
@@ -14,6 +14,2 @@ module.exports = { | ||
}, | ||
decompress: { | ||
param: '--decompress', | ||
type: 'boolean', | ||
}, | ||
rewrite: { | ||
@@ -28,6 +24,2 @@ param: '--rewrite', | ||
}, | ||
dedupe: { | ||
invertedParam: '--no-dedupe', | ||
type: 'boolean', | ||
}, | ||
stripPrefix: { | ||
@@ -57,6 +49,2 @@ param: '--strip-prefix', | ||
}, | ||
useArtifactBundle: { | ||
param: '--use-artifact-bundle', | ||
type: 'boolean', | ||
}, | ||
}; |
{ | ||
"name": "@sentry/cli", | ||
"version": "1.77.2", | ||
"version": "1.77.3", | ||
"description": "A command line utility to work with Sentry. https://docs.sentry.io/hosted/learn/cli/", | ||
"repository": "git://github.com/getsentry/sentry-cli.git", | ||
"homepage": "https://docs.sentry.io/hosted/learn/cli/", | ||
"author": "Sentry", | ||
"license": "BSD-3-Clause", | ||
"keywords": [ | ||
"sentry", | ||
"sentry-cli", | ||
"cli" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/getsentry/sentry-cli" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/getsentry/sentry-cli/issues" | ||
}, | ||
"engines": { | ||
"node": ">= 10" | ||
"node": ">= 8" | ||
}, | ||
"main": "js/index.js", | ||
"types": "js/index.d.ts", | ||
"bin": { | ||
"sentry-cli": "bin/sentry-cli" | ||
}, | ||
"scripts": { | ||
"install": "node ./scripts/install.js", | ||
"fix": "npm-run-all fix:eslint fix:prettier", | ||
"fix:eslint": "eslint --fix bin/* scripts/**/*.js js/**/*.js", | ||
"fix:prettier": "prettier --write bin/* scripts/**/*.js js/**/*.js", | ||
"test": "npm-run-all test:jest test:eslint test:prettier test:vercel-nft", | ||
"test:jest": "jest", | ||
"test:watch": "jest --watch --notify", | ||
"test:eslint": "eslint bin/* scripts/**/*.js js/**/*.js", | ||
"test:prettier": "prettier --check bin/* scripts/**/*.js js/**/*.js", | ||
"test:vercel-nft": "node scripts/test-vercel-nft.js" | ||
}, | ||
"dependencies": { | ||
"https-proxy-agent": "^5.0.0", | ||
"mkdirp": "^0.5.5", | ||
"node-fetch": "^2.6.7", | ||
@@ -26,40 +48,24 @@ "progress": "^2.0.3", | ||
"@vercel/nft": "^0.22.1", | ||
"eslint": "^7.32.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"jest": "^27.5.1", | ||
"eslint": "^6.8.0", | ||
"eslint-config-airbnb-base": "^14.1.0", | ||
"eslint-config-prettier": "^6.10.1", | ||
"eslint-plugin-import": "^2.20.2", | ||
"jest": "^25.3.0", | ||
"npm-run-all": "^4.1.5", | ||
"prettier": "2.8.8" | ||
"prettier": "^1.19.1" | ||
}, | ||
"optionalDependencies": { | ||
"@sentry/cli-darwin": "1.77.2", | ||
"@sentry/cli-linux-arm": "1.77.2", | ||
"@sentry/cli-linux-arm64": "1.77.2", | ||
"@sentry/cli-linux-i686": "1.77.2", | ||
"@sentry/cli-linux-x64": "1.77.2", | ||
"@sentry/cli-win32-i686": "1.77.2", | ||
"@sentry/cli-win32-x64": "1.77.2" | ||
}, | ||
"scripts": { | ||
"postinstall": "node ./scripts/install.js", | ||
"fix": "npm-run-all fix:eslint fix:prettier", | ||
"fix:eslint": "eslint --fix bin/* scripts/**/*.js js/**/*.js", | ||
"fix:prettier": "prettier --write bin/* scripts/**/*.js js/**/*.js", | ||
"test": "npm-run-all test:jest test:eslint test:prettier test:vercel-nft", | ||
"test:jest": "jest", | ||
"test:watch": "jest --watch --notify", | ||
"test:eslint": "eslint bin/* scripts/**/*.js js/**/*.js", | ||
"test:prettier": "prettier --check bin/* scripts/**/*.js js/**/*.js", | ||
"test:vercel-nft": "node scripts/test-vercel-nft.js" | ||
}, | ||
"jest": { | ||
"collectCoverage": true, | ||
"collectCoverage": false, | ||
"testEnvironment": "node", | ||
"testPathIgnorePatterns": [ | ||
"<rootDir>/src" | ||
"src/utils" | ||
] | ||
}, | ||
"volta": { | ||
"node": "20.10.0", | ||
"node": "10.24.1", | ||
"yarn": "1.22.19" | ||
}, | ||
"publishConfig": { | ||
"tag": "v1" | ||
} | ||
} |
<p align="center"> | ||
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank"> | ||
<picture> | ||
<source srcset="https://sentry-brand.storage.googleapis.com/sentry-logo-white.png" media="(prefers-color-scheme: dark)" /> | ||
<source srcset="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" /> | ||
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" alt="Sentry" width="280"> | ||
</picture> | ||
</a> | ||
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280"> | ||
<br /> | ||
</p> | ||
@@ -20,3 +15,3 @@ | ||
primarily used to upload debug symbols to Sentry if you are not using the | ||
Fastlane tools. | ||
fastlane tools. | ||
@@ -29,30 +24,9 @@ * Downloads can be found under | ||
If you are on OS X or Linux, you can use the automated downloader which will fetch the latest release version for you and install it: | ||
The recommended way to install is with everybody's favorite curl to bash: | ||
curl -sL https://sentry.io/get-cli/ | bash | ||
We do, however, encourage you to pin the specific version of the CLI, so your builds are always reproducible. | ||
To do that, you can use the exact same method, with an additional version specifier: | ||
curl -sL https://sentry.io/get-cli/ | SENTRY_CLI_VERSION=2.0.4 bash | ||
This will automatically download the correct version of `sentry-cli` for your operating system and install it. If necessary, it will prompt for your admin password for `sudo`. For a different installation location or for systems without `sudo` (like Windows), you can `export INSTALL_DIR=/custom/installation/path` before running this command. | ||
If you are using `sentry-cli` on Windows environments, [Microsoft Visual C++ Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist) is required. | ||
To verify it’s installed correctly you can bring up the help: | ||
sentry-cli --help | ||
### pip | ||
_New in 2.14.3_: `sentry-cli` can also be installed using `pip`: | ||
```bash | ||
pip install sentry-cli | ||
``` | ||
### Node | ||
Additionally, you can also install this binary via npm: | ||
Additionally you can also install this binary via npm: | ||
@@ -82,18 +56,10 @@ npm install @sentry/cli | ||
There are a few environment variables that you can provide to control the npm installation: | ||
Another option is to use the environment variable `SENTRYCLI_CDNURL`. | ||
```sh | ||
SENTRYCLI_CDNURL=https://mymirror.local/path npm install @sentry/cli | ||
``` | ||
SENTRYCLI_CDNURL=<url> # Use alternative cdn url for downloading binary | ||
SENTRYCLI_USE_LOCAL=1 # Use local instance of sentry-cli binary (looked up via $PATH environment) | ||
SENTRYCLI_SKIP_DOWNLOAD=1 # Skip downloading binary entirely | ||
SENTRYCLI_NO_PROGRESS_BAR=1 # Do not print the progress bar when downloading binary (default for non-TTY environments like CI) | ||
SENTRYCLI_LOG_STREAM=<stdout|stderr> # Changes where to redirect install script output | ||
``` | ||
When using `sentry-cli` via JavaScript API or any 3rd party plugin that is consuming said API, | ||
you can also use `SENTRY_BINARY_PATH=<path>` alongside `SENTRYCLI_SKIP_DOWNLOAD=1` to completely | ||
control what binaries are downloaded and used throughout the whole process. | ||
If you're installing the CLI with NPM from behind a proxy, the install script will | ||
use either NPM's configured HTTPS proxy server or the value from your `HTTPS_PROXY` | ||
use either NPM's configured HTTPS proxy server, or the value from your `HTTPS_PROXY` | ||
environment variable. | ||
@@ -119,19 +85,2 @@ | ||
Starting version _`2.8.0`_, in case you see `"error: config value 'safe.directory' was not found;"` message, | ||
you also need to correctly set UID and GID of mounted volumes like so: | ||
```sh | ||
docker run --rm -u "$(id -u):$(id -g)" -v $(pwd):/work getsentry/sentry-cli --help | ||
``` | ||
This is required due to security issue in older `git` implementations. See [here](https://github.blog/2022-04-12-git-security-vulnerability-announced/) for more details. | ||
## Update | ||
To update sentry-cli to the latest version run: | ||
```sh | ||
sentry-cli update | ||
``` | ||
## Compiling | ||
@@ -138,0 +87,0 @@ |
@@ -5,346 +5,28 @@ #!/usr/bin/env node | ||
const http = require('http'); | ||
const fs = require('fs'); | ||
const os = require('os'); | ||
const path = require('path'); | ||
const crypto = require('crypto'); | ||
const zlib = require('zlib'); | ||
const stream = require('stream'); | ||
const process = require('process'); | ||
const { downloadBinary } = require('../js/install'); | ||
const fetch = require('node-fetch'); | ||
const HttpsProxyAgent = require('https-proxy-agent'); | ||
const ProgressBar = require('progress'); | ||
const Proxy = require('proxy-from-env'); | ||
const which = require('which'); | ||
const helper = require('../js/helper'); | ||
const pkgInfo = require('../package.json'); | ||
const Logger = require('../js/logger'); | ||
const logger = new Logger(getLogStream('stderr')); | ||
const CDN_URL = | ||
process.env.SENTRYCLI_LOCAL_CDNURL || | ||
process.env.npm_config_sentrycli_cdnurl || | ||
process.env.SENTRYCLI_CDNURL || | ||
'https://downloads.sentry-cdn.com/sentry-cli'; | ||
function getLogStream(defaultStream) { | ||
const logStream = process.env.SENTRYCLI_LOG_STREAM || defaultStream; | ||
if (logStream === 'stdout') { | ||
return process.stdout; | ||
} | ||
if (logStream === 'stderr') { | ||
return process.stderr; | ||
} | ||
throw new Error( | ||
`Incorrect SENTRYCLI_LOG_STREAM env variable. Possible values: 'stdout' | 'stderr'` | ||
); | ||
} | ||
function shouldRenderProgressBar() { | ||
const silentFlag = process.argv.some((v) => v === '--silent'); | ||
const silentConfig = process.env.npm_config_loglevel === 'silent'; | ||
const silentEnv = process.env.SENTRYCLI_NO_PROGRESS_BAR; | ||
const ciEnv = process.env.CI === 'true' || process.env.CI === '1'; | ||
const notTTY = !process.stdout.isTTY; | ||
// If any of possible options is set, skip rendering of progress bar | ||
return !(silentFlag || silentConfig || silentEnv || ciEnv || notTTY); | ||
} | ||
function getDownloadUrl(platform, arch) { | ||
const releasesUrl = `${CDN_URL}/${pkgInfo.version}/sentry-cli`; | ||
let archString = ''; | ||
switch (arch) { | ||
case 'x64': | ||
archString = 'x86_64'; | ||
break; | ||
case 'x86': | ||
case 'ia32': | ||
archString = 'i686'; | ||
break; | ||
case 'arm64': | ||
archString = 'aarch64'; | ||
break; | ||
case 'arm': | ||
archString = 'armv7'; | ||
break; | ||
default: | ||
archString = arch; | ||
} | ||
switch (platform) { | ||
case 'darwin': | ||
return `${releasesUrl}-Darwin-universal`; | ||
case 'win32': | ||
// Windows arm machines can run x64 binaries | ||
if (arch === 'arm64') { | ||
archString = 'x86_64'; | ||
} | ||
return `${releasesUrl}-Windows-${archString}.exe`; | ||
case 'linux': | ||
case 'freebsd': | ||
return `${releasesUrl}-Linux-${archString}`; | ||
default: | ||
return null; | ||
} | ||
} | ||
function createProgressBar(name, total) { | ||
const incorrectTotal = typeof total !== 'number' || Number.isNaN(total); | ||
if (incorrectTotal || !shouldRenderProgressBar()) { | ||
return { | ||
tick: () => {}, | ||
}; | ||
} | ||
const logStream = getLogStream('stdout'); | ||
if (logStream.isTTY) { | ||
return new ProgressBar(`fetching ${name} :bar :percent :etas`, { | ||
complete: '█', | ||
incomplete: '░', | ||
width: 20, | ||
total, | ||
if (process.env.SENTRYCLI_LOCAL_CDNURL) { | ||
// For testing, mock the CDN by spawning a local server | ||
const server = http.createServer((request, response) => { | ||
const contents = fs.readFileSync(path.join(__dirname, '../js/__mocks__/sentry-cli')); | ||
response.writeHead(200, { | ||
'Content-Type': 'application/octet-stream', | ||
'Content-Length': String(contents.byteLength), | ||
}); | ||
} | ||
response.end(contents); | ||
}); | ||
let pct = null; | ||
let current = 0; | ||
return { | ||
tick: (length) => { | ||
current += length; | ||
const next = Math.round((current / total) * 100); | ||
if (next > pct) { | ||
pct = next; | ||
logStream.write(`fetching ${name} ${pct}%\n`); | ||
} | ||
}, | ||
}; | ||
server.listen(8999); | ||
process.on('exit', () => server.close()); | ||
} | ||
function npmCache() { | ||
const keys = ['npm_config_cache', 'npm_config_cache_folder', 'npm_config_yarn_offline_mirror']; | ||
for (let key of [...keys, ...keys.map((k) => k.toUpperCase())]) { | ||
if (process.env[key]) return process.env[key]; | ||
} | ||
if (process.env.APPDATA) { | ||
return path.join(process.env.APPDATA, 'npm-cache'); | ||
} | ||
return path.join(os.homedir(), '.npm'); | ||
} | ||
function getCachedPath(url) { | ||
const digest = crypto.createHash('md5').update(url).digest('hex').slice(0, 6); | ||
return path.join( | ||
npmCache(), | ||
'sentry-cli', | ||
`${digest}-${path.basename(url).replace(/[^a-zA-Z0-9.]+/g, '-')}` | ||
); | ||
} | ||
function getTempFile(cached) { | ||
return `${cached}.${process.pid}-${Math.random().toString(16).slice(2)}.tmp`; | ||
} | ||
function validateChecksum(tempPath, name) { | ||
let storedHash; | ||
try { | ||
const checksums = fs.readFileSync(path.join(__dirname, '../checksums.txt'), 'utf8'); | ||
const entries = checksums.split('\n'); | ||
for (let i = 0; i < entries.length; i++) { | ||
const [key, value] = entries[i].split('='); | ||
if (key === name) { | ||
storedHash = value; | ||
break; | ||
} | ||
} | ||
} catch (e) { | ||
logger.log( | ||
'Checksums are generated when the package is published to npm. They are not available directly in the source repository. Skipping validation.' | ||
); | ||
return; | ||
} | ||
if (!storedHash) { | ||
logger.log(`Checksum for ${name} not found, skipping validation.`); | ||
return; | ||
} | ||
const currentHash = crypto.createHash('sha256').update(fs.readFileSync(tempPath)).digest('hex'); | ||
if (storedHash !== currentHash) { | ||
fs.unlinkSync(tempPath); | ||
throw new Error( | ||
`Checksum validation for ${name} failed.\nExpected: ${storedHash}\nReceived: ${currentHash}` | ||
); | ||
} else { | ||
logger.log('Checksum validation passed.'); | ||
} | ||
} | ||
async function downloadBinary() { | ||
const arch = os.arch(); | ||
const platform = os.platform(); | ||
const outputPath = helper.getFallbackBinaryPath(); | ||
if (process.env.SENTRYCLI_USE_LOCAL === '1') { | ||
try { | ||
const binPaths = which.sync('sentry-cli', { all: true }); | ||
if (!binPaths.length) throw new Error('Binary not found'); | ||
const binPath = binPaths[binPaths.length - 1]; | ||
logger.log(`Using local binary: ${binPath}`); | ||
fs.copyFileSync(binPath, outputPath); | ||
return Promise.resolve(); | ||
} catch (e) { | ||
throw new Error( | ||
'Configured installation of local binary, but it was not found.' + | ||
'Make sure that `sentry-cli` executable is available in your $PATH or disable SENTRYCLI_USE_LOCAL env variable.' | ||
); | ||
} | ||
} | ||
const downloadUrl = getDownloadUrl(platform, arch); | ||
if (!downloadUrl) { | ||
throw new Error(`Unsupported target ${platform}-${arch}`); | ||
} | ||
const cachedPath = getCachedPath(downloadUrl); | ||
if (fs.existsSync(cachedPath)) { | ||
logger.log(`Using cached binary: ${cachedPath}`); | ||
fs.copyFileSync(cachedPath, outputPath); | ||
return; | ||
} | ||
const proxyUrl = Proxy.getProxyForUrl(downloadUrl); | ||
const agent = proxyUrl ? new HttpsProxyAgent(proxyUrl) : null; | ||
logger.log(`Downloading from ${downloadUrl}`); | ||
if (proxyUrl) { | ||
logger.log(`Using proxy URL: ${proxyUrl}`); | ||
} | ||
let response; | ||
try { | ||
response = await fetch(downloadUrl, { | ||
agent, | ||
compress: false, | ||
headers: { | ||
'accept-encoding': 'gzip, deflate, br', | ||
}, | ||
redirect: 'follow', | ||
}); | ||
} catch (error) { | ||
let errorMsg = `Unable to download sentry-cli binary from ${downloadUrl}.\nError message: ${error.message}`; | ||
if (error.code) { | ||
errorMsg += `\nError code: ${error.code}`; | ||
} | ||
throw new Error(errorMsg); | ||
} | ||
if (!response.ok) { | ||
let errorMsg = `Unable to download sentry-cli binary from ${downloadUrl}.\nServer returned: ${response.status}`; | ||
if (response.statusText) { | ||
errorMsg += ` - ${response.statusText}`; | ||
} | ||
throw new Error(errorMsg); | ||
} | ||
const contentEncoding = response.headers.get('content-encoding'); | ||
let decompressor; | ||
if (/\bgzip\b/.test(contentEncoding)) { | ||
decompressor = zlib.createGunzip(); | ||
} else if (/\bdeflate\b/.test(contentEncoding)) { | ||
decompressor = zlib.createInflate(); | ||
} else if (/\bbr\b/.test(contentEncoding)) { | ||
decompressor = zlib.createBrotliDecompress(); | ||
} else { | ||
decompressor = new stream.PassThrough(); | ||
} | ||
const name = downloadUrl.match(/.*\/(.*?)$/)[1]; | ||
let downloadedBytes = 0; | ||
const totalBytes = parseInt(response.headers.get('content-length'), 10); | ||
const progressBar = createProgressBar(name, totalBytes); | ||
const tempPath = getTempFile(cachedPath); | ||
fs.mkdirSync(path.dirname(tempPath), { recursive: true }); | ||
await new Promise((resolve, reject) => { | ||
response.body | ||
.on('error', (e) => reject(e)) | ||
.on('data', (chunk) => { | ||
downloadedBytes += chunk.length; | ||
progressBar.tick(chunk.length); | ||
}) | ||
.pipe(decompressor) | ||
.pipe(fs.createWriteStream(tempPath, { mode: '0755' })) | ||
.on('error', (e) => reject(e)) | ||
.on('close', () => { | ||
if (downloadedBytes >= totalBytes) { | ||
resolve(); | ||
} else { | ||
reject(new Error('connection interrupted')); | ||
} | ||
}); | ||
downloadBinary() | ||
.then(() => process.exit(0)) | ||
.catch(e => { | ||
// eslint-disable-next-line no-console | ||
console.error(e.toString()); | ||
process.exit(1); | ||
}); | ||
if (process.env.SENTRYCLI_SKIP_CHECKSUM_VALIDATION !== '1') { | ||
validateChecksum(tempPath, name); | ||
} | ||
fs.copyFileSync(tempPath, cachedPath); | ||
fs.copyFileSync(tempPath, outputPath); | ||
fs.unlinkSync(tempPath); | ||
} | ||
async function checkVersion() { | ||
const output = await helper.execute(['--version']); | ||
const version = output.replace('sentry-cli ', '').trim(); | ||
const expected = pkgInfo.version; | ||
if (version !== expected) { | ||
throw new Error(`Unexpected sentry-cli version "${version}", expected "${expected}"`); | ||
} | ||
} | ||
if (process.env.SENTRYCLI_SKIP_DOWNLOAD === '1') { | ||
logger.log(`Skipping download because SENTRYCLI_SKIP_DOWNLOAD=1 detected.`); | ||
process.exit(0); | ||
} | ||
const { packageName: distributionPackageName, subpath: distributionSubpath } = | ||
helper.getDistributionForThisPlatform(); | ||
if (distributionPackageName === undefined) { | ||
helper.throwUnsupportedPlatformError(); | ||
} | ||
try { | ||
require.resolve(`${distributionPackageName}/${distributionSubpath}`); | ||
// If the `resolve` call succeeds it means a binary was installed successfully via optional dependencies so we can skip the manual postinstall download. | ||
process.exit(0); | ||
} catch (e) { | ||
// Optional dependencies likely didn't get installed - proceed with fallback downloading manually | ||
// Log message inspired by esbuild: https://github.com/evanw/esbuild/blob/914f6080c77cfe32a54888caa51ca6ea13873ce9/lib/npm/node-install.ts#L253 | ||
logger.log( | ||
`Sentry CLI failed to locate the "${distributionPackageName}" package after installation! | ||
This can happen if you use an option to disable optional dependencies during installation, like "--no-optional", "--ignore-optional", or "--omit=optional". Sentry CLI uses the "optionalDependencies" package.json feature to install the correct binary for your platform and operating system. This post-install script will now try to work around this by manually downloading the Sentry CLI binary from the Sentry CDN. If this fails, you need to remove the "--no-optional", "--ignore-optional", and "--omit=optional" flags for Sentry CLI to work.` | ||
); | ||
downloadBinary() | ||
.then(() => checkVersion()) | ||
.then(() => { | ||
process.exit(0); | ||
}) | ||
.catch((e) => { | ||
console.error(e); | ||
process.exit(1); | ||
}); | ||
} |
@@ -0,1 +1,9 @@ | ||
const major = process.versions.node.split('.')[0]; | ||
// @vercel/nft doe not support Node.js v8 | ||
if (major < 10) { | ||
process.exit(0); | ||
} | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
const { nodeFileTrace } = require('@vercel/nft'); | ||
@@ -6,3 +14,4 @@ | ||
// Trace the module entrypoint | ||
nodeFileTrace([entryPoint]).then((result) => { | ||
nodeFileTrace([entryPoint]).then(result => { | ||
// eslint-disable-next-line no-console | ||
console.log('@vercel/nft traced dependencies:', Array.from(result.fileList)); | ||
@@ -12,7 +21,9 @@ | ||
if (result.fileList.has('sentry-cli') || result.fileList.has('sentry-cli.exe')) { | ||
// eslint-disable-next-line no-console | ||
console.error('ERROR: The sentry-cli binary should not be found by @vercel/nft'); | ||
process.exit(-1); | ||
} else { | ||
// eslint-disable-next-line no-console | ||
console.log('The sentry-cli binary was not traced by @vercel/nft'); | ||
} | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
87226
6
18
17
8
1119
101
6
+ Addedmkdirp@^0.5.5
+ Addedminimist@1.2.8(transitive)
+ Addedmkdirp@0.5.6(transitive)
- Removed@sentry/cli-darwin@1.77.2(transitive)
- Removed@sentry/cli-linux-arm@1.77.2(transitive)
- Removed@sentry/cli-linux-arm64@1.77.2(transitive)
- Removed@sentry/cli-linux-i686@1.77.2(transitive)
- Removed@sentry/cli-linux-x64@1.77.2(transitive)
- Removed@sentry/cli-win32-i686@1.77.2(transitive)
- Removed@sentry/cli-win32-x64@1.77.2(transitive)