@sentry/wizard
Version:
Sentry wizard helping you to configure your project
213 lines • 10.8 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.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