Skip to content

Commit

Permalink
fix: Manually download binary if optional dependency binary can't be …
Browse files Browse the repository at this point in the history
…found after installation (#1874)

Co-authored-by: Yagiz Nizipli <yagiz@nizipli.com>
  • Loading branch information
lforst and anonrig authored Dec 28, 2023
1 parent be17486 commit c0f161b
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 34 deletions.
67 changes: 51 additions & 16 deletions js/helper.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict';

const os = require('os');
const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');

const BINARY_DISTRIBUTIONS = [
Expand All @@ -13,6 +15,23 @@ const BINARY_DISTRIBUTIONS = [
{ 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.
*
* 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() {
const parts = [];
parts.push(__dirname);
parts.push('..');
parts.push(`sentry-cli${process.platform === 'win32' ? '.exe' : ''}`);
return path.resolve(...parts);
}

function getDistributionForThisPlatform() {
const arch = os.arch();
const platform = os.platform();
Expand Down Expand Up @@ -67,11 +86,25 @@ function getDistributionForThisPlatform() {
}

/**
* This convoluted function resolves the path to the `sentry-cli` binary in a
* way that can't be analysed by @vercel/nft.
* Throws an error with a message stating that Sentry CLI doesn't support the current platform.
*
* @returns {never} nothing. It throws.
*/
function throwUnsupportedPlatformError() {
throw new Error(
`Unsupported operating system or architecture! Sentry CLI does not work on this architecture.
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.
*
* 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() {
Expand All @@ -82,14 +115,13 @@ function getBinaryPath() {
const { packageName, subpath } = getDistributionForThisPlatform();

if (packageName === undefined) {
throw new Error(
`Unsupported operating system or architecture! Sentry CLI does not work on this architecture.
throwUnsupportedPlatformError();
}

Sentry CLI supports:
- Darwin (macOS)
- Linux and FreeBSD on x64, x86, ia32, arm64, and arm architectures
- Windows x64, x86, and ia32 architectures`
);
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;
Expand Down Expand Up @@ -123,10 +155,10 @@ It seems like none of the "@sentry/cli" package's optional dependencies got inst
}

/**
* Absolute path to the sentry-cli binary (platform dependent).
* @type {string}
* Will be used as the binary path when defined with `mockBinaryPath`.
* @type {string | undefined}
*/
let binaryPath = getBinaryPath();
let mockedBinaryPath;

/**
* Overrides the default binary path with a mock value, useful for testing.
Expand All @@ -136,7 +168,7 @@ let binaryPath = getBinaryPath();
*/
// TODO(v3): Remove this function
function mockBinaryPath(mockPath) {
binaryPath = mockPath;
mockedBinaryPath = mockPath;
}

/**
Expand Down Expand Up @@ -222,7 +254,7 @@ function prepareCommand(command, schema, options) {
* @returns {string}
*/
function getPath() {
return binaryPath;
return mockedBinaryPath !== undefined ? mockedBinaryPath : getBinaryPath();
}

/**
Expand Down Expand Up @@ -318,4 +350,7 @@ module.exports = {
mockBinaryPath,
prepareCommand,
serializeOptions,
getDistributionForThisPlatform,
throwUnsupportedPlatformError,
getFallbackBinaryPath,
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@sentry/cli-win32-x64": "2.23.1"
},
"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",
Expand Down
46 changes: 28 additions & 18 deletions scripts/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@

'use strict';

// TODO(v3): Remove this file

console.log(
'DEPRECATION NOTICE: The Sentry CLI install script has been deprecated. The package now uses "optionalDependencies" instead to install architecture-compatible binaries distributed over npm.'
);

const fs = require('fs');
const os = require('os');
const path = require('path');
Expand Down Expand Up @@ -198,7 +192,7 @@ function validateChecksum(tempPath, name) {
async function downloadBinary() {
const arch = os.arch();
const platform = os.platform();
const outputPath = helper.getPath();
const outputPath = helper.getFallbackBinaryPath();

if (process.env.SENTRYCLI_USE_LOCAL === '1') {
try {
Expand Down Expand Up @@ -323,14 +317,30 @@ if (process.env.SENTRYCLI_SKIP_DOWNLOAD === '1') {
process.exit(0);
}

(async () => {
try {
await downloadBinary();
await checkVersion();
process.exit(0);
} catch (e) {
// eslint-disable-next-line no-console
console.error(e.toString());
process.exit(1);
}
})();
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
logger.log(
`Sentry CLI binary installation via optional dependencies was unsuccessful. Downloading manually instead.`
);

downloadBinary()
.then(() => checkVersion())
.then(() => {
process.exit(0);
})
.catch((e) => {
console.error(e);
process.exit(1);
});
}

0 comments on commit c0f161b

Please sign in to comment.