UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

276 lines (273 loc) 13.2 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.getWranglerDeployCommand = exports.findOutDir = exports.safeInsertArgsToWranglerDeployCommand = exports.getSentryCliCommand = exports.configureWrangler = exports.DIST_DIR = 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 clack_1 = require("../../utils/clack"); const package_json_1 = require("../../utils/package-json"); const package_manager_1 = require("../../utils/package-manager"); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const yargs_1 = __importDefault(require("yargs")); const helpers_1 = require("yargs/helpers"); const SENTRY_NPM_SCRIPT_NAME = 'sentry:sourcemaps'; /** * only exported for testing */ exports.DIST_DIR = path_1.default.join('.', 'dist'); async function configureWrangler(options) { clack.note(chalk_1.default.whiteBright(`Configuring source maps upload with Cloudflare Wrangler requires the wizard to: - Modify your deploy command to access source maps - Set the SENTRY_RELEASE env var to identify source maps Note: This setup may need additional configuration. We recommend using Vite to build your worker instead, for an easier and more reliable setup. Learn more about CloudFlare's Vite setup here: ${chalk_1.default.underline(chalk_1.default.cyan('https://developers.cloudflare.com/workers/vite-plugin/get-started/'))} You can switch to Vite and re-run this wizard later. Otherwise, let's proceed with the Wrangler setup.`), 'Before we get started'); const proceed = await (0, clack_1.abortIfCancelled)(clack.confirm({ message: 'Do you want to proceed with the Wrangler setup?', })); if (!proceed) { await (0, clack_1.abort)('Got it! You can switch to Vite and re-run this wizard later.', 0); return; } await (0, clack_1.installPackage)({ packageName: '@sentry/cli', alreadyInstalled: (0, package_json_1.hasPackageInstalled)('@sentry/cli', await (0, clack_1.getPackageDotJson)()), devDependency: true, }); if (!(await askContinueIfHasSentrySourcemapsScript())) { return; } const deployCommand = await getDeployCommand(); if (!deployCommand) { return; } const outDir = await getWranglerOutDir(deployCommand); await createAndAddSentrySourcemapsScript({ ...options, outDir }); await writePostDeployCommand(deployCommand); await modifyDeployCommand(deployCommand, outDir); await (0, clack_1.addSentryCliConfig)({ authToken: options.authToken }); } exports.configureWrangler = configureWrangler; async function createAndAddSentrySourcemapsScript(options) { const pkgJson = await (0, clack_1.getPackageDotJson)(); pkgJson.scripts = pkgJson.scripts ?? {}; pkgJson.scripts[SENTRY_NPM_SCRIPT_NAME] = getSentryCliCommand(options); await fs_1.default.promises.writeFile(path_1.default.join(process.cwd(), 'package.json'), JSON.stringify(pkgJson, null, 2)); clack.log.success(`Added a ${chalk_1.default.cyan(SENTRY_NPM_SCRIPT_NAME)} script to your ${chalk_1.default.cyan('package.json')}.`); } /** * only exported for testing */ function getSentryCliCommand(options) { const sentryCliOptions = options.selfHosted ? ` --url ${options.url}` : ''; const orgAndProjectArgs = `--org=${options.orgSlug} --project=${options.projectSlug}`; const stripPrefixPath = `${options.outDir}${path_1.default.sep}..`; return [ '_SENTRY_RELEASE=$(sentry-cli releases propose-version)', `sentry-cli${sentryCliOptions} releases new $_SENTRY_RELEASE ${orgAndProjectArgs}`, `sentry-cli${sentryCliOptions} sourcemaps upload ${orgAndProjectArgs} --release=$_SENTRY_RELEASE --strip-prefix '${stripPrefixPath}' ${options.outDir}`, ].join(' && '); } exports.getSentryCliCommand = getSentryCliCommand; async function askContinueIfHasSentrySourcemapsScript() { const pkgJson = await (0, clack_1.getPackageDotJson)(); pkgJson.scripts = pkgJson.scripts ?? {}; if (pkgJson.scripts[SENTRY_NPM_SCRIPT_NAME]) { clack.log.warn(`The ${chalk_1.default.cyan(SENTRY_NPM_SCRIPT_NAME)} script already exists in your ${chalk_1.default.cyan('package.json')}. This likely means that you already ran this wizard once. If things don't work yet, try overwriting the script and continue with the wizard.`); const overwrite = await (0, clack_1.abortIfCancelled)(clack.select({ message: 'Do you want to overwrite it?', options: [ { label: 'Yes', value: true, hint: 'Overwrite the existing script' }, { label: 'No', value: false, hint: 'This will exit the wizard' }, ], })); if (!overwrite) { return false; } } return true; } async function getDeployCommand() { const pkgJson = await (0, clack_1.getPackageDotJson)(); const scripts = pkgJson.scripts ?? {}; let deployCommand = Object.keys(scripts).find((key) => /wrangler\s+deploy/.test(scripts[key] ?? '')); const packageManager = await (0, clack_1.getPackageManager)(package_manager_1.NPM); const isDeployCommand = !!deployCommand && (await (0, clack_1.abortIfCancelled)(clack.confirm({ message: `Is ${chalk_1.default.cyan(`${packageManager.runScriptCommand} ${deployCommand}`)} your build and deploy command?`, }))); if (Object.keys(scripts).length && (!deployCommand || !isDeployCommand)) { deployCommand = await (0, clack_1.abortIfCancelled)(clack.select({ message: `Which ${packageManager.name} command in your ${chalk_1.default.cyan('package.json')} builds your worker and deploys it?`, options: Object.keys(scripts) .map((script) => ({ label: script, value: script, })) .concat({ label: 'None of the above', value: 'none' }), })); } if (!deployCommand || deployCommand === '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 undefined; } return deployCommand; } async function writePostDeployCommand(deployCommand) { const pkgJson = await (0, clack_1.getPackageDotJson)(); const packageManager = await (0, clack_1.getPackageManager)(package_manager_1.NPM); pkgJson.scripts = pkgJson.scripts ?? {}; pkgJson.scripts[`post${deployCommand}`] = `${packageManager.runScriptCommand} ${SENTRY_NPM_SCRIPT_NAME}`; await fs_1.default.promises.writeFile(path_1.default.join(process.cwd(), 'package.json'), JSON.stringify(pkgJson, null, 2)); clack.log.success(`Added a ${chalk_1.default.cyan(`post${deployCommand}`)} script to your ${chalk_1.default.cyan('package.json')}.`); } async function modifyDeployCommand(deployCommand, outDir) { const pkgJson = await (0, clack_1.getPackageDotJson)(); pkgJson.scripts = pkgJson.scripts ?? {}; const oldDeployCommand = pkgJson.scripts[deployCommand]; if (!oldDeployCommand) { clack.log.warn(`The ${chalk_1.default.cyan(deployCommand)} script doesn't seem to be part of your package.json scripts anymore. Cannot modify it. Please modify it manually:`); await (0, clack_1.showCopyPasteInstructions)({ codeSnippet: `wrangler deploy --outdir ${outDir} --var SENTRY_RELEASE:$(sentry-cli releases propose-version) --upload-source-maps`, filename: 'package.json', }); return; } const newDeployCommand = safeInsertArgsToWranglerDeployCommand(oldDeployCommand, outDir); if (!newDeployCommand) { clack.log.warn(`The ${chalk_1.default.cyan(deployCommand)} script doesn't seem to be a valid ${chalk_1.default.cyan('wrangler deploy')} command. Cannot modify it. Please modify it manually:`); await (0, clack_1.showCopyPasteInstructions)({ codeSnippet: oldDeployCommand, filename: 'package.json', }); return; } pkgJson.scripts[deployCommand] = newDeployCommand; await fs_1.default.promises.writeFile(path_1.default.join(process.cwd(), 'package.json'), JSON.stringify(pkgJson, null, 2)); clack.log.success(`Modified your ${chalk_1.default.cyan(deployCommand)} script to enable uploading source maps.`); } /** * Takes care of inserting the necessary arguments into the deploy command. * Ensures that existing arguments and values are kept and that the * wrangler deploy command is valid. * * only exported for testing */ function safeInsertArgsToWranglerDeployCommand(deployCommand, outDir) { // split deployCommand into individual bash commands (potentially separated by &&, ||, >> etc.) const originalWranglerDeployCommand = getWranglerDeployCommand(deployCommand); if (!originalWranglerDeployCommand) { return undefined; } const existingArgs = originalWranglerDeployCommand .split(' ') .map((arg) => arg.trim()) .filter(Boolean); const parsedArgs = (0, yargs_1.default)((0, helpers_1.hideBin)(existingArgs)).parse(); const newArgs = []; if (!parsedArgs.outdir) { newArgs.push('--outdir', outDir); } // Adding --upload-source-maps saves us from having to // modify the `wrangler.toml` or `wrangler.jsonc` files. // Not ideal because this forces source maps to be uploaded // but we'll live with it for now. if (!parsedArgs['upload-source-maps']) { newArgs.push('--upload-source-maps'); } // This is how we inject the SENTRY_RELEASE variable, // which is picked up by the CloudFlare SDK. // multiple --var arguments are allowed, so no need to check for existing --var arguments. newArgs.push('--var', 'SENTRY_RELEASE:$(sentry-cli releases propose-version)'); return deployCommand .replace(originalWranglerDeployCommand, `${originalWranglerDeployCommand} ${newArgs.join(' ')} `) .trim(); } exports.safeInsertArgsToWranglerDeployCommand = safeInsertArgsToWranglerDeployCommand; /** * Look up an already specified --outdir argument and return it if found. * Otherwise, we defined `dist` as the default outdir. */ async function getWranglerOutDir(deployScript) { const pkgJson = await (0, clack_1.getPackageDotJson)(); const scripts = pkgJson.scripts ?? {}; const deployCommand = scripts[deployScript]; if (!deployCommand) { return exports.DIST_DIR; } return findOutDir(deployCommand); } /** * only exported for testing */ function findOutDir(deployCommand) { const args = getWranglerDeployCommand(deployCommand) ?.split(' ') .map((arg) => arg.trim()); if (!args) { return exports.DIST_DIR; } const outDirArgIndex = args.findIndex((arg) => arg.startsWith('--outdir')); if (outDirArgIndex === -1) { return exports.DIST_DIR; } const outDirArg = args[outDirArgIndex]; if (outDirArg.startsWith('--outdir=')) { return outDirArg.split('=')[1].trim().replace(/['"]/g, ''); } const maybeOutDir = args[outDirArgIndex + 1]; if (maybeOutDir && !maybeOutDir.startsWith('--')) { return maybeOutDir.replace(/['"]/g, ''); } return exports.DIST_DIR; } exports.findOutDir = findOutDir; /** * Exported for testing */ function getWranglerDeployCommand(deployCommand) { const individualCommands = deployCommand.split(/&&|\|\||>>|>|<|\||;/); const originalWranglerDeployCommand = individualCommands.find((cmd) => { const argv = cmd .split(' ') .map((arg) => arg.trim()) .filter(Boolean); return argv[0] === 'wrangler' && argv.includes('deploy'); }); return originalWranglerDeployCommand; } exports.getWranglerDeployCommand = getWranglerDeployCommand; //# sourceMappingURL=wrangler.js.map