@sentry/wizard
Version:
Sentry wizard helping you to configure your project
276 lines (273 loc) • 13.2 kB
JavaScript
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
;