Skip to content

Commit

Permalink
fix(core): not exit when one plugin installation failed
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongemi committed Dec 7, 2024
1 parent de8b189 commit 50d0dd7
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 73 deletions.
4 changes: 3 additions & 1 deletion packages/js/src/utils/typescript/ts-solution-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ export function assertNotUsingTsSolutionSetup(
],
});

process.exit(1);
throw new Error(
`The ${artifactString} doesn't yet support the existing TypeScript setup. See the error above.`
);
}

export function findRuntimeTsConfigName(
Expand Down
104 changes: 71 additions & 33 deletions packages/nx/src/command-line/import/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { tmpdir } from 'tmp';
import { prompt } from 'enquirer';
import { output } from '../../utils/output';
import * as createSpinner from 'ora';
import { detectPlugins, installPlugins } from '../init/init-v2';
import { detectPlugins } from '../init/init-v2';
import { readNxJson } from '../../config/nx-json';
import { workspaceRoot } from '../../utils/workspace-root';
import {
Expand All @@ -29,6 +29,11 @@ import {
needsInstall,
} from './utils/needs-install';
import { minimatch } from 'minimatch';
import { readPackageJson } from '../../project-graph/file-utils';
import {
configurePlugins,
runPackageManagerInstallPlugins,
} from '../init/configure-plugins';

const importRemoteName = '__tmp_nx_import__';

Expand Down Expand Up @@ -60,7 +65,7 @@ export interface ImportOptions {

export async function importHandler(options: ImportOptions) {
process.env.NX_RUNNING_NX_IMPORT = 'true';
let { sourceRepository, ref, source, destination } = options;
let { sourceRepository, ref, source, destination, verbose } = options;
const destinationGitClient = new GitRepository(process.cwd());

if (await destinationGitClient.hasUncommittedChanges()) {
Expand Down Expand Up @@ -287,42 +292,28 @@ export async function importHandler(options: ImportOptions) {
destinationGitClient
);

// If install fails, we should continue since the errors could be resolved later.
let installFailed = false;
if (plugins.length > 0) {
try {
output.log({ title: 'Installing Plugins' });
installPlugins(workspaceRoot, plugins, pmc, updatePackageScripts);

await destinationGitClient.amendCommit();
} catch (e) {
installFailed = true;
output.error({
title: `Install failed: ${e.message || 'Unknown error'}`,
bodyLines: [e.stack],
});
}
} else if (await needsInstall(packageManager, originalPackageWorkspaces)) {
try {
output.log({
title: 'Installing dependencies for imported code',
});

runInstall(workspaceRoot, getPackageManagerCommand(packageManager));
const installed = await runInstallDestinationRepo(
plugins,
pmc,
packageManager,
originalPackageWorkspaces,
destinationGitClient
);

await destinationGitClient.amendCommit();
} catch (e) {
installFailed = true;
output.error({
title: `Install failed: ${e.message || 'Unknown error'}`,
bodyLines: [e.stack],
});
}
if (installed && plugins.length > 0) {
await configurePlugins(
plugins,
updatePackageScripts,
pmc,
workspaceRoot,
verbose,
destinationGitClient
);
}

console.log(await destinationGitClient.showStat());

if (installFailed) {
if (installed === false) {
const pmc = getPackageManagerCommand(packageManager);
output.warn({
title: `The import was successful, but the install failed`,
Expand Down Expand Up @@ -397,6 +388,53 @@ async function createTemporaryRemote(
}

/**
* Run install for the imported code and plugins
* @returns true if the install failed
*/
async function runInstallDestinationRepo(
plugins: string[],
pmc: PackageManagerCommands,
packageManager: PackageManager,
originalPackageWorkspaces: Set<string>,
destinationGitClient: GitRepository
): Promise<boolean> {
// If install fails, we should continue since the errors could be resolved later.
let installed = true;
if (plugins.length > 0) {
output.log({ title: 'Installing Plugins' });
try {
runPackageManagerInstallPlugins(workspaceRoot, pmc, plugins);
await destinationGitClient.amendCommit();
} catch (e) {
installed = false;
output.error({
title: `Install failed: ${e.message || 'Unknown error'}`,
bodyLines: [
'The following plugins were not installed:',
...plugins.map((p) => `- ${chalk.bold(p)}`),
e.stack,
],
});
}
} else if (await needsInstall(packageManager, originalPackageWorkspaces)) {
try {
output.log({
title: 'Installing dependencies for imported code',
});
runInstall(workspaceRoot, getPackageManagerCommand(packageManager));
await destinationGitClient.amendCommit();
} catch (e) {
installed = false;
output.error({
title: `Install failed: ${e.message || 'Unknown error'}`,
bodyLines: [e.stack],
});
}
}
return installed;
}

/*
* If the user imports a project that isn't in the workspaces entry, we should add that path to the workspaces entry.
*/
async function handleMissingWorkspacesEntry(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as createSpinner from 'ora';
import { dirname, join, relative } from 'path';
import { mkdir, rm } from 'node:fs/promises';
import { join, relative } from 'path';
import { GitRepository } from '../../../utils/git-utils';

export async function prepareSourceRepo(
Expand Down
155 changes: 155 additions & 0 deletions packages/nx/src/command-line/init/configure-plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import * as createSpinner from 'ora';
import { bold } from 'chalk';

import {
getPackageManagerCommand,
PackageManagerCommands,
} from '../../utils/package-manager';
import { GitRepository } from '../../utils/git-utils';
import { output } from '../../utils/output';
import { flushChanges, FsTree } from '../../generators/tree';
import {
Generator as NxGenerator,
GeneratorCallback,
} from '../../config/misc-interfaces';
import { getGeneratorInformation } from '../generate/generator-utils';
import { workspaceRoot } from '../../utils/workspace-root';
import { addDepsToPackageJson, runInstall } from './implementation/utils';

export function runPackageManagerInstallPlugins(
repoRoot: string,
pmc: PackageManagerCommands = getPackageManagerCommand(),
plugins: string[]
) {
if (plugins.length === 0) {
return;
}
addDepsToPackageJson(repoRoot, plugins);
runInstall(repoRoot, pmc);
}

/**
* Install plugins
* Get the implementation of the plugin's init generator and run it
* @returns a list of succeeded plugins and a map of failed plugins to errors
*/
export async function installPlugins(
plugins: string[],
updatePackageScripts: boolean,
repoRoot: string = workspaceRoot,
verbose: boolean = false
): Promise<{
succeededPlugins: string[];
failedPlugins: { [plugin: string]: Error };
}> {
if (plugins.length === 0) {
return {
succeededPlugins: [],
failedPlugins: {},
};
}
output.log({ title: '🔨 Configuring plugins' });
const spinner = createSpinner();
let succeededPlugins = [];
const failedPlugins: {
[pluginName: string]: Error;
} = {};

for (const plugin of plugins) {
const host = new FsTree(repoRoot, verbose, `install ${plugin}`);
spinner.start('Installing plugin ' + plugin);
try {
const { implementationFactory } = getGeneratorInformation(
plugin,
'init',
repoRoot,
{}
);
const implementation: NxGenerator = implementationFactory();
const task: GeneratorCallback | void = await implementation(host, {
keepExistingVersions: true,
updatePackageScripts,
});
if (task) {
await task();
}
succeededPlugins.push(plugin);
spinner.succeed('Installed plugin ' + plugin);
const changes = host.listChanges();
flushChanges(repoRoot, changes);
} catch (e) {
failedPlugins[plugin] = e;
spinner.fail('Failed to install plugin ' + plugin);
}
}

return {
succeededPlugins,
failedPlugins,
};
}

/**
* Configures plugins, installs them, and outputs the results
* @returns a list of succeeded plugins and a map of failed plugins to errors
*/
export async function configurePlugins(
plugins: string[],
updatePackageScripts: boolean,
pmc: PackageManagerCommands,
repoRoot: string = workspaceRoot,
verbose: boolean = false,
destinationGitClient?: GitRepository
): Promise<{
succeededPlugins: string[];
failedPlugins: { [plugin: string]: Error };
}> {
if (plugins.length === 0) {
return {
succeededPlugins: [],
failedPlugins: {},
};
}

let { succeededPlugins, failedPlugins } = await installPlugins(
plugins,
updatePackageScripts,
repoRoot,
verbose
);

if (succeededPlugins.length > 0) {
if (destinationGitClient) {
destinationGitClient.amendCommit();
}
output.success({
title: 'Installed Plugins',
bodyLines: succeededPlugins.map((p) => `- ${bold(p)}`),
});
}
if (Object.keys(failedPlugins).length > 0) {
output.error({
title: `Failed to install plugins`,
bodyLines: [
'The following plugins were not installed:',
...Object.keys(failedPlugins).map((p) => `- ${bold(p)}`),
],
});
Object.entries(failedPlugins).forEach(([plugin, error]) => {
output.error({
title: `Failed to install ${plugin}`,
bodyLines: [error.stack ?? error.message ?? error.toString()],
});
});
output.error({
title: `To install the plugins manually`,
bodyLines: [
'You may need to run commands to install the plugins:',
...Object.keys(failedPlugins).map(
(p) => `- ${bold(pmc.exec + ' nx add ' + p)}`
),
],
});
}
return { succeededPlugins, failedPlugins };
}
Loading

0 comments on commit 50d0dd7

Please sign in to comment.