nx 21.2.0 → 21.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/init-local.js +6 -0
- package/bin/nx.js +5 -9
- package/package.json +11 -11
- package/src/adapter/decorate-cli.js +2 -1
- package/src/adapter/ngcli-adapter.d.ts +11 -0
- package/src/adapter/ngcli-adapter.js +101 -7
- package/src/command-line/migrate/migrate-ui-api.d.ts +20 -3
- package/src/command-line/migrate/migrate-ui-api.js +115 -12
- package/src/command-line/migrate/run-migration-process.js +85 -0
- package/src/core/graph/main.js +1 -1
- package/src/core/graph/styles.css +1 -1
- package/src/daemon/server/server.js +7 -1
- package/src/native/nx.wasm32-wasi.wasm +0 -0
- package/src/tasks-runner/forked-process-task-runner.js +4 -0
- package/src/tasks-runner/life-cycles/nx-cloud-ci-message-life-cycle.d.ts +7 -0
- package/src/tasks-runner/life-cycles/nx-cloud-ci-message-life-cycle.js +49 -0
- package/src/tasks-runner/run-command.js +2 -0
- package/src/tasks-runner/running-tasks/node-child-process.d.ts +2 -0
- package/src/tasks-runner/running-tasks/node-child-process.js +16 -2
- package/src/tasks-runner/task-orchestrator.js +7 -1
- package/src/utils/package-json.js +1 -1
- package/src/utils/package-manager.d.ts +1 -0
- package/src/utils/package-manager.js +10 -1
package/bin/init-local.js
CHANGED
@@ -14,6 +14,12 @@ const nx_console_prompt_1 = require("../src/utils/nx-console-prompt");
|
|
14
14
|
async function initLocal(workspace) {
|
15
15
|
process.env.NX_CLI_SET = 'true';
|
16
16
|
try {
|
17
|
+
// In case Nx Cloud forcibly exits while the TUI is running, ensure the terminal is restored etc.
|
18
|
+
process.on('exit', (...args) => {
|
19
|
+
if (typeof globalThis.tuiOnProcessExit === 'function') {
|
20
|
+
globalThis.tuiOnProcessExit(...args);
|
21
|
+
}
|
22
|
+
});
|
17
23
|
perf_hooks_1.performance.mark('init-local');
|
18
24
|
if (workspace.type !== 'nx' && shouldDelegateToAngularCLI()) {
|
19
25
|
console.warn((0, strip_indents_1.stripIndents) `Using Nx to run Angular CLI commands is deprecated and will be removed in a future version.
|
package/bin/nx.js
CHANGED
@@ -17,12 +17,6 @@ const perf_hooks_1 = require("perf_hooks");
|
|
17
17
|
const workspace_context_1 = require("../src/utils/workspace-context");
|
18
18
|
const client_1 = require("../src/daemon/client/client");
|
19
19
|
const db_connection_1 = require("../src/utils/db-connection");
|
20
|
-
// In case Nx Cloud forcibly exits while the TUI is running, ensure the terminal is restored etc.
|
21
|
-
process.on('exit', (...args) => {
|
22
|
-
if (typeof globalThis.tuiOnProcessExit === 'function') {
|
23
|
-
globalThis.tuiOnProcessExit(...args);
|
24
|
-
}
|
25
|
-
});
|
26
20
|
async function main() {
|
27
21
|
if (process.argv[2] !== 'report' &&
|
28
22
|
process.argv[2] !== '--version' &&
|
@@ -71,7 +65,7 @@ async function main() {
|
|
71
65
|
handleNoWorkspace(GLOBAL_NX_VERSION);
|
72
66
|
}
|
73
67
|
if (!localNx) {
|
74
|
-
handleMissingLocalInstallation();
|
68
|
+
handleMissingLocalInstallation(workspace ? workspace.dir : null);
|
75
69
|
}
|
76
70
|
// this file is already in the local workspace
|
77
71
|
if (isLocalInstall) {
|
@@ -138,9 +132,11 @@ function resolveNx(workspace) {
|
|
138
132
|
paths: [workspace ? workspace.dir : globalsRoot],
|
139
133
|
});
|
140
134
|
}
|
141
|
-
function handleMissingLocalInstallation() {
|
135
|
+
function handleMissingLocalInstallation(detectedWorkspaceRoot) {
|
142
136
|
output_1.output.error({
|
143
|
-
title:
|
137
|
+
title: detectedWorkspaceRoot
|
138
|
+
? `Could not find Nx modules at "${detectedWorkspaceRoot}".`
|
139
|
+
: `Could not find Nx modules in this workspace.`,
|
144
140
|
bodyLines: [`Have you run ${chalk.bold.white(`npm/yarn install`)}?`],
|
145
141
|
});
|
146
142
|
process.exit(1);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "nx",
|
3
|
-
"version": "21.2.
|
3
|
+
"version": "21.2.2",
|
4
4
|
"private": false,
|
5
5
|
"description": "The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.",
|
6
6
|
"repository": {
|
@@ -83,16 +83,16 @@
|
|
83
83
|
}
|
84
84
|
},
|
85
85
|
"optionalDependencies": {
|
86
|
-
"@nx/nx-darwin-arm64": "21.2.
|
87
|
-
"@nx/nx-darwin-x64": "21.2.
|
88
|
-
"@nx/nx-freebsd-x64": "21.2.
|
89
|
-
"@nx/nx-linux-arm-gnueabihf": "21.2.
|
90
|
-
"@nx/nx-linux-arm64-gnu": "21.2.
|
91
|
-
"@nx/nx-linux-arm64-musl": "21.2.
|
92
|
-
"@nx/nx-linux-x64-gnu": "21.2.
|
93
|
-
"@nx/nx-linux-x64-musl": "21.2.
|
94
|
-
"@nx/nx-win32-arm64-msvc": "21.2.
|
95
|
-
"@nx/nx-win32-x64-msvc": "21.2.
|
86
|
+
"@nx/nx-darwin-arm64": "21.2.2",
|
87
|
+
"@nx/nx-darwin-x64": "21.2.2",
|
88
|
+
"@nx/nx-freebsd-x64": "21.2.2",
|
89
|
+
"@nx/nx-linux-arm-gnueabihf": "21.2.2",
|
90
|
+
"@nx/nx-linux-arm64-gnu": "21.2.2",
|
91
|
+
"@nx/nx-linux-arm64-musl": "21.2.2",
|
92
|
+
"@nx/nx-linux-x64-gnu": "21.2.2",
|
93
|
+
"@nx/nx-linux-x64-musl": "21.2.2",
|
94
|
+
"@nx/nx-win32-arm64-msvc": "21.2.2",
|
95
|
+
"@nx/nx-win32-x64-msvc": "21.2.2"
|
96
96
|
},
|
97
97
|
"nx-migrations": {
|
98
98
|
"migrations": "./migrations.json",
|
@@ -1,11 +1,12 @@
|
|
1
1
|
"use strict";
|
2
|
+
// TODO(v22): remove this file
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
4
|
exports.decorateCli = decorateCli;
|
4
5
|
const fs_1 = require("fs");
|
5
6
|
const output_1 = require("../utils/output");
|
6
7
|
function decorateCli() {
|
7
8
|
output_1.output.warn({
|
8
|
-
title: `Decoration of the Angular CLI is deprecated and will be removed in
|
9
|
+
title: `Decoration of the Angular CLI is deprecated and will be removed in Nx v22`,
|
9
10
|
bodyLines: [
|
10
11
|
`Please replace usage of "ng <command>" in any scripts, particularly for CI, with "nx <command>"`,
|
11
12
|
],
|
@@ -87,4 +87,15 @@ export declare function wrapAngularDevkitSchematic(collectionName: string, gener
|
|
87
87
|
[k: string]: any;
|
88
88
|
}) => Promise<GeneratorCallback>;
|
89
89
|
export declare const getLogger: (isVerbose?: boolean) => logging.Logger;
|
90
|
+
/**
|
91
|
+
* Restores Nx tokens in options when possible by comparing new and previous
|
92
|
+
* options.
|
93
|
+
* The function preserves tokens in the following cases:
|
94
|
+
* 1. When the resolved previous value matches the new value exactly
|
95
|
+
* 2. When the previous value used {workspaceRoot}
|
96
|
+
* 3. When the previous value used {projectRoot} and the new value starts with
|
97
|
+
* the project root path
|
98
|
+
* Those are the only safe cases, for all other cases, the new value is used as-is.
|
99
|
+
*/
|
100
|
+
export declare function restoreNxTokensInOptions<T extends Object | Array<unknown>>(newOptions: T, previousOptions: T, project: ProjectConfiguration): T;
|
90
101
|
export {};
|
@@ -8,6 +8,7 @@ exports.generate = generate;
|
|
8
8
|
exports.runMigration = runMigration;
|
9
9
|
exports.mockSchematicsForTesting = mockSchematicsForTesting;
|
10
10
|
exports.wrapAngularDevkitSchematic = wrapAngularDevkitSchematic;
|
11
|
+
exports.restoreNxTokensInOptions = restoreNxTokensInOptions;
|
11
12
|
const core_1 = require("@angular-devkit/core");
|
12
13
|
const node_1 = require("@angular-devkit/core/node");
|
13
14
|
const chalk = require("chalk");
|
@@ -28,6 +29,7 @@ const angular_json_1 = require("./angular-json");
|
|
28
29
|
const executor_utils_1 = require("../command-line/run/executor-utils");
|
29
30
|
const plugins_1 = require("../project-graph/plugins");
|
30
31
|
const schema_utils_1 = require("../config/schema-utils");
|
32
|
+
const project_configuration_utils_1 = require("../project-graph/utils/project-configuration-utils");
|
31
33
|
async function createBuilderContext(builderInfo, context) {
|
32
34
|
require('./compat');
|
33
35
|
const fsHost = new NxScopedHostForBuilders(context.root);
|
@@ -296,10 +298,32 @@ class NxScopedHost extends core_1.virtualFs.ScopedHost {
|
|
296
298
|
const projects = configV2.projects;
|
297
299
|
const allObservables = [];
|
298
300
|
Object.keys(projects).forEach((projectName) => {
|
299
|
-
if (projectsInAngularJson.includes(projectName)) {
|
300
|
-
//
|
301
|
-
|
302
|
-
|
301
|
+
if (!projectsInAngularJson.includes(projectName)) {
|
302
|
+
// Restore tokens in options if they were present before
|
303
|
+
const previousProject = existingConfig.projects[projectName];
|
304
|
+
const newProject = projects[projectName];
|
305
|
+
if (previousProject &&
|
306
|
+
newProject.targets &&
|
307
|
+
previousProject.targets) {
|
308
|
+
for (const [targetName, target] of Object.entries(newProject.targets)) {
|
309
|
+
const previousTarget = previousProject.targets[targetName];
|
310
|
+
if (target.options &&
|
311
|
+
previousTarget &&
|
312
|
+
previousTarget.options) {
|
313
|
+
target.options = restoreNxTokensInOptions(target.options, previousTarget.options, newProject);
|
314
|
+
}
|
315
|
+
if (target.configurations &&
|
316
|
+
previousTarget &&
|
317
|
+
previousTarget.configurations) {
|
318
|
+
for (const [configName, config] of Object.entries(target.configurations)) {
|
319
|
+
if (previousTarget.configurations[configName]) {
|
320
|
+
target.configurations[configName] =
|
321
|
+
restoreNxTokensInOptions(config, previousTarget.configurations[configName], newProject);
|
322
|
+
}
|
323
|
+
}
|
324
|
+
}
|
325
|
+
}
|
326
|
+
}
|
303
327
|
(0, project_configuration_1.updateProjectConfiguration)({
|
304
328
|
root,
|
305
329
|
exists: () => true,
|
@@ -430,9 +454,23 @@ class NxScopeHostUsedForWrappedSchematics extends NxScopedHost {
|
|
430
454
|
read(path) {
|
431
455
|
if ((path === 'angular.json' || path === '/angular.json') &&
|
432
456
|
(0, angular_json_1.isAngularPluginInstalled)()) {
|
433
|
-
|
434
|
-
|
435
|
-
|
457
|
+
// Replace the Nx-specific tokens in all target options
|
458
|
+
const projects = Object.fromEntries((0, project_configuration_1.getProjects)(this.host));
|
459
|
+
for (const [projectName, project] of Object.entries(projects)) {
|
460
|
+
if (project.targets) {
|
461
|
+
for (const [targetName, target] of Object.entries(project.targets)) {
|
462
|
+
if (target.options) {
|
463
|
+
target.options = (0, project_configuration_utils_1.resolveNxTokensInOptions)(target.options, { ...project, name: projectName }, `${projectName}:${targetName}`);
|
464
|
+
}
|
465
|
+
if (target.configurations) {
|
466
|
+
for (const [configName, config] of Object.entries(target.configurations)) {
|
467
|
+
target.configurations[configName] = (0, project_configuration_utils_1.resolveNxTokensInOptions)(config, { ...project, name: projectName }, `${projectName}:${targetName}:${configName}`);
|
468
|
+
}
|
469
|
+
}
|
470
|
+
}
|
471
|
+
}
|
472
|
+
}
|
473
|
+
const projectJsonConfig = (0, angular_json_1.toOldFormat)({ projects });
|
436
474
|
return super.readExistingAngularJson().pipe((0, operators_1.map)((angularJson) => {
|
437
475
|
if (angularJson) {
|
438
476
|
return Buffer.from(JSON.stringify({
|
@@ -852,3 +890,59 @@ async function getWrappedWorkspaceNodeModulesArchitectHost(workspace, root, proj
|
|
852
890
|
}
|
853
891
|
return new WrappedWorkspaceNodeModulesArchitectHost(workspace, root, projects);
|
854
892
|
}
|
893
|
+
/**
|
894
|
+
* Restores Nx tokens in options when possible by comparing new and previous
|
895
|
+
* options.
|
896
|
+
* The function preserves tokens in the following cases:
|
897
|
+
* 1. When the resolved previous value matches the new value exactly
|
898
|
+
* 2. When the previous value used {workspaceRoot}
|
899
|
+
* 3. When the previous value used {projectRoot} and the new value starts with
|
900
|
+
* the project root path
|
901
|
+
* Those are the only safe cases, for all other cases, the new value is used as-is.
|
902
|
+
*/
|
903
|
+
function restoreNxTokensInOptions(newOptions, previousOptions, project) {
|
904
|
+
if (!newOptions || !previousOptions) {
|
905
|
+
return newOptions;
|
906
|
+
}
|
907
|
+
const result = Array.isArray(newOptions)
|
908
|
+
? [...newOptions]
|
909
|
+
: { ...newOptions };
|
910
|
+
const resolvedPreviousOptions = (0, project_configuration_utils_1.resolveNxTokensInOptions)(previousOptions, project, '');
|
911
|
+
for (const key of Object.keys(newOptions)) {
|
912
|
+
const newValue = newOptions[key];
|
913
|
+
const previousValue = previousOptions[key];
|
914
|
+
if (typeof newValue === 'string' && typeof previousValue === 'string') {
|
915
|
+
if (resolvedPreviousOptions[key] === newValue) {
|
916
|
+
// If the resolved previous value matches the new value, use the previous
|
917
|
+
// value (potentially with tokens)
|
918
|
+
result[key] = previousValue;
|
919
|
+
}
|
920
|
+
else if (previousValue.startsWith('{workspaceRoot}/')) {
|
921
|
+
// If the previous value started with {workspaceRoot}, prefix the new
|
922
|
+
// value with {workspaceRoot}
|
923
|
+
result[key] = `{workspaceRoot}/${newValue.replace(/^\//, '')}`;
|
924
|
+
}
|
925
|
+
else if (previousValue.startsWith('{projectRoot}/') &&
|
926
|
+
newValue.startsWith(`${project.root}/`)) {
|
927
|
+
// If the previous value started with {projectRoot} and the new value
|
928
|
+
// starts with the project root, replace the project root with the
|
929
|
+
// {projectRoot} token
|
930
|
+
result[key] = newValue.replace(`${project.root}/`, '{projectRoot}/');
|
931
|
+
}
|
932
|
+
else {
|
933
|
+
// Otherwise, use the new value as-is
|
934
|
+
result[key] = newValue;
|
935
|
+
}
|
936
|
+
}
|
937
|
+
else if (typeof newValue === 'object' &&
|
938
|
+
typeof previousValue === 'object' &&
|
939
|
+
newValue &&
|
940
|
+
previousValue) {
|
941
|
+
result[key] = restoreNxTokensInOptions(newValue, previousValue, project);
|
942
|
+
}
|
943
|
+
else {
|
944
|
+
result[key] = newValue;
|
945
|
+
}
|
946
|
+
}
|
947
|
+
return result;
|
948
|
+
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import type { MigrationDetailsWithId } from '../../config/misc-interfaces';
|
2
2
|
import type { FileChange } from '../../generators/tree';
|
3
3
|
export type MigrationsJsonMetadata = {
|
4
|
-
completedMigrations?: Record<string, SuccessfulMigration | FailedMigration | SkippedMigration>;
|
4
|
+
completedMigrations?: Record<string, SuccessfulMigration | FailedMigration | SkippedMigration | StoppedMigration>;
|
5
5
|
runningMigrations?: string[];
|
6
6
|
initialGitRef?: {
|
7
7
|
ref: string;
|
@@ -25,6 +25,11 @@ export type FailedMigration = {
|
|
25
25
|
export type SkippedMigration = {
|
26
26
|
type: 'skipped';
|
27
27
|
};
|
28
|
+
export type StoppedMigration = {
|
29
|
+
type: 'stopped';
|
30
|
+
name: string;
|
31
|
+
error: string;
|
32
|
+
};
|
28
33
|
export declare function recordInitialMigrationMetadata(workspacePath: string, versionToMigrateTo: string): void;
|
29
34
|
export declare function finishMigrationProcess(workspacePath: string, squashCommits: boolean, commitMessage: string): void;
|
30
35
|
export declare function runSingleMigration(workspacePath: string, migration: MigrationDetailsWithId, configuration: {
|
@@ -36,7 +41,7 @@ export declare function modifyMigrationsJsonMetadata(workspacePath: string, modi
|
|
36
41
|
export declare function addSuccessfulMigration(id: string, fileChanges: Omit<FileChange, 'content'>[], ref: string, nextSteps: string[]): (migrationsJsonMetadata: MigrationsJsonMetadata) => MigrationsJsonMetadata;
|
37
42
|
export declare function updateRefForSuccessfulMigration(id: string, ref: string): (migrationsJsonMetadata: MigrationsJsonMetadata) => MigrationsJsonMetadata;
|
38
43
|
export declare function addFailedMigration(id: string, error: string): (migrationsJsonMetadata: MigrationsJsonMetadata) => {
|
39
|
-
completedMigrations?: Record<string, SuccessfulMigration | FailedMigration | SkippedMigration>;
|
44
|
+
completedMigrations?: Record<string, SuccessfulMigration | FailedMigration | SkippedMigration | StoppedMigration>;
|
40
45
|
runningMigrations?: string[];
|
41
46
|
initialGitRef?: {
|
42
47
|
ref: string;
|
@@ -46,7 +51,17 @@ export declare function addFailedMigration(id: string, error: string): (migratio
|
|
46
51
|
targetVersion?: string;
|
47
52
|
};
|
48
53
|
export declare function addSkippedMigration(id: string): (migrationsJsonMetadata: MigrationsJsonMetadata) => {
|
49
|
-
completedMigrations?: Record<string, SuccessfulMigration | FailedMigration | SkippedMigration>;
|
54
|
+
completedMigrations?: Record<string, SuccessfulMigration | FailedMigration | SkippedMigration | StoppedMigration>;
|
55
|
+
runningMigrations?: string[];
|
56
|
+
initialGitRef?: {
|
57
|
+
ref: string;
|
58
|
+
subject: string;
|
59
|
+
};
|
60
|
+
confirmedPackageUpdates?: boolean;
|
61
|
+
targetVersion?: string;
|
62
|
+
};
|
63
|
+
export declare function addStoppedMigration(id: string, error: string): (migrationsJsonMetadata: MigrationsJsonMetadata) => {
|
64
|
+
completedMigrations?: Record<string, SuccessfulMigration | FailedMigration | SkippedMigration | StoppedMigration>;
|
50
65
|
runningMigrations?: string[];
|
51
66
|
initialGitRef?: {
|
52
67
|
ref: string;
|
@@ -57,3 +72,5 @@ export declare function addSkippedMigration(id: string): (migrationsJsonMetadata
|
|
57
72
|
};
|
58
73
|
export declare function readMigrationsJsonMetadata(workspacePath: string): MigrationsJsonMetadata;
|
59
74
|
export declare function undoMigration(workspacePath: string, id: string): (migrationsJsonMetadata: MigrationsJsonMetadata) => MigrationsJsonMetadata;
|
75
|
+
export declare function killMigrationProcess(migrationId: string, workspacePath?: string): boolean;
|
76
|
+
export declare function stopMigration(migrationId: string): (migrationsJsonMetadata: MigrationsJsonMetadata) => MigrationsJsonMetadata;
|
@@ -9,12 +9,18 @@ exports.addSuccessfulMigration = addSuccessfulMigration;
|
|
9
9
|
exports.updateRefForSuccessfulMigration = updateRefForSuccessfulMigration;
|
10
10
|
exports.addFailedMigration = addFailedMigration;
|
11
11
|
exports.addSkippedMigration = addSkippedMigration;
|
12
|
+
exports.addStoppedMigration = addStoppedMigration;
|
12
13
|
exports.readMigrationsJsonMetadata = readMigrationsJsonMetadata;
|
13
14
|
exports.undoMigration = undoMigration;
|
15
|
+
exports.killMigrationProcess = killMigrationProcess;
|
16
|
+
exports.stopMigration = stopMigration;
|
14
17
|
const child_process_1 = require("child_process");
|
15
18
|
const fs_1 = require("fs");
|
16
19
|
const path_1 = require("path");
|
17
20
|
const migrate_1 = require("./migrate");
|
21
|
+
let currentMigrationProcess = null;
|
22
|
+
let currentMigrationId = null;
|
23
|
+
let migrationCancelled = false;
|
18
24
|
function recordInitialMigrationMetadata(workspacePath, versionToMigrateTo) {
|
19
25
|
const migrationsJsonPath = (0, path_1.join)(workspacePath, 'migrations.json');
|
20
26
|
const parsedMigrationsJson = JSON.parse((0, fs_1.readFileSync)(migrationsJsonPath, 'utf-8'));
|
@@ -63,20 +69,62 @@ function finishMigrationProcess(workspacePath, squashCommits, commitMessage) {
|
|
63
69
|
}
|
64
70
|
async function runSingleMigration(workspacePath, migration, configuration) {
|
65
71
|
try {
|
72
|
+
// Set current migration tracking
|
73
|
+
currentMigrationId = migration.id;
|
74
|
+
migrationCancelled = false;
|
66
75
|
modifyMigrationsJsonMetadata(workspacePath, addRunningMigration(migration.id));
|
67
76
|
const gitRefBefore = (0, child_process_1.execSync)('git rev-parse HEAD', {
|
68
77
|
cwd: workspacePath,
|
69
78
|
encoding: 'utf-8',
|
70
79
|
}).trim();
|
71
|
-
//
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
80
|
+
// Run migration in a separate process so it can be cancelled
|
81
|
+
const runMigrationProcessPath = require.resolve('./run-migration-process.js');
|
82
|
+
const migrationProcess = (0, child_process_1.spawn)('node', [
|
83
|
+
runMigrationProcessPath,
|
84
|
+
workspacePath,
|
85
|
+
migration.id,
|
86
|
+
migration.package,
|
87
|
+
migration.name,
|
88
|
+
migration.version,
|
89
|
+
configuration.createCommits.toString(),
|
90
|
+
configuration.commitPrefix || 'chore: [nx migration] ',
|
91
|
+
], {
|
77
92
|
cwd: workspacePath,
|
78
|
-
|
79
|
-
})
|
93
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
94
|
+
});
|
95
|
+
// Track the process for cancellation
|
96
|
+
currentMigrationProcess = migrationProcess;
|
97
|
+
// Handle process output
|
98
|
+
let output = '';
|
99
|
+
migrationProcess.stdout.on('data', (data) => {
|
100
|
+
output += data.toString();
|
101
|
+
});
|
102
|
+
migrationProcess.stderr.on('data', (data) => {
|
103
|
+
console.error('Migration stderr:', data.toString());
|
104
|
+
});
|
105
|
+
// Wait for the process to complete
|
106
|
+
const exitCode = await new Promise((resolve, reject) => {
|
107
|
+
migrationProcess.on('close', (code) => {
|
108
|
+
resolve(code);
|
109
|
+
});
|
110
|
+
migrationProcess.on('error', (error) => {
|
111
|
+
reject(error);
|
112
|
+
});
|
113
|
+
});
|
114
|
+
currentMigrationProcess = null;
|
115
|
+
if (exitCode !== 0) {
|
116
|
+
throw new Error(`Migration process exited with code ${exitCode}`);
|
117
|
+
}
|
118
|
+
// Parse the result from the migration process (extract the JSON output)
|
119
|
+
const jsonStr = output
|
120
|
+
.trim()
|
121
|
+
.split('\n')
|
122
|
+
.find((line) => line.startsWith('{'));
|
123
|
+
const result = JSON.parse(jsonStr);
|
124
|
+
if (result.type === 'error') {
|
125
|
+
throw new Error(result.message);
|
126
|
+
}
|
127
|
+
const { fileChanges, gitRefAfter, nextSteps } = result;
|
80
128
|
modifyMigrationsJsonMetadata(workspacePath, addSuccessfulMigration(migration.id, fileChanges.map((change) => ({
|
81
129
|
path: change.path,
|
82
130
|
type: change.type,
|
@@ -99,9 +147,21 @@ async function runSingleMigration(workspacePath, migration, configuration) {
|
|
99
147
|
}
|
100
148
|
}
|
101
149
|
catch (e) {
|
102
|
-
|
150
|
+
// Check if migration was cancelled/stopped
|
151
|
+
if (migrationCancelled && currentMigrationId === migration.id) {
|
152
|
+
// Migration was stopped by user, don't add as failed since it's already marked as stopped
|
153
|
+
console.log(`Migration ${migration.id} was stopped by user`);
|
154
|
+
}
|
155
|
+
else {
|
156
|
+
// Migration failed normally
|
157
|
+
modifyMigrationsJsonMetadata(workspacePath, addFailedMigration(migration.id, e.message));
|
158
|
+
}
|
103
159
|
}
|
104
160
|
finally {
|
161
|
+
// Clear the tracking variables
|
162
|
+
currentMigrationProcess = null;
|
163
|
+
currentMigrationId = null;
|
164
|
+
migrationCancelled = false;
|
105
165
|
modifyMigrationsJsonMetadata(workspacePath, removeRunningMigration(migration.id));
|
106
166
|
(0, child_process_1.execSync)('git add migrations.json', {
|
107
167
|
cwd: workspacePath,
|
@@ -187,6 +247,23 @@ function addSkippedMigration(id) {
|
|
187
247
|
return copied;
|
188
248
|
};
|
189
249
|
}
|
250
|
+
function addStoppedMigration(id, error) {
|
251
|
+
return (migrationsJsonMetadata) => {
|
252
|
+
const copied = { ...migrationsJsonMetadata };
|
253
|
+
if (!copied.completedMigrations) {
|
254
|
+
copied.completedMigrations = {};
|
255
|
+
}
|
256
|
+
copied.completedMigrations = {
|
257
|
+
...copied.completedMigrations,
|
258
|
+
[id]: {
|
259
|
+
type: 'stopped',
|
260
|
+
name: id,
|
261
|
+
error,
|
262
|
+
},
|
263
|
+
};
|
264
|
+
return copied;
|
265
|
+
};
|
266
|
+
}
|
190
267
|
function addRunningMigration(id) {
|
191
268
|
return (migrationsJsonMetadata) => {
|
192
269
|
migrationsJsonMetadata.runningMigrations = [
|
@@ -200,9 +277,6 @@ function removeRunningMigration(id) {
|
|
200
277
|
return (migrationsJsonMetadata) => {
|
201
278
|
migrationsJsonMetadata.runningMigrations =
|
202
279
|
migrationsJsonMetadata.runningMigrations?.filter((n) => n !== id);
|
203
|
-
if (migrationsJsonMetadata.runningMigrations?.length === 0) {
|
204
|
-
delete migrationsJsonMetadata.runningMigrations;
|
205
|
-
}
|
206
280
|
return migrationsJsonMetadata;
|
207
281
|
};
|
208
282
|
}
|
@@ -226,3 +300,32 @@ function undoMigration(workspacePath, id) {
|
|
226
300
|
return migrationsJsonMetadata;
|
227
301
|
};
|
228
302
|
}
|
303
|
+
function killMigrationProcess(migrationId, workspacePath) {
|
304
|
+
try {
|
305
|
+
if (workspacePath) {
|
306
|
+
modifyMigrationsJsonMetadata(workspacePath, stopMigration(migrationId));
|
307
|
+
}
|
308
|
+
// Check if this is the currently running migration and kill the process
|
309
|
+
if (currentMigrationId === migrationId && currentMigrationProcess) {
|
310
|
+
currentMigrationProcess.kill('SIGTERM');
|
311
|
+
// Some processes may not respond to SIGTERM immediately,
|
312
|
+
// so we give it a short timeout before forcefully killing it
|
313
|
+
setTimeout(() => {
|
314
|
+
if (currentMigrationProcess && !currentMigrationProcess.killed) {
|
315
|
+
currentMigrationProcess.kill('SIGKILL');
|
316
|
+
}
|
317
|
+
}, 2000);
|
318
|
+
}
|
319
|
+
return true;
|
320
|
+
}
|
321
|
+
catch (error) {
|
322
|
+
console.error(`Failed to stop migration ${migrationId}:`, error);
|
323
|
+
return false;
|
324
|
+
}
|
325
|
+
}
|
326
|
+
function stopMigration(migrationId) {
|
327
|
+
return (migrationsJsonMetadata) => {
|
328
|
+
const updated = addStoppedMigration(migrationId, 'Migration was stopped by user')(migrationsJsonMetadata);
|
329
|
+
return removeRunningMigration(migrationId)(updated);
|
330
|
+
};
|
331
|
+
}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
const { runNxOrAngularMigration } = require('./migrate');
|
2
|
+
const { execSync } = require('child_process');
|
3
|
+
|
4
|
+
async function runMigrationProcess() {
|
5
|
+
const [
|
6
|
+
,
|
7
|
+
,
|
8
|
+
workspacePath,
|
9
|
+
migrationId,
|
10
|
+
migrationPackage,
|
11
|
+
migrationName,
|
12
|
+
migrationVersion,
|
13
|
+
createCommits,
|
14
|
+
commitPrefix,
|
15
|
+
] = process.argv;
|
16
|
+
|
17
|
+
const migration = {
|
18
|
+
id: migrationId,
|
19
|
+
package: migrationPackage,
|
20
|
+
name: migrationName,
|
21
|
+
version: migrationVersion,
|
22
|
+
};
|
23
|
+
|
24
|
+
const configuration = {
|
25
|
+
createCommits: createCommits === 'true',
|
26
|
+
commitPrefix: commitPrefix || 'chore: [nx migration] ',
|
27
|
+
};
|
28
|
+
|
29
|
+
try {
|
30
|
+
const gitRefBefore = execSync('git rev-parse HEAD', {
|
31
|
+
cwd: workspacePath,
|
32
|
+
encoding: 'utf-8',
|
33
|
+
}).trim();
|
34
|
+
|
35
|
+
const { changes: fileChanges, nextSteps } = await runNxOrAngularMigration(
|
36
|
+
workspacePath,
|
37
|
+
migration,
|
38
|
+
false,
|
39
|
+
configuration.createCommits,
|
40
|
+
configuration.commitPrefix,
|
41
|
+
undefined,
|
42
|
+
true
|
43
|
+
);
|
44
|
+
|
45
|
+
const gitRefAfter = execSync('git rev-parse HEAD', {
|
46
|
+
cwd: workspacePath,
|
47
|
+
encoding: 'utf-8',
|
48
|
+
}).trim();
|
49
|
+
|
50
|
+
// Report success
|
51
|
+
process.stdout.write(
|
52
|
+
JSON.stringify({
|
53
|
+
type: 'success',
|
54
|
+
fileChanges: fileChanges.map((change) => ({
|
55
|
+
path: change.path,
|
56
|
+
type: change.type,
|
57
|
+
})),
|
58
|
+
gitRefAfter,
|
59
|
+
nextSteps,
|
60
|
+
})
|
61
|
+
);
|
62
|
+
|
63
|
+
process.exit(0);
|
64
|
+
} catch (error) {
|
65
|
+
// Report failure
|
66
|
+
process.stdout.write(
|
67
|
+
JSON.stringify({
|
68
|
+
type: 'error',
|
69
|
+
message: error.message,
|
70
|
+
})
|
71
|
+
);
|
72
|
+
|
73
|
+
process.exit(1);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
runMigrationProcess().catch((error) => {
|
78
|
+
process.stdout.write(
|
79
|
+
JSON.stringify({
|
80
|
+
type: 'error',
|
81
|
+
message: error.message,
|
82
|
+
})
|
83
|
+
);
|
84
|
+
process.exit(1);
|
85
|
+
});
|