UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

305 lines (301 loc) 13.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.configureCI = exports.setupCI = exports.runSourcemapsWizard = void 0; // @ts-expect-error - clack is ESM and TS complains about that. It works though const prompts_1 = __importDefault(require("@clack/prompts")); const Sentry = __importStar(require("@sentry/node")); const chalk_1 = __importDefault(require("chalk")); const telemetry_1 = require("../telemetry"); const clack_1 = require("../utils/clack"); const package_manager_1 = require("../utils/package-manager"); const url_1 = require("../utils/url"); const is_unicorn_supported_1 = require("../utils/vendor/is-unicorn-supported"); const angular_1 = require("./tools/angular"); const create_react_app_1 = require("./tools/create-react-app"); const esbuild_1 = require("./tools/esbuild"); const rollup_1 = require("./tools/rollup"); const sentry_cli_1 = require("./tools/sentry-cli"); const tsc_1 = require("./tools/tsc"); const vite_1 = require("./tools/vite"); const webpack_1 = require("./tools/webpack"); const detect_tool_1 = require("./utils/detect-tool"); const other_wizards_1 = require("./utils/other-wizards"); const sdk_version_1 = require("./utils/sdk-version"); const path_1 = require("path"); const wrangler_1 = require("./tools/wrangler"); async function runSourcemapsWizard(options, preSelectedTool) { return (0, telemetry_1.withTelemetry)({ enabled: options.telemetryEnabled, integration: 'sourcemaps', wizardOptions: options, }, () => runSourcemapsWizardWithTelemetry(options, preSelectedTool)); } exports.runSourcemapsWizard = runSourcemapsWizard; async function runSourcemapsWizardWithTelemetry(options, preSelectedTool) { if (!preSelectedTool) { (0, clack_1.printWelcome)({ wizardName: 'Sentry Source Maps Upload Configuration Wizard', message: `This wizard will help you upload source maps to Sentry as part of your build. Thank you for using Sentry :)${options.telemetryEnabled ? ` (This setup wizard sends telemetry data and crash reports to Sentry. You can turn this off by running the wizard with the '--disable-telemetry' flag.)` : ''}`, promoCode: options.promoCode, }); } const moreSuitableWizard = await (0, telemetry_1.traceStep)('check-framework-wizard', other_wizards_1.checkIfMoreSuitableWizardExistsAndAskForRedirect); if (moreSuitableWizard) { await (0, telemetry_1.traceStep)('run-framework-wizard', () => moreSuitableWizard(options)); return; } if (!preSelectedTool) { await (0, clack_1.confirmContinueIfNoOrDirtyGitRepo)({ ignoreGitChanges: options.ignoreGitChanges, cwd: undefined, }); } await (0, telemetry_1.traceStep)('check-sdk-version', sdk_version_1.ensureMinimumSdkVersionIsInstalled); const { selfHosted, selectedProject, sentryUrl, authToken } = await (0, clack_1.getOrAskForProjectData)(options); const wizardOptionsWithPreSelectedProject = { ...options, preSelectedProject: { project: selectedProject, authToken, selfHosted, }, }; const selectedTool = preSelectedTool || (await (0, telemetry_1.traceStep)('select-tool', askForUsedBundlerTool)); Sentry.setTag('selected-tool', selectedTool); if (selectedTool === 'no-tool') { prompts_1.default.log.info("No Problem! But in this case, there's nothing to configure :)"); await (0, clack_1.abort)('Exiting, have a great day!', 0); return; } await (0, telemetry_1.traceStep)('tool-setup', () => startToolSetupFlow(selectedTool, { orgSlug: selectedProject.organization.slug, projectSlug: selectedProject.slug, selfHosted, url: sentryUrl, authToken, }, wizardOptionsWithPreSelectedProject, preSelectedTool)); await (0, telemetry_1.traceStep)('ci-setup', () => setupCI(selectedTool, authToken, options.comingFrom)); if (!preSelectedTool) { // running prettier is only necessary if the source maps wizard is the main flow // skip it, if it's called from another wizard (e.g. angular) await (0, clack_1.runPrettierIfInstalled)({ cwd: process.cwd() }); } if (!preSelectedTool) { await (0, telemetry_1.traceStep)('outro', () => printOutro(sentryUrl, selectedProject.organization.slug, selectedProject.id)); } } async function askForUsedBundlerTool() { const selectedTool = await (0, clack_1.abortIfCancelled)(prompts_1.default.select({ message: 'Which framework, bundler or build tool are you using?', options: [ { label: 'Angular', value: 'angular', hint: 'Select this option if you are using Angular.', }, { label: 'Create React App', value: 'create-react-app', hint: 'Select this option if you set up your app with Create React App.', }, { label: 'Cloudflare Wrangler', value: 'wrangler', hint: "You're using `wrangler deploy` to build and deploy your Cloudflare Worker.", }, { label: 'Webpack', value: 'webpack', hint: 'Select this if you are using Webpack and you have access to your Webpack config.', }, { label: 'Vite', value: 'vite', hint: 'Select this if you are using Vite and you have access to your Vite config.', }, { label: 'esbuild', value: 'esbuild', hint: 'Select this if you are using esbuild and you have access to your esbuild config.', }, { label: 'Rollup', value: 'rollup', hint: 'Select this if you are using Rollup and you have access to your Rollup config.', }, { label: 'tsc', value: 'tsc', hint: 'Configure source maps when using tsc as build tool', }, { label: 'I use another tool', value: 'sentry-cli', hint: 'This will configure source maps upload for you using sentry-cli', }, { label: "I don't minify, transpile or bundle my code", value: 'no-tool', hint: 'This will exit the wizard', }, ], initialValue: await (0, detect_tool_1.detectUsedTool)(), })); return selectedTool; } async function startToolSetupFlow(selectedTool, options, wizardOptions, preSelectedTool) { switch (selectedTool) { case 'webpack': await (0, webpack_1.configureWebPackPlugin)(options); break; case 'vite': await (0, vite_1.configureVitePlugin)(options); break; case 'esbuild': await (0, esbuild_1.configureEsbuildPlugin)(options); break; case 'rollup': await (0, rollup_1.configureRollupPlugin)(options); break; case 'tsc': await (0, sentry_cli_1.configureSentryCLI)(options, tsc_1.configureTscSourcemapGenerationFlow); break; case 'create-react-app': await (0, sentry_cli_1.configureSentryCLI)({ ...options, defaultArtifactPath: `.${path_1.sep}build` }, create_react_app_1.configureCRASourcemapGenerationFlow); break; case 'wrangler': await (0, wrangler_1.configureWrangler)(options); break; case 'angular': await (0, sentry_cli_1.configureSentryCLI)({ ...options, defaultArtifactPath: `.${path_1.sep}dist` }, angular_1.configureAngularSourcemapGenerationFlow, preSelectedTool === 'angular'); break; default: await (0, sentry_cli_1.configureSentryCLI)(options); break; } } async function setupCI(selectedTool, authToken, comingFrom) { if (comingFrom === 'vercel') { prompts_1.default.log.info('Sentry Vercel integration is already configured. Skipping CI setup.'); Sentry.setTag('using-ci', true); } else { await (0, telemetry_1.traceStep)('configure-ci', () => configureCI(selectedTool, authToken)); } } exports.setupCI = setupCI; async function configureCI(selectedTool, authToken) { const isUsingCI = await (0, clack_1.abortIfCancelled)(prompts_1.default.select({ message: `Are you using a CI/CD tool to build and deploy your application?`, options: [ { label: 'Yes', hint: 'I use a tool like GitHub Actions, GitLab, CircleCI, TravisCI, Jenkins, Vercel, ...', value: true, }, { label: 'No', hint: 'I build and deploy my application manually', value: false, }, ], initialValue: true, })); Sentry.setTag('using-ci', isUsingCI); const isCliBasedFlowTool = [ 'sentry-cli', 'tsc', 'angular', 'create-react-app', ].includes(selectedTool); const authTokenFile = isCliBasedFlowTool ? clack_1.SENTRY_CLI_RC_FILE : clack_1.SENTRY_DOT_ENV_FILE; if (!isUsingCI) { prompts_1.default.log.info(`No Problem! Just make sure that the Sentry auth token from ${chalk_1.default.cyan(authTokenFile)} is available whenever you build and deploy your app.`); return; } if (isCliBasedFlowTool) { await (0, telemetry_1.traceStep)('ci-npm-script-setup', sentry_cli_1.setupNpmScriptInCI); } await (0, telemetry_1.traceStep)('ci-auth-token-setup', () => setupAuthTokenInCI(authToken)); } exports.configureCI = configureCI; async function setupAuthTokenInCI(authToken) { prompts_1.default.log.step('Add the Sentry authentication token as an environment variable to your CI setup:'); // Intentially logging directly to console here so that the code can be copied/pasted directly // eslint-disable-next-line no-console console.log(chalk_1.default.greenBright(` SENTRY_AUTH_TOKEN=${authToken} `)); prompts_1.default.log.warn(chalk_1.default.yellow('DO NOT commit this auth token to your repository!')); const addedEnvVarToCI = await (0, clack_1.abortIfCancelled)(prompts_1.default.select({ message: 'Did you configure CI as shown above?', options: [ { label: 'Yes, continue!', value: true }, { label: "I'll do it later...", value: false, hint: chalk_1.default.yellow('You need to set the auth token to upload source maps in CI'), }, ], initialValue: true, })); Sentry.setTag('added-env-var-to-ci', addedEnvVarToCI); if (!addedEnvVarToCI) { prompts_1.default.log.info("Don't forget! :)"); } } async function printOutro(url, orgSlug, projectId) { const packageManager = await (0, clack_1.getPackageManager)(package_manager_1.NPM); const issueStreamUrl = (0, url_1.getIssueStreamUrl)({ url, orgSlug, projectId }); const arrow = (0, is_unicorn_supported_1.isUnicodeSupported)() ? '→' : '->'; prompts_1.default.outro(`${chalk_1.default.green("That's it - everything is set up!")} ${chalk_1.default.cyan(`Test and validate your setup locally with the following Steps: 1. Build your application in ${chalk_1.default.bold('production mode')}. ${chalk_1.default.gray(`${arrow} For example, run ${chalk_1.default.bold(packageManager.buildCommand)}.`)} ${chalk_1.default.gray(`${arrow} You should see source map upload logs in your console.`)} 2. Run your application and throw a test error. ${chalk_1.default.gray(`${arrow} The error should appear in Sentry:`)} ${chalk_1.default.gray(`${arrow} ${issueStreamUrl}`)} 3. Open the error in Sentry and verify that it's source-mapped. ${chalk_1.default.gray(`${arrow} The stack trace should show your original source code.`)} `)} ${chalk_1.default.dim(`If you encounter any issues, please refer to the Troubleshooting Guide: https://docs.sentry.io/platforms/javascript/sourcemaps/troubleshooting_js If the guide doesn't help or you encounter a bug, please let us know: https://github.com/getsentry/sentry-javascript/issues`)} `); } //# sourceMappingURL=sourcemaps-wizard.js.map