UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

213 lines 10.8 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.addSentryCommandToBuildCommand = exports.setupNpmScriptInCI = exports.configureSentryCLI = void 0; // @ts-expect-error - clack is ESM and TS complains about that. It works though const clack = __importStar(require("@clack/prompts")); const chalk_1 = __importDefault(require("chalk")); const Sentry = __importStar(require("@sentry/node")); const path = __importStar(require("path")); const fs = __importStar(require("fs")); const clack_1 = require("../../utils/clack"); const package_json_1 = require("../../utils/package-json"); const telemetry_1 = require("../../telemetry"); const package_manager_1 = require("../../utils/package-manager"); const SENTRY_NPM_SCRIPT_NAME = 'sentry:sourcemaps'; let addedToBuildCommand = false; async function configureSentryCLI(options, configureSourcemapGenerationFlow = defaultConfigureSourcemapGenerationFlow, skipValidation = false) { const packageDotJson = await (0, clack_1.getPackageDotJson)(); await (0, clack_1.installPackage)({ packageName: '@sentry/cli@^2', alreadyInstalled: (0, package_json_1.hasPackageInstalled)('@sentry/cli', packageDotJson), }); let validPath = false; let relativeArtifactPath; do { const rawArtifactPath = await (0, clack_1.abortIfCancelled)(clack.text({ message: 'Where are your build artifacts located?', placeholder: relativeArtifactPath ?? options.defaultArtifactPath ?? `.${path.sep}out`, initialValue: relativeArtifactPath ?? options.defaultArtifactPath ?? `.${path.sep}out`, validate(value) { if (!value) { return 'Please enter a path.'; } }, })); if (path.isAbsolute(rawArtifactPath)) { relativeArtifactPath = path.relative(process.cwd(), rawArtifactPath); } else { relativeArtifactPath = rawArtifactPath; } if ((0, clack_1.artifactsExist)(relativeArtifactPath)) { validPath = true; continue; } const runBuildOrEnterPathOrProceed = await (0, clack_1.askToRunBuildOrEnterPathOrProceed)({ relativeArtifactPath, }); validPath = runBuildOrEnterPathOrProceed?.validPath ?? false; relativeArtifactPath = runBuildOrEnterPathOrProceed?.relativeArtifactPath ?? relativeArtifactPath; } while (!validPath); const relativePosixArtifactPath = relativeArtifactPath .split(path.sep) .join(path.posix.sep); if (!skipValidation) { await configureSourcemapGenerationFlow(); } await createAndAddNpmScript(options, relativePosixArtifactPath); if (await askShouldAddToBuildCommand()) { await (0, telemetry_1.traceStep)('sentry-cli-add-to-build-cmd', () => addSentryCommandToBuildCommand()); } else { clack.log.info(`No problem, just make sure to run this script ${chalk_1.default.bold('after')} building your application but ${chalk_1.default.bold('before')} deploying!`); } await (0, clack_1.addSentryCliConfig)({ authToken: options.authToken }); } exports.configureSentryCLI = configureSentryCLI; async function setupNpmScriptInCI() { if (addedToBuildCommand) { // No need to tell users to add it manually to their CI // if the script is already added to the build command return; } const addedToCI = await (0, clack_1.abortIfCancelled)(clack.select({ message: `Add a step to your CI pipeline that runs the ${chalk_1.default.cyan(SENTRY_NPM_SCRIPT_NAME)} script ${chalk_1.default.bold('right after')} building your application.`, options: [ { label: 'I did, continue!', value: true }, { label: "I'll do it later...", value: false, hint: chalk_1.default.yellow(`You need to run ${chalk_1.default.cyan(SENTRY_NPM_SCRIPT_NAME)} after each build for source maps to work properly.`), }, ], initialValue: true, })); Sentry.setTag('added-ci-script', addedToCI); if (!addedToCI) { clack.log.info("Don't forget! :)"); } } exports.setupNpmScriptInCI = setupNpmScriptInCI; async function createAndAddNpmScript(options, relativePosixArtifactPath) { const sentryCliNpmScript = `sentry-cli sourcemaps inject --org ${options.orgSlug} --project ${options.projectSlug} ${relativePosixArtifactPath} && sentry-cli${options.selfHosted ? ` --url ${options.url}` : ''} sourcemaps upload --org ${options.orgSlug} --project ${options.projectSlug} ${relativePosixArtifactPath}`; const packageDotJson = await (0, clack_1.getPackageDotJson)(); packageDotJson.scripts = packageDotJson.scripts || {}; packageDotJson.scripts[SENTRY_NPM_SCRIPT_NAME] = sentryCliNpmScript; await fs.promises.writeFile(path.join(process.cwd(), 'package.json'), JSON.stringify(packageDotJson, null, 2)); clack.log.info(`Added a ${chalk_1.default.cyan(SENTRY_NPM_SCRIPT_NAME)} script to your ${chalk_1.default.cyan('package.json')}.`); } async function askShouldAddToBuildCommand() { const shouldAddToBuildCommand = await (0, clack_1.abortIfCancelled)(clack.select({ message: `Do you want to automatically run the ${chalk_1.default.cyan(SENTRY_NPM_SCRIPT_NAME)} script after each production build?`, options: [ { label: 'Yes', value: true, hint: 'This will modify your prod build command', }, { label: 'No', value: false }, ], initialValue: true, })); Sentry.setTag('modify-build-command', shouldAddToBuildCommand); return shouldAddToBuildCommand; } /** * Add the sentry:sourcemaps command to the prod build command in the package.json * - Detect the user's build command * - Append the sentry:sourcemaps command to it * * @param packageDotJson The package.json which will be modified. */ async function addSentryCommandToBuildCommand() { const packageDotJson = await (0, clack_1.getPackageDotJson)(); // This usually shouldn't happen because earlier we added the // SENTRY_NPM_SCRIPT_NAME script but just to be sure packageDotJson.scripts = packageDotJson.scripts || {}; const allNpmScripts = Object.keys(packageDotJson.scripts).filter((s) => s !== SENTRY_NPM_SCRIPT_NAME); const packageManager = await (0, clack_1.getPackageManager)(package_manager_1.NPM); // Heuristic to pre-select the build command: // Often, 'build' is the prod build command, so we favour it. // If it's not there, commands that include 'build' might be the prod build command. let buildCommand = typeof packageDotJson.scripts.build === 'string' ? 'build' : allNpmScripts.find((s) => s.toLocaleLowerCase().includes('build')); const isProdBuildCommand = !!buildCommand && (await (0, clack_1.abortIfCancelled)(clack.confirm({ message: `Is ${chalk_1.default.cyan(`${packageManager.runScriptCommand} ${buildCommand}`)} your production build command?`, }))); if (allNpmScripts.length && (!buildCommand || !isProdBuildCommand)) { buildCommand = await (0, clack_1.abortIfCancelled)(clack.select({ message: `Which ${packageManager.name} command in your ${chalk_1.default.cyan('package.json')} builds your application for production?`, options: allNpmScripts .map((script) => ({ label: script, value: script, })) .concat({ label: 'None of the above', value: 'none' }), })); } if (!buildCommand || buildCommand === 'none') { clack.log.warn(`We can only add the ${chalk_1.default.cyan(SENTRY_NPM_SCRIPT_NAME)} script to another \`script\` in your ${chalk_1.default.cyan('package.json')}. Please add it manually to your prod build command.`); return; } const oldCommand = packageDotJson.scripts[buildCommand]; if (!oldCommand) { // very unlikely to happen but nevertheless clack.log.warn(`\`${buildCommand}\` doesn't seem to be part of your package.json scripts`); return; } const newCommand = `${oldCommand} && ${packageManager.runScriptCommand} ${SENTRY_NPM_SCRIPT_NAME}`; if (oldCommand.endsWith(SENTRY_NPM_SCRIPT_NAME)) { clack.log.info(`It seems like ${chalk_1.default.cyan(SENTRY_NPM_SCRIPT_NAME)} is already part of your ${chalk_1.default.cyan(buildCommand)} command. Will not add it again. Current command: ${chalk_1.default.dim(oldCommand)} Would have injected: ${chalk_1.default.dim(newCommand)}`); return; } packageDotJson.scripts[buildCommand] = newCommand; await fs.promises.writeFile(path.join(process.cwd(), 'package.json'), JSON.stringify(packageDotJson, null, 2)); addedToBuildCommand = true; clack.log.info(`Added ${chalk_1.default.cyan(SENTRY_NPM_SCRIPT_NAME)} script to your ${chalk_1.default.cyan(buildCommand)} command.`); } exports.addSentryCommandToBuildCommand = addSentryCommandToBuildCommand; async function defaultConfigureSourcemapGenerationFlow() { await (0, clack_1.abortIfCancelled)(clack.select({ message: `Verify that your build tool is generating source maps. ${chalk_1.default.dim('(Your build output folder should contain .js.map files after a build)')}`, options: [{ label: 'I checked. Continue!', value: true }], initialValue: true, })); } //# sourceMappingURL=sentry-cli.js.map