Skip to content

Commit

Permalink
feat(nuxt): Add downgrade path to nitro 2.9.7 (#725)
Browse files Browse the repository at this point in the history
* feat(nuxt): Add downgrade path to nitro 2.9.7

Also adds automatic resolutions for @vercel/nft and ofetch.
See: getsentry/sentry-javascript#14514

* Update changelog

* Rewrite logic to just add overrides and not install deps

* Make clack utility for package overrides generic

* Pin nitropack version to ~2.9.7
  • Loading branch information
andreiborza authored Nov 29, 2024
1 parent 8af901c commit 6ac7776
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

- Remove Profiling for Android ([#720](https://github.com/getsentry/sentry-wizard/pull/720))
- Add downgrade path to nitro 2.9.7 ([#725](https://github.com/getsentry/sentry-wizard/pull/725))

## 3.35.0

Expand Down
29 changes: 26 additions & 3 deletions e2e-tests/tests/nuxt-3.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,36 @@ async function runWizardOnNuxtProject(projectDir: string): Promise<void> {
'Please select your package manager.',
);

const tracingOptionPrompted =
const nitropackOverridePrompted =
packageManagerPrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
// Selecting `yarn` as the package manager
[KEYS.DOWN, KEYS.ENTER],
// Do you want to install version 2.9.7 of nitropack and add an override to package.json?
'Do you want to add an override for nitropack version ~2.9.7?',
{
timeout: 240_000,
},
));

const nftOverridePrompted =
nitropackOverridePrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
// Selecting `yes` to downgrade nitropack
KEYS.ENTER,
'Do you want to add an override for @vercel/nft version ^0.27.4?',
// 'Do you want to install version',
{
timeout: 240_000,
},
));

const tracingOptionPrompted =
nftOverridePrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
KEYS.ENTER,
// "Do you want to enable Tracing", sometimes doesn't work as `Tracing` can be printed in bold.
'to track the performance of your application?',
'Do you want to enable',
{
timeout: 240_000,
},
Expand All @@ -59,7 +82,7 @@ async function runWizardOnNuxtProject(projectDir: string): Promise<void> {
const replayOptionPrompted =
tracingOptionPrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
[KEYS.ENTER],
KEYS.ENTER,
// "Do you want to enable Sentry Session Replay", sometimes doesn't work as `Sentry Session Replay` can be printed in bold.
'to get a video-like reproduction of errors during a user session?',
));
Expand Down
27 changes: 25 additions & 2 deletions e2e-tests/tests/nuxt-4.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,36 @@ async function runWizardOnNuxtProject(projectDir: string): Promise<void> {
'Please select your package manager.',
);

const tracingOptionPrompted =
const nitropackOverridePrompted =
packageManagerPrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
// Selecting `yarn` as the package manager
[KEYS.DOWN, KEYS.ENTER],
// Do you want to install version 2.9.7 of nitropack and add an override to package.json?
'Do you want to add an override for nitropack version ~2.9.7?',
{
timeout: 240_000,
},
));

const nftOverridePrompted =
nitropackOverridePrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
// Selecting `yes` to downgrade nitropack
KEYS.ENTER,
'Do you want to add an override for @vercel/nft version ^0.27.4?',
// 'Do you want to install version',
{
timeout: 240_000,
},
));

const tracingOptionPrompted =
nftOverridePrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
KEYS.ENTER,
// "Do you want to enable Tracing", sometimes doesn't work as `Tracing` can be printed in bold.
'to track the performance of your application?',
'Do you want to enable',
{
timeout: 240_000,
},
Expand Down
5 changes: 4 additions & 1 deletion e2e-tests/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,10 @@ export function createFile(filePath: string, content?: string) {
* @param oldContent
* @param newContent
*/
export function modifyFile(filePath: string, replaceMap: Record<string, string>) {
export function modifyFile(
filePath: string,
replaceMap: Record<string, string>,
) {
const fileContent = fs.readFileSync(filePath, 'utf-8');
let newFileContent = fileContent;

Expand Down
15 changes: 13 additions & 2 deletions src/nuxt/nuxt-wizard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-ignore - clack is ESM and TS complains about that. It works though
import * as clack from '@clack/prompts';
import * as Sentry from '@sentry/node';
import chalk from 'chalk';
import { lt, minVersion } from 'semver';
import type { WizardOptions } from '../utils/types';
import { traceStep, withTelemetry } from '../telemetry';
Expand All @@ -14,19 +15,24 @@ import {
ensurePackageIsInstalled,
getOrAskForProjectData,
getPackageDotJson,
getPackageManager,
installPackage,
printWelcome,
runPrettierIfInstalled,
} from '../utils/clack-utils';
import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
import { addSDKModule, getNuxtConfig, createConfigFiles } from './sdk-setup';
import {
addSDKModule,
getNuxtConfig,
createConfigFiles,
addNuxtOverrides,
} from './sdk-setup';
import {
createExampleComponent,
createExamplePage,
supportsExamplePage,
} from './sdk-example';
import { isNuxtV4 } from './utils';
import chalk from 'chalk';

export function runNuxtWizard(options: WizardOptions) {
return withTelemetry(
Expand Down Expand Up @@ -85,12 +91,17 @@ export async function runNuxtWizardWithTelemetry(
const { authToken, selectedProject, selfHosted, sentryUrl } =
await getOrAskForProjectData(options, 'javascript-nuxt');

const packageManager = await getPackageManager();

await addNuxtOverrides(packageManager, minVer);

const sdkAlreadyInstalled = hasPackageInstalled('@sentry/nuxt', packageJson);
Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);

await installPackage({
packageName: '@sentry/nuxt',
alreadyInstalled: sdkAlreadyInstalled,
packageManager,
});

await addDotEnvSentryBuildPluginFile(authToken);
Expand Down
39 changes: 39 additions & 0 deletions src/nuxt/sdk-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ import {
import {
abort,
abortIfCancelled,
askShouldAddPackageOverride,
featureSelectionPrompt,
isUsingTypeScript,
} from '../utils/clack-utils';
import { traceStep } from '../telemetry';
import { lt, SemVer } from 'semver';
import { PackageManager } from '../utils/package-manager';

const possibleNuxtConfig = [
'nuxt.config.js',
Expand Down Expand Up @@ -207,3 +210,39 @@ export async function createConfigFiles(dsn: string) {
});
}
}

export async function addNuxtOverrides(
packageManager: PackageManager,
nuxtMinVer: SemVer | null,
) {
const overrides = [
{
pkgName: 'nitropack',
pkgVersion: '~2.9.7',
},
{
pkgName: '@vercel/nft',
pkgVersion: '^0.27.4',
},
...(nuxtMinVer && lt(nuxtMinVer, '3.14.0')
? [{ pkgName: 'ofetch', pkgVersion: '^1.4.0' }]
: []),
];

clack.log.warn(
`To ensure Sentry can properly instrument your code it needs to add version overrides for some Nuxt dependencies.\n\nFor more info see: ${chalk.cyan(
'https://github.com/getsentry/sentry-javascript/issues/14514',
)}`,
);

for (const { pkgName, pkgVersion } of overrides) {
const shouldAddOverride = await askShouldAddPackageOverride(
pkgName,
pkgVersion,
);

if (shouldAddOverride) {
await packageManager.addOverride(pkgName, pkgVersion);
}
}
}
47 changes: 42 additions & 5 deletions src/utils/clack-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,13 +355,15 @@ export async function installPackage({
alreadyInstalled,
askBeforeUpdating = true,
packageNameDisplayLabel,
packageManager,
}: {
/** The string that is passed to the package manager CLI as identifier to install (e.g. `@sentry/nextjs`, or `@sentry/nextjs@^8`) */
packageName: string;
alreadyInstalled: boolean;
askBeforeUpdating?: boolean;
/** Overrides what is shown in the installation logs in place of the `packageName` option. Useful if the `packageName` is ugly (e.g. `@sentry/nextjs@^8`) */
packageNameDisplayLabel?: string;
packageManager?: PackageManager;
}): Promise<{ packageManager?: PackageManager }> {
return traceStep('install-package', async () => {
if (alreadyInstalled && askBeforeUpdating) {
Expand All @@ -380,18 +382,18 @@ export async function installPackage({

const sdkInstallSpinner = clack.spinner();

const packageManager = await getPackageManager();
const pkgManager = packageManager || (await getPackageManager());

sdkInstallSpinner.start(
`${alreadyInstalled ? 'Updating' : 'Installing'} ${chalk.bold.cyan(
packageNameDisplayLabel ?? packageName,
)} with ${chalk.bold(packageManager.label)}.`,
)} with ${chalk.bold(pkgManager.label)}.`,
);

try {
await new Promise<void>((resolve, reject) => {
childProcess.exec(
`${packageManager.installCommand} ${packageName} ${packageManager.flags}`,
`${pkgManager.installCommand} ${packageName} ${pkgManager.flags}`,
(err, stdout, stderr) => {
if (err) {
// Write a log file so we can better troubleshoot issues
Expand Down Expand Up @@ -430,10 +432,10 @@ export async function installPackage({
sdkInstallSpinner.stop(
`${alreadyInstalled ? 'Updated' : 'Installed'} ${chalk.bold.cyan(
packageNameDisplayLabel ?? packageName,
)} with ${chalk.bold(packageManager.label)}.`,
)} with ${chalk.bold(pkgManager.label)}.`,
);

return { packageManager };
return { packageManager: pkgManager };
});
}

Expand Down Expand Up @@ -808,6 +810,26 @@ export async function getPackageDotJson(): Promise<PackageDotJson> {
return packageJson || {};
}

export async function updatePackageDotJson(
packageDotJson: PackageDotJson,
): Promise<void> {
try {
await fs.promises.writeFile(
path.join(process.cwd(), 'package.json'),
// TODO: maybe figure out the original indentation
JSON.stringify(packageDotJson, null, 2),
{
encoding: 'utf8',
flag: 'w',
},
);
} catch {
clack.log.error(`Unable to update your ${chalk.cyan('package.json')}.`);

await abort();
}
}

export async function getPackageManager(): Promise<PackageManager> {
const detectedPackageManager = detectPackageManger();

Expand Down Expand Up @@ -1469,3 +1491,18 @@ export async function featureSelectionPrompt<F extends ReadonlyArray<Feature>>(
return selectedFeatures as { [key in F[number]['id']]: boolean };
});
}

export async function askShouldAddPackageOverride(
pkgName: string,
pkgVersion: string,
): Promise<boolean> {
return traceStep(`ask-add-package-override`, () =>
abortIfCancelled(
clack.confirm({
message: `Do you want to add an override for ${chalk.cyan(
pkgName,
)} version ${chalk.cyan(pkgVersion)}?`,
}),
),
);
}
5 changes: 5 additions & 0 deletions src/utils/package-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ export type PackageDotJson = {
scripts?: Record<string, string | undefined>;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
resolutions?: Record<string, string>;
overrides?: Record<string, string>;
pnpm?: {
overrides?: Record<string, string>;
};
};

type NpmPackage = {
Expand Down
Loading

0 comments on commit 6ac7776

Please sign in to comment.