Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nuxt): Add downgrade path to nitro 2.9.7 #725

Merged
merged 5 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
'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 @@
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 All @@ -84,27 +107,27 @@
function testNuxtProjectSetup(projectDir: string) {
const integration = Integration.nuxt;

test('package.json is updated correctly', () => {

Check warning on line 110 in e2e-tests/tests/nuxt-3.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
checkPackageJson(projectDir, integration);
});

test('.env-sentry-build-plugin is created and contains the auth token', () => {

Check warning on line 114 in e2e-tests/tests/nuxt-3.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
checkEnvBuildPlugin(projectDir);
});

test('config files created', () => {

Check warning on line 118 in e2e-tests/tests/nuxt-3.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
checkFileExists(`${projectDir}/sentry.server.config.ts`);
checkFileExists(`${projectDir}/sentry.client.config.ts`);
});

test('example page exists', () => {

Check warning on line 123 in e2e-tests/tests/nuxt-3.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
checkFileExists(`${projectDir}/pages/sentry-example-page.vue`);
checkFileExists(`${projectDir}/server/api/sentry-example-api.ts`);
});
}

function testNuxtProjectConfigs(projectDir: string) {
test('nuxt config contains sentry module', () => {

Check warning on line 130 in e2e-tests/tests/nuxt-3.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
checkFileContents(path.resolve(projectDir, 'nuxt.config.ts'), [
"modules: ['@sentry/nuxt/module'],",
'sentry: {',
Expand All @@ -119,7 +142,7 @@
]);
});

test('sentry.client.config.ts contents', () => {

Check warning on line 145 in e2e-tests/tests/nuxt-3.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
checkFileContents(path.resolve(projectDir, 'sentry.client.config.ts'), [
'import * as Sentry from "@sentry/nuxt";',
'Sentry.init({',
Expand All @@ -143,7 +166,7 @@
]);
});

test('sentry.server.config.ts contents', () => {

Check warning on line 169 in e2e-tests/tests/nuxt-3.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
checkFileContents(path.resolve(projectDir, 'sentry.server.config.ts'), [
'import * as Sentry from "@sentry/nuxt";',
'Sentry.init({',
Expand All @@ -159,11 +182,11 @@
}

function testNuxtProjectBuildsAndRuns(projectDir: string) {
test('builds successfully', async () => {

Check warning on line 185 in e2e-tests/tests/nuxt-3.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
await checkIfBuilds(projectDir, 'preview this build');
});

test('runs on prod mode correctly', async () => {

Check warning on line 189 in e2e-tests/tests/nuxt-3.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
await checkIfRunsOnProdMode(projectDir, 'Listening on');
});
}
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 @@
'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 Expand Up @@ -83,7 +106,7 @@
function testNuxtProjectSetup(projectDir: string) {
const integration = Integration.nuxt;

test('package.json is updated correctly', () => {

Check warning on line 109 in e2e-tests/tests/nuxt-4.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Test has no assertions
checkPackageJson(projectDir, integration);
});

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
Loading