Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(nextjs): Download CLI binary if it can't be found #9584

Merged
merged 1 commit into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@sentry/types": "7.80.1",
"@sentry/utils": "7.80.1",
"@sentry/vercel-edge": "7.80.1",
"@sentry/webpack-plugin": "1.20.0",
"@sentry/webpack-plugin": "1.21.0",
"chalk": "3.0.0",
"resolve": "1.22.8",
"rollup": "2.78.0",
Expand Down
93 changes: 68 additions & 25 deletions packages/nextjs/src/config/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as chalk from 'chalk';
import * as fs from 'fs';
import * as path from 'path';
import { sync as resolveSync } from 'resolve';
import type { Compiler } from 'webpack';

import type { VercelCronsConfig } from '../common/types';
// Note: If you need to import a type from Webpack, do it in `types.ts` and export it from there. Otherwise, our
Expand All @@ -22,6 +23,7 @@ import type {
WebpackConfigObjectWithModuleRules,
WebpackEntryProperty,
WebpackModuleRule,
WebpackPluginInstance,
} from './types';

const RUNTIME_TO_SDK_ENTRYPOINT_MAP = {
Expand All @@ -35,8 +37,8 @@ const RUNTIME_TO_SDK_ENTRYPOINT_MAP = {
let showedMissingAuthTokenErrorMsg = false;
let showedMissingOrgSlugErrorMsg = false;
let showedMissingProjectSlugErrorMsg = false;
let showedMissingCLiBinaryErrorMsg = false;
let showedHiddenSourceMapsWarningMsg = false;
let showedMissingCliBinaryWarningMsg = false;

// TODO: merge default SentryWebpackPlugin ignore with their SentryWebpackPlugin ignore or ignoreFile
// TODO: merge default SentryWebpackPlugin include with their SentryWebpackPlugin include
Expand Down Expand Up @@ -376,6 +378,7 @@ export function constructWebpackConfigFunction(
const SentryWebpackPlugin = loadModule<SentryCliPlugin>('@sentry/webpack-plugin');
if (SentryWebpackPlugin) {
newConfig.plugins = newConfig.plugins || [];
newConfig.plugins.push(new SentryCliDownloadPlugin());
newConfig.plugins.push(
// @ts-expect-error - this exists, the dynamic import just doesn't know about it
new SentryWebpackPlugin(
Expand Down Expand Up @@ -739,6 +742,19 @@ export function getWebpackPluginOptions(
if (err) {
const errorMessagePrefix = `${chalk.red('error')} -`;

if (err.message.includes('ENOENT')) {
if (!showedMissingCliBinaryWarningMsg) {
// eslint-disable-next-line no-console
console.error(
`\n${errorMessagePrefix} ${chalk.bold(
'The Sentry binary to upload sourcemaps could not be found.',
)} Source maps will not be uploaded. Please check that post-install scripts are enabled in your package manager when installing your dependencies and please run your build once without any caching to avoid caching issues of dependencies.\n`,
);
showedMissingCliBinaryWarningMsg = true;
}
return;
}

// Hardcoded way to check for missing auth token until we have a better way of doing this.
if (err.message.includes('Authentication credentials were not provided.')) {
let msg;
Expand Down Expand Up @@ -835,30 +851,6 @@ function shouldEnableWebpackPlugin(buildContext: BuildContext, userSentryOptions
const { isServer } = buildContext;
const { disableServerWebpackPlugin, disableClientWebpackPlugin } = userSentryOptions;

/** Non-negotiable */

// This check is necessary because currently, `@sentry/cli` uses a post-install script to download an
// architecture-specific version of the `sentry-cli` binary. If `yarn install`, `npm install`, or `npm ci` are run
// with the `--ignore-scripts` option, this will be blocked and the missing binary will cause an error when users
// try to build their apps.
const SentryWebpackPlugin = loadModule<SentryCliPlugin>('@sentry/webpack-plugin');

// @ts-expect-error - this exists, the dynamic import just doesn't know it
if (!SentryWebpackPlugin || !SentryWebpackPlugin.cliBinaryExists()) {
if (!showedMissingCLiBinaryErrorMsg) {
// eslint-disable-next-line no-console
console.error(
`${chalk.red('error')} - ${chalk.bold(
'Sentry CLI binary not found.',
)} Source maps will not be uploaded. Please check that postinstall scripts are enabled in your package manager when installing your dependencies and please run your build once without any caching to avoid caching issues of dependencies.\n`,
);
showedMissingCLiBinaryErrorMsg = true;
}
return false;
}

/** User override */

if (isServer && disableServerWebpackPlugin !== undefined) {
return !disableServerWebpackPlugin;
} else if (!isServer && disableClientWebpackPlugin !== undefined) {
Expand Down Expand Up @@ -1047,3 +1039,54 @@ function getRequestAsyncStorageModuleLocation(

return undefined;
}

let downloadingCliAttempted = false;

class SentryCliDownloadPlugin implements WebpackPluginInstance {
public apply(compiler: Compiler): void {
compiler.hooks.beforeRun.tapAsync('SentryCliDownloadPlugin', (compiler, callback) => {
const SentryWebpackPlugin = loadModule<SentryCliPlugin>('@sentry/webpack-plugin');
if (!SentryWebpackPlugin) {
// Pretty much an invariant.
return callback();
}

// @ts-expect-error - this exists, the dynamic import just doesn't know it
if (SentryWebpackPlugin.cliBinaryExists()) {
return callback();
}

if (!downloadingCliAttempted) {
downloadingCliAttempted = true;
// eslint-disable-next-line no-console
console.log(
`\n${chalk.cyan('info')} - ${chalk.bold(
'Sentry binary to upload source maps not found.',
)} Package manager post-install scripts are likely disabled or there is a caching issue. Manually downloading instead...`,
);

// @ts-expect-error - this exists, the dynamic import just doesn't know it
const cliDownloadPromise: Promise<void> = SentryWebpackPlugin.downloadCliBinary({
log: () => {
// No logs from directly from CLI
},
});

cliDownloadPromise.then(
() => {
// eslint-disable-next-line no-console
console.log(`${chalk.cyan('info')} - Sentry binary was successfully downloaded.\n`);
return callback();
},
e => {
// eslint-disable-next-line no-console
console.error(`${chalk.red('error')} - Sentry binary download failed:`, e);
return callback();
},
);
} else {
return callback();
}
});
}
}
41 changes: 31 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5067,7 +5067,7 @@
magic-string "0.27.0"
unplugin "1.0.1"

"@sentry/cli@^1.74.4", "@sentry/cli@^1.74.6":
"@sentry/cli@^1.74.4":
version "1.74.6"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.6.tgz#c4f276e52c6f5e8c8d692845a965988068ebc6f5"
integrity sha512-pJ7JJgozyjKZSTjOGi86chIngZMLUlYt2HOog+OJn+WGvqEkVymu8m462j1DiXAnex9NspB4zLLNuZ/R6rTQHg==
Expand All @@ -5080,6 +5080,18 @@
proxy-from-env "^1.1.0"
which "^2.0.2"

"@sentry/cli@^1.77.1":
version "1.77.1"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.77.1.tgz#ebcf884712ef6c3c75443f491ec16f6a22148aec"
integrity sha512-OtJ7U9LeuPUAY/xow9wwcjM9w42IJIpDtClTKI/RliE685vd/OJUIpiAvebHNthDYpQynvwb/0iuF4fonh+CKw==
dependencies:
https-proxy-agent "^5.0.0"
mkdirp "^0.5.5"
node-fetch "^2.6.7"
progress "^2.0.3"
proxy-from-env "^1.1.0"
which "^2.0.2"

"@sentry/cli@^2.17.0":
version "2.17.0"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.17.0.tgz#fc809ecd721eb5323502625fa904b786af28ad89"
Expand Down Expand Up @@ -5124,12 +5136,12 @@
dependencies:
"@sentry/cli" "^1.74.4"

"@sentry/webpack-plugin@1.20.0":
version "1.20.0"
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.20.0.tgz#e7add76122708fb6b4ee7951294b521019720e58"
integrity sha512-Ssj1mJVFsfU6vMCOM2d+h+KQR7QHSfeIP16t4l20Uq/neqWXZUQ2yvQfe4S3BjdbJXz/X4Rw8Hfy1Sd0ocunYw==
"@sentry/webpack-plugin@1.21.0":
version "1.21.0"
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.21.0.tgz#bbe7cb293751f80246a4a56f9a7dd6de00f14b58"
integrity sha512-x0PYIMWcsTauqxgl7vWUY6sANl+XGKtx7DCVnnY7aOIIlIna0jChTAPANTfA2QrK+VK+4I/4JxatCEZBnXh3Og==
dependencies:
"@sentry/cli" "^1.74.6"
"@sentry/cli" "^1.77.1"
webpack-sources "^2.0.0 || ^3.0.0"

"@sideway/address@^4.1.3":
Expand Down Expand Up @@ -8290,10 +8302,10 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==

axios@1.3.4, axios@^1.2.2:
version "1.3.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024"
integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==
axios@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102"
integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
Expand All @@ -8316,6 +8328,15 @@ axios@^1.0.0:
form-data "^4.0.0"
proxy-from-env "^1.1.0"

axios@^1.2.2:
version "1.3.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024"
integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"

b4a@^1.6.4:
version "1.6.4"
resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9"
Expand Down