Skip to content

Commit

Permalink
feat(sourcemaps): Check if correct SDK version is installed (#336)
Browse files Browse the repository at this point in the history
Add a mechanism to check if users installed an SDK version `>=7.47.0` to be compatible with debug-id-based source maps. During this check, we distinguish between four cases:

1. Users didn't install any SDK yet
   -> We tell them to install an SDK and then continue with the wizard
2. Users installed an SDK in the range >=7.47.0
   -> All good, no need to do anything!
3. Users installed an SDK in the range >=7.0.0 <= 7.46.0
   -> We ask if they want to auto-update to the latest version
4. Users installed an SDK in the range <7.x
   -> We tell users to manually upgrade (migrate between majors) and then continue with the wizard
  • Loading branch information
Lms24 committed Jul 6, 2023
1 parent 802fccf commit 50fd5b1
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- feat(sourcemaps): Check if correct SDK version is installed (#336)
- feat: Open browser when logging in (sourcemaps, sveltekit, nextjs) (#328)
- feat(sourcmaps): Add create-react-app option (#335)
- fix: Support `--url` arg in NextJs, SvelteKit and Sourcemaps wizards (#331)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"opn": "^5.4.0",
"r2": "^2.0.1",
"read-env": "^1.3.0",
"semver": "^7.3.5",
"semver": "^7.5.3",
"xcode": "3.0.1",
"yargs": "^16.2.0"
},
Expand Down
3 changes: 3 additions & 0 deletions src/sourcemaps/sourcemaps-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { configureRollupPlugin } from './tools/rollup';
import { configureEsbuildPlugin } from './tools/esbuild';
import { WizardOptions } from '../utils/types';
import { configureCRASourcemapGenerationFlow } from './tools/create-react-app';
import { ensureMinimumSdkVersionIsInstalled } from './utils/sdk-version';

type SupportedTools =
| 'webpack'
Expand All @@ -43,6 +44,8 @@ export async function runSourcemapsWizard(

await confirmContinueEvenThoughNoGitRepo();

await ensureMinimumSdkVersionIsInstalled();

const { url: sentryUrl, selfHosted } = await askForSelfHosted(options.url);

const { projects, apiKeys } = await askForWizardLogin({
Expand Down
264 changes: 264 additions & 0 deletions src/sourcemaps/utils/sdk-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
// @ts-ignore - clack is ESM and TS complains about that. It works though
import clack from '@clack/prompts';
import chalk from 'chalk';
import { minVersion, satisfies } from 'semver';
import {
abortIfCancelled,
getPackageDotJson,
getPackageVersion,
installPackage,
} from '../../utils/clack-utils';

import * as Sentry from '@sentry/node';

const MINIMUM_DEBUG_ID_SDK_VERSION = '7.47.0';

// This array is orderd by the SDKs we want to check for first.
// The reason is that some SDKs depend on others and some users might
// have added the dependencies to their package.json. We want to make sure
// that we actually detect the "top-level" SDK first.
const SENTRY_SDK_PACKAGE_NAMES = [
// SDKs using other framework SDKs need to be checked first
'@sentry/gatsby',
'@sentry/nextjs',
'@sentry/remix',
'@sentry/sveltekit',

// Framework SDKs
'@sentry/angular',
'@sentry/angular-ivy',
'@sentry/ember',
'@sentry/react',
'@sentry/svelte',
'@sentry/vue',
'@sentry/serverless',

// Base SDKs
'@sentry/browser',
'@sentry/node',
] as const;

type SdkPackage = {
name: (typeof SENTRY_SDK_PACKAGE_NAMES)[number];
version: string;
};

/**
* Check for a minimum SDK version and prompt the user to upgrade if necessary.
* We distinguish between 4 cases here:
*
* 1. Users didn't install any SDK yet
* -> We tell them to install an SDK and then continue with the wizard
* 2. Users installed an SDK in the range >=7.47.0
* -> All good, no need to do anything!
* 3. Users installed an SDK in the range >=7.0.0 <= 7.46.0
* -> We ask if they want to auto-update to the latest version
* 4. Users installed an SDK in the range <7.x
* -> We tell users to manually upgrade (migrate between majors)
*/
export async function ensureMinimumSdkVersionIsInstalled(): Promise<void> {
const packageJson = await getPackageDotJson();

const installedSdkPackages = SENTRY_SDK_PACKAGE_NAMES.map((packageName) => ({
name: packageName,
version: getPackageVersion(packageName, packageJson),
})).filter((sdkPackage): sdkPackage is SdkPackage => !!sdkPackage.version);

const installedSdkPackage =
installedSdkPackages.length > 0 && installedSdkPackages[0];

// Case 1:
if (!installedSdkPackage) {
return await handleNoSdkInstalled();
}

const { name: installedSdkName, version: installedSdkVersionOrRange } =
installedSdkPackage;

const minInstalledVersion = getMinInstalledVersion(
installedSdkVersionOrRange,
installedSdkName,
);

if (!minInstalledVersion) {
// This is handled in the getMinInstalledVersion function
return;
}

const hasDebugIdCompatibleSdkVersion = satisfies(
minInstalledVersion,
`>=${MINIMUM_DEBUG_ID_SDK_VERSION}`,
);

// Case 2:
if (hasDebugIdCompatibleSdkVersion) {
Sentry.setTag('initial-sdk-version', '>=7.47.0');
return;
}

const hasV7SdkVersion = satisfies(minInstalledVersion, '>=7.0.0');

clack.log.warn(
`${chalk.yellowBright(
`It seems like you're using an outdated version (${installedSdkVersionOrRange}) of the ${chalk.bold(
installedSdkName,
)} SDK.`,
)}
Uploading source maps is easiest with an SDK from version ${chalk.bold(
MINIMUM_DEBUG_ID_SDK_VERSION,
)} or newer.
`,
);

// Case 3:
if (hasV7SdkVersion) {
await handleAutoUpdateSdk(installedSdkName);
return;
}

// Case 4:
await handleManuallyUpdateSdk(minInstalledVersion);
}

async function handleManuallyUpdateSdk(minInstalledVersion: string) {
Sentry.setTag(
'initial-sdk-version',
`${satisfies(minInstalledVersion, '>=6.0.0') ? '6.x' : '<6.0.0'}`,
);

clack.log
.info(`When upgrading from a version older than 7.0.0, make sure to follow the migration guide:
https://github.com/getsentry/sentry-javascript/blob/develop/MIGRATION.md#upgrading-from-6x-to-7x
`);

const didUpdate = await abortIfCancelled(
clack.select({
message: 'Did you update your SDK to the latest version?',
options: [
{
label: 'Yes!',
value: true,
},
{
label: "No, I'll do it later...",
value: false,
hint: chalk.yellow(
`Remember to update your SDK to at least ${MINIMUM_DEBUG_ID_SDK_VERSION}.`,
),
},
],
initialValue: true,
}),
);

Sentry.setTag(
'resolved-sdk-status',
didUpdate ? 'updated-manually' : 'update-later',
);
}

async function handleAutoUpdateSdk(packageName: string) {
Sentry.setTag('initial-sdk-version', '>=7.0.0 <= 7.47.0');

const shouldUpdate = await abortIfCancelled(
clack.select({
message:
'Do you want to automatically update your SDK to the latest version?',
options: [
{
label: 'Yes!',
value: true,
hint: chalk.green('Recommended'),
},
{
label: "No, I'll do it later...",
value: false,
hint: chalk.yellow(
`Remember to update your SDK to at least ${MINIMUM_DEBUG_ID_SDK_VERSION}.`,
),
},
],
initialValue: true,
}),
);

if (shouldUpdate) {
await installPackage({
packageName,
alreadyInstalled: true,
askBeforeUpdating: false, // we already did this above
});
}

Sentry.setTag(
'resolved-sdk-status',
shouldUpdate ? 'updated-automatically' : 'update-later',
);
}

async function handleNoSdkInstalled(): Promise<void> {
Sentry.setTag('initial-sdk-version', 'none');

clack.log.warn(
`${chalk.yellowBright(
`It seems like you didn't yet install a Sentry SDK in your project.`,
)}
We recommend setting up the SDK before continuing with the source maps wizard.
${chalk.dim(`Take a look at our docs to get started:
https://docs.sentry.io/`)}`,
);

const installedSDK = await abortIfCancelled(
clack.select({
message: 'Did you set up your Sentry SDK?',
options: [
{ label: 'Yes, continue!', value: true },
{
label: "I'll do it later...",
value: false,
hint: chalk.yellow(
'You need to set up an SDK before you can use Sentry',
),
},
],
initialValue: true,
}),
);

Sentry.setTag(
'resolved-sdk-status',
installedSDK ? 'installed-manually' : 'install-later',
);
}

function getMinInstalledVersion(
installedSdkVersionOrRange: string,
installedSdkName: string,
): string | undefined {
try {
// If `minVersion` is unable to parse the version it will throw an error
// However, it will also return `null` if the parameter is undefined, which
// we explicitly checked before but the typing doesn't know that.
const minInstalledVersion = minVersion(installedSdkVersionOrRange)?.version;
if (minInstalledVersion) {
return minInstalledVersion;
}
} catch {
// handling this, along with the `null` case below
}

Sentry.setTag('initial-sdk-version', 'unknown');
clack.log.warn(
`${chalk.yellow(
`Could not parse the version of your installed SDK ("${installedSdkName}": "${installedSdkVersionOrRange}")`,
)}
Please make sure that your Sentry SDK is updated to version ${chalk.bold(
MINIMUM_DEBUG_ID_SDK_VERSION,
)} or newer.
`,
);

return undefined;
}
15 changes: 12 additions & 3 deletions src/utils/clack-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,11 +259,13 @@ export async function askForProjectSelection(
export async function installPackage({
packageName,
alreadyInstalled,
askBeforeUpdating = true,
}: {
packageName: string;
alreadyInstalled: boolean;
askBeforeUpdating?: boolean;
}): Promise<void> {
if (alreadyInstalled) {
if (alreadyInstalled && askBeforeUpdating) {
const shouldUpdatePackage = await abortIfCancelled(
clack.confirm({
message: `The ${chalk.bold.cyan(
Expand Down Expand Up @@ -586,9 +588,16 @@ export function hasPackageInstalled(
packageName: string,
packageJson: PackageDotJson,
): boolean {
return getPackageVersion(packageName, packageJson) !== undefined;
}

export function getPackageVersion(
packageName: string,
packageJson: PackageDotJson,
): string | undefined {
return (
!!packageJson?.dependencies?.[packageName] ||
!!packageJson?.devDependencies?.[packageName]
packageJson?.dependencies?.[packageName] ||
packageJson?.devDependencies?.[packageName]
);
}

Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3975,6 +3975,13 @@ semver@^7.3.7:
dependencies:
lru-cache "^6.0.0"

semver@^7.5.3:
version "7.5.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e"
integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==
dependencies:
lru-cache "^6.0.0"

set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
Expand Down

0 comments on commit 50fd5b1

Please sign in to comment.