feat(core): running one task uses the same tasks runner as run-many
This commit is contained in:
parent
66634804ad
commit
c3fdf2e702
@ -268,11 +268,11 @@ forEachCli(() => {
|
|||||||
const build = runCommand(
|
const build = runCommand(
|
||||||
`npm run affected:build -- --files="apps/${myapp}/src/main.ts,libs/${mypublishablelib}/src/index.ts" --parallel`
|
`npm run affected:build -- --files="apps/${myapp}/src/main.ts,libs/${mypublishablelib}/src/index.ts" --parallel`
|
||||||
);
|
);
|
||||||
console.log(build);
|
|
||||||
// make sure that the package is done building before we start building the app
|
// make sure that the package is done building before we start building the app
|
||||||
expect(
|
expect(
|
||||||
build.indexOf('Built Angular Package!') <
|
build.indexOf('Built Angular Package!') <
|
||||||
build.indexOf(`"build" "${myapp}"`)
|
build.indexOf(`Generating ES5 bundles for differential loading.`)
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ forEachCli(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Cache', () => {
|
describe('Cache', () => {
|
||||||
it('should not use cache when it is not enabled', async () => {
|
it('should cache command execution', async () => {
|
||||||
ensureProject();
|
ensureProject();
|
||||||
|
|
||||||
const myapp1 = uniq('myapp1');
|
const myapp1 = uniq('myapp1');
|
||||||
@ -128,6 +128,12 @@ forEachCli(() => {
|
|||||||
'read the output from cache'
|
'read the output from cache'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// build individual project with caching
|
||||||
|
const individualBuildWithCache = runCommand(
|
||||||
|
`npm run nx -- build ${myapp1}`
|
||||||
|
);
|
||||||
|
expect(individualBuildWithCache).toContain('Cached Output');
|
||||||
|
|
||||||
// run lint with caching
|
// run lint with caching
|
||||||
// --------------------------------------------
|
// --------------------------------------------
|
||||||
const outputWithNoLintCached = runCommand(
|
const outputWithNoLintCached = runCommand(
|
||||||
@ -1,5 +1,7 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
import { Workspace } from './workspace';
|
import { Workspace } from './workspace';
|
||||||
|
import { parseRunOneOptions } from './parse-run-one-options';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nx is being run inside a workspace.
|
* Nx is being run inside a workspace.
|
||||||
@ -7,25 +9,35 @@ import { Workspace } from './workspace';
|
|||||||
* @param workspace Relevant local workspace properties
|
* @param workspace Relevant local workspace properties
|
||||||
*/
|
*/
|
||||||
export function initLocal(workspace: Workspace) {
|
export function initLocal(workspace: Workspace) {
|
||||||
// required to make sure nrwl/workspace import works
|
const supportedNxCommands = require('@nrwl/workspace/src/command-line/supported-nx-commands')
|
||||||
if (workspace.type === 'nx') {
|
.supportedNxCommands;
|
||||||
require(path.join(
|
const runOpts = runOneOptions(workspace);
|
||||||
workspace.dir,
|
|
||||||
'node_modules',
|
|
||||||
'@nrwl',
|
|
||||||
'tao',
|
|
||||||
'src',
|
|
||||||
'compat',
|
|
||||||
'compat.js'
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The commandsObject is a Yargs object declared in `nx-commands.ts`,
|
if (supportedNxCommands.includes(process.argv[2])) {
|
||||||
// It is exposed and bootstrapped here to provide CLI features.
|
// required to make sure nrwl/workspace import works
|
||||||
const w = require('@nrwl/workspace');
|
if (workspace.type === 'nx') {
|
||||||
if (w.supportedNxCommands.includes(process.argv[2])) {
|
require(path.join(
|
||||||
w.commandsObject.argv;
|
workspace.dir,
|
||||||
} else if (workspace.type === 'nx') {
|
'node_modules',
|
||||||
|
'@nrwl',
|
||||||
|
'tao',
|
||||||
|
'src',
|
||||||
|
'compat',
|
||||||
|
'compat.js'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
require('@nrwl/workspace/src/command-line/nx-commands').commandsObject.argv;
|
||||||
|
} else {
|
||||||
|
if (runOpts === false || process.env.NX_SKIP_TASKS_RUNNER) {
|
||||||
|
loadCli(workspace);
|
||||||
|
} else {
|
||||||
|
require('@nrwl/workspace/src/command-line/run-one').runOne(runOpts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCli(workspace: Workspace) {
|
||||||
|
if (workspace.type === 'nx') {
|
||||||
require(path.join(
|
require(path.join(
|
||||||
workspace.dir,
|
workspace.dir,
|
||||||
'node_modules',
|
'node_modules',
|
||||||
@ -34,9 +46,6 @@ export function initLocal(workspace: Workspace) {
|
|||||||
'index.js'
|
'index.js'
|
||||||
));
|
));
|
||||||
} else if (workspace.type === 'angular') {
|
} else if (workspace.type === 'angular') {
|
||||||
w.output.note({
|
|
||||||
title: `Nx didn't recognize the command, forwarding on to the Angular CLI.`
|
|
||||||
});
|
|
||||||
require(path.join(
|
require(path.join(
|
||||||
workspace.dir,
|
workspace.dir,
|
||||||
'node_modules',
|
'node_modules',
|
||||||
@ -45,5 +54,37 @@ export function initLocal(workspace: Workspace) {
|
|||||||
'lib',
|
'lib',
|
||||||
'init.js'
|
'init.js'
|
||||||
));
|
));
|
||||||
|
} else {
|
||||||
|
console.error(`Cannot recognize the workspace type.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function runOneOptions(
|
||||||
|
workspace: Workspace
|
||||||
|
): false | { project; target; configuration; overrides } {
|
||||||
|
try {
|
||||||
|
const nxJson = JSON.parse(
|
||||||
|
fs.readFileSync(path.join(workspace.dir, 'nx.json')).toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
const workspaceConfigJson = JSON.parse(
|
||||||
|
fs
|
||||||
|
.readFileSync(
|
||||||
|
path.join(
|
||||||
|
workspace.dir,
|
||||||
|
workspace.type === 'nx' ? 'workspace.json' : 'angular.json'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
return parseRunOneOptions(
|
||||||
|
nxJson,
|
||||||
|
workspaceConfigJson,
|
||||||
|
process.argv.slice(2)
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
77
packages/cli/lib/parse-run-one-options.spec.ts
Normal file
77
packages/cli/lib/parse-run-one-options.spec.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { parseRunOneOptions } from './parse-run-one-options';
|
||||||
|
|
||||||
|
describe('parseRunOneOptions', () => {
|
||||||
|
const nxJson = { tasksRunnerOptions: { default: { runner: 'somerunner' } } };
|
||||||
|
const workspaceJson = { projects: { myproj: { architect: { build: {} } } } };
|
||||||
|
const args = ['build', 'myproj', '--configuration=production', '--flag=true'];
|
||||||
|
|
||||||
|
it('should work', () => {
|
||||||
|
expect(parseRunOneOptions(nxJson, workspaceJson, args)).toEqual({
|
||||||
|
project: 'myproj',
|
||||||
|
target: 'build',
|
||||||
|
configuration: 'production',
|
||||||
|
overrides: { flag: 'true' }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with run syntax', () => {
|
||||||
|
expect(
|
||||||
|
parseRunOneOptions(nxJson, workspaceJson, [
|
||||||
|
'run',
|
||||||
|
'myproj:build:production',
|
||||||
|
'--flag=true'
|
||||||
|
])
|
||||||
|
).toEqual({
|
||||||
|
project: 'myproj',
|
||||||
|
target: 'build',
|
||||||
|
configuration: 'production',
|
||||||
|
overrides: { flag: 'true' }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use defaultProjectName when no provided', () => {
|
||||||
|
expect(
|
||||||
|
parseRunOneOptions(
|
||||||
|
nxJson,
|
||||||
|
{ ...workspaceJson, cli: { defaultProjectName: 'myproj' } },
|
||||||
|
['build', '--flag=true']
|
||||||
|
)
|
||||||
|
).toEqual({
|
||||||
|
project: 'myproj',
|
||||||
|
target: 'build',
|
||||||
|
overrides: { flag: 'true' }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when no runner is set', () => {
|
||||||
|
expect(parseRunOneOptions({}, workspaceJson, args)).toBe(false);
|
||||||
|
expect(
|
||||||
|
parseRunOneOptions({ tasksRunnerOptions: {} }, workspaceJson, args)
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
parseRunOneOptions(
|
||||||
|
{ tasksRunnerOptions: { default: {} } },
|
||||||
|
workspaceJson,
|
||||||
|
args
|
||||||
|
)
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when the task is not recognized', () => {
|
||||||
|
expect(parseRunOneOptions(nxJson, {}, args)).toBe(false);
|
||||||
|
expect(parseRunOneOptions(nxJson, { projects: {} }, args)).toBe(false);
|
||||||
|
expect(
|
||||||
|
parseRunOneOptions(nxJson, { projects: { architect: {} } }, args)
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when cannot find the right project', () => {
|
||||||
|
expect(
|
||||||
|
parseRunOneOptions(nxJson, workspaceJson, ['build', 'wrongproj'])
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when no project specified', () => {
|
||||||
|
expect(parseRunOneOptions(nxJson, workspaceJson, ['build'])).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
76
packages/cli/lib/parse-run-one-options.ts
Normal file
76
packages/cli/lib/parse-run-one-options.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import yargsParser = require('yargs-parser');
|
||||||
|
|
||||||
|
export function parseRunOneOptions(
|
||||||
|
nxJson: any,
|
||||||
|
workspaceConfigJson: any,
|
||||||
|
args: string[]
|
||||||
|
): false | { project; target; configuration; overrides } {
|
||||||
|
// custom runner is not set, no tasks runner
|
||||||
|
if (
|
||||||
|
!nxJson.tasksRunnerOptions ||
|
||||||
|
!nxJson.tasksRunnerOptions.default ||
|
||||||
|
!nxJson.tasksRunnerOptions.default.runner
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the list of all possible tasks doesn't include the given name, no tasks runner
|
||||||
|
let allPossibleTasks = ['run'];
|
||||||
|
Object.values(workspaceConfigJson.projects || {}).forEach((p: any) => {
|
||||||
|
allPossibleTasks.push(...Object.keys(p.architect || {}));
|
||||||
|
});
|
||||||
|
if (allPossibleTasks.indexOf(args[0]) === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let defaultProjectName = null;
|
||||||
|
try {
|
||||||
|
defaultProjectName = workspaceConfigJson.cli.defaultProjectName;
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const overrides = yargsParser(args, {
|
||||||
|
boolean: ['prod'],
|
||||||
|
string: ['configuration', 'project']
|
||||||
|
});
|
||||||
|
|
||||||
|
let project;
|
||||||
|
let target;
|
||||||
|
let configuration;
|
||||||
|
|
||||||
|
if (overrides._[0] === 'run') {
|
||||||
|
[project, target, configuration] = overrides._[1].split(':');
|
||||||
|
} else {
|
||||||
|
target = overrides._[0];
|
||||||
|
project = overrides._[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!project && defaultProjectName) {
|
||||||
|
project = defaultProjectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overrides.configuration) {
|
||||||
|
configuration = overrides.configuration;
|
||||||
|
}
|
||||||
|
if (overrides.prod) {
|
||||||
|
configuration = 'production';
|
||||||
|
}
|
||||||
|
if (overrides.project) {
|
||||||
|
project = overrides.project;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need both to be able to run a target, no tasks runner
|
||||||
|
if (!project || !target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need both to be able to run a target, no tasks runner
|
||||||
|
if (!workspaceConfigJson.projects[project]) return false;
|
||||||
|
|
||||||
|
const res = { project, target, configuration, overrides };
|
||||||
|
delete overrides['_'];
|
||||||
|
delete overrides['configuration'];
|
||||||
|
delete overrides['prod'];
|
||||||
|
delete overrides['project'];
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
70
packages/cli/lib/run-cli.ts
Normal file
70
packages/cli/lib/run-cli.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { findWorkspaceRoot } from './find-workspace-root';
|
||||||
|
|
||||||
|
const workspace = findWorkspaceRoot(process.cwd());
|
||||||
|
|
||||||
|
setUpOutputWatching();
|
||||||
|
requireCli();
|
||||||
|
|
||||||
|
function requireCli() {
|
||||||
|
if (workspace.type === 'nx') {
|
||||||
|
require(path.join(
|
||||||
|
workspace.dir,
|
||||||
|
'node_modules',
|
||||||
|
'@nrwl',
|
||||||
|
'tao',
|
||||||
|
'index.js'
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
require(path.join(
|
||||||
|
workspace.dir,
|
||||||
|
'node_modules',
|
||||||
|
'@angular',
|
||||||
|
'cli',
|
||||||
|
'lib',
|
||||||
|
'init.js'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need to collect all stdout and stderr and store it, so the caching mechanism
|
||||||
|
* could store it.
|
||||||
|
*
|
||||||
|
* Writing stdout and stderr into different stream is too risky when using TTY.
|
||||||
|
*
|
||||||
|
* So we are simply monkey-patching the Javascript object. In this case the actual output will always be correct.
|
||||||
|
* And the cached output should be correct unless the CLI bypasses process.stdout or console.log and uses some
|
||||||
|
* C-binary to write to stdout.
|
||||||
|
*/
|
||||||
|
function setUpOutputWatching() {
|
||||||
|
const stdoutWrite = process.stdout._write;
|
||||||
|
const stderrWrite = process.stderr._write;
|
||||||
|
|
||||||
|
let out = [];
|
||||||
|
|
||||||
|
process.stdout._write = (
|
||||||
|
chunk: any,
|
||||||
|
encoding: string,
|
||||||
|
callback: Function
|
||||||
|
) => {
|
||||||
|
out.push(chunk.toString());
|
||||||
|
stdoutWrite.apply(process.stdout, [chunk, encoding, callback]);
|
||||||
|
};
|
||||||
|
|
||||||
|
process.stderr._write = (
|
||||||
|
chunk: any,
|
||||||
|
encoding: string,
|
||||||
|
callback: Function
|
||||||
|
) => {
|
||||||
|
out.push(chunk.toString());
|
||||||
|
stderrWrite.apply(process.stderr, [chunk, encoding, callback]);
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on('exit', code => {
|
||||||
|
if (code === 0) {
|
||||||
|
fs.writeFileSync(process.env.NX_TERMINAL_OUTPUT_PATH, out.join(''));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -51,7 +51,7 @@ class InsightsRemoteCache implements RemoteCache {
|
|||||||
if (e.response && e.response.status === 404) {
|
if (e.response && e.response.status === 404) {
|
||||||
// cache miss. print nothing
|
// cache miss. print nothing
|
||||||
} else if (e.code === 'ECONNREFUSED') {
|
} else if (e.code === 'ECONNREFUSED') {
|
||||||
console.error(`Error: Cannot cannot to remote cache.`);
|
console.error(`Error: Cannot connect to remote cache.`);
|
||||||
} else {
|
} else {
|
||||||
console.error(e.message);
|
console.error(e.message);
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ class InsightsRemoteCache implements RemoteCache {
|
|||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code === 'ECONNREFUSED') {
|
if (e.code === 'ECONNREFUSED') {
|
||||||
console.error(`Error: Cannot cannot to remote cache.`);
|
console.error(`Error: Cannot connect to remote cache.`);
|
||||||
} else {
|
} else {
|
||||||
console.error(e.message);
|
console.error(e.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,9 +57,6 @@ function parseRunOpts(
|
|||||||
);
|
);
|
||||||
project = defaultProjectName;
|
project = defaultProjectName;
|
||||||
}
|
}
|
||||||
if (!project || !target) {
|
|
||||||
throwInvalidInvocation();
|
|
||||||
}
|
|
||||||
if (runOptions.configuration) {
|
if (runOptions.configuration) {
|
||||||
configuration = runOptions.configuration;
|
configuration = runOptions.configuration;
|
||||||
}
|
}
|
||||||
@ -69,6 +66,9 @@ function parseRunOpts(
|
|||||||
if (runOptions.project) {
|
if (runOptions.project) {
|
||||||
project = runOptions.project;
|
project = runOptions.project;
|
||||||
}
|
}
|
||||||
|
if (!project || !target) {
|
||||||
|
throwInvalidInvocation();
|
||||||
|
}
|
||||||
const res = { project, target, configuration, help, runOptions };
|
const res = { project, target, configuration, help, runOptions };
|
||||||
delete runOptions['help'];
|
delete runOptions['help'];
|
||||||
delete runOptions['_'];
|
delete runOptions['_'];
|
||||||
|
|||||||
@ -20,10 +20,8 @@ export {
|
|||||||
resolveUserExistingPrettierConfig
|
resolveUserExistingPrettierConfig
|
||||||
} from './src/utils/common';
|
} from './src/utils/common';
|
||||||
export { output } from './src/utils/output';
|
export { output } from './src/utils/output';
|
||||||
export {
|
export { commandsObject } from './src/command-line/nx-commands';
|
||||||
commandsObject,
|
export { supportedNxCommands } from './src/command-line/supported-nx-commands';
|
||||||
supportedNxCommands
|
|
||||||
} from './src/command-line/nx-commands';
|
|
||||||
export { readWorkspaceJson, readNxJson } from './src/core/file-utils';
|
export { readWorkspaceJson, readNxJson } from './src/core/file-utils';
|
||||||
export { NxJson } from './src/core/shared-interfaces';
|
export { NxJson } from './src/core/shared-interfaces';
|
||||||
export {
|
export {
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
import { calculateFileChanges, readEnvironment } from '../core/file-utils';
|
import { calculateFileChanges, readEnvironment } from '../core/file-utils';
|
||||||
import { printAffected } from './print-affected';
|
import { printAffected } from './print-affected';
|
||||||
import { projectHasTargetAndConfiguration } from '../utils/project-has-target-and-configuration';
|
import { projectHasTargetAndConfiguration } from '../utils/project-has-target-and-configuration';
|
||||||
|
import { DefaultReporter } from '../tasks-runner/default-reporter';
|
||||||
|
|
||||||
export function affected(command: string, parsedArgs: yargs.Arguments): void {
|
export function affected(command: string, parsedArgs: yargs.Arguments): void {
|
||||||
const { nxArgs, overrides } = splitArgsIntoNxArgsAndOverrides(parsedArgs);
|
const { nxArgs, overrides } = splitArgsIntoNxArgsAndOverrides(parsedArgs);
|
||||||
@ -109,7 +110,8 @@ export function affected(command: string, parsedArgs: yargs.Arguments): void {
|
|||||||
projectGraph,
|
projectGraph,
|
||||||
env,
|
env,
|
||||||
nxArgs,
|
nxArgs,
|
||||||
overrides
|
overrides,
|
||||||
|
new DefaultReporter()
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,31 +14,6 @@ import { runMany } from './run-many';
|
|||||||
|
|
||||||
const noop = (yargs: yargs.Argv): yargs.Argv => yargs;
|
const noop = (yargs: yargs.Argv): yargs.Argv => yargs;
|
||||||
|
|
||||||
export const supportedNxCommands = [
|
|
||||||
'affected',
|
|
||||||
'affected:apps',
|
|
||||||
'affected:libs',
|
|
||||||
'affected:build',
|
|
||||||
'affected:test',
|
|
||||||
'affected:e2e',
|
|
||||||
'affected:dep-graph',
|
|
||||||
'affected:lint',
|
|
||||||
'print-affected',
|
|
||||||
'dep-graph',
|
|
||||||
'format',
|
|
||||||
'format:check',
|
|
||||||
'format:write',
|
|
||||||
'workspace-schematic',
|
|
||||||
'workspace-lint',
|
|
||||||
'migrate',
|
|
||||||
'report',
|
|
||||||
'run-many',
|
|
||||||
'list',
|
|
||||||
'help',
|
|
||||||
'--help',
|
|
||||||
'--version'
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exposing the Yargs commands object so the documentation generator can
|
* Exposing the Yargs commands object so the documentation generator can
|
||||||
* parse it. The CLI will consume it and call the `.argv` to bootstrapped
|
* parse it. The CLI will consume it and call the `.argv` to bootstrapped
|
||||||
|
|||||||
@ -10,13 +10,21 @@ import {
|
|||||||
} from '../core/project-graph';
|
} from '../core/project-graph';
|
||||||
import { readEnvironment } from '../core/file-utils';
|
import { readEnvironment } from '../core/file-utils';
|
||||||
import { projectHasTargetAndConfiguration } from '../utils/project-has-target-and-configuration';
|
import { projectHasTargetAndConfiguration } from '../utils/project-has-target-and-configuration';
|
||||||
|
import { DefaultReporter } from '../tasks-runner/default-reporter';
|
||||||
|
|
||||||
export function runMany(parsedArgs: yargs.Arguments): void {
|
export function runMany(parsedArgs: yargs.Arguments): void {
|
||||||
const { nxArgs, overrides } = splitArgsIntoNxArgsAndOverrides(parsedArgs);
|
const { nxArgs, overrides } = splitArgsIntoNxArgsAndOverrides(parsedArgs);
|
||||||
const env = readEnvironment(nxArgs.target);
|
const env = readEnvironment(nxArgs.target);
|
||||||
const projectGraph = createProjectGraph();
|
const projectGraph = createProjectGraph();
|
||||||
const projects = projectsToRun(nxArgs, projectGraph);
|
const projects = projectsToRun(nxArgs, projectGraph);
|
||||||
runCommand(projects, projectGraph, env, nxArgs, overrides);
|
runCommand(
|
||||||
|
projects,
|
||||||
|
projectGraph,
|
||||||
|
env,
|
||||||
|
nxArgs,
|
||||||
|
overrides,
|
||||||
|
new DefaultReporter()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function projectsToRun(nxArgs: NxArgs, projectGraph: ProjectGraph) {
|
function projectsToRun(nxArgs: NxArgs, projectGraph: ProjectGraph) {
|
||||||
|
|||||||
23
packages/workspace/src/command-line/run-one.ts
Normal file
23
packages/workspace/src/command-line/run-one.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { runCommand } from '../tasks-runner/run-command';
|
||||||
|
import { createProjectGraph } from '../core/project-graph';
|
||||||
|
import { readEnvironment } from '../core/file-utils';
|
||||||
|
import { EmptyReporter } from '../tasks-runner/empty-reporter';
|
||||||
|
|
||||||
|
export function runOne(opts: {
|
||||||
|
project: string;
|
||||||
|
target: string;
|
||||||
|
configuration: string;
|
||||||
|
overrides: any;
|
||||||
|
}): void {
|
||||||
|
const env = readEnvironment(opts.target);
|
||||||
|
const projectGraph = createProjectGraph();
|
||||||
|
const projects = [projectGraph.nodes[opts.project]];
|
||||||
|
runCommand(
|
||||||
|
projects,
|
||||||
|
projectGraph,
|
||||||
|
env,
|
||||||
|
opts,
|
||||||
|
opts.overrides,
|
||||||
|
new EmptyReporter()
|
||||||
|
);
|
||||||
|
}
|
||||||
24
packages/workspace/src/command-line/supported-nx-commands.ts
Normal file
24
packages/workspace/src/command-line/supported-nx-commands.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
export const supportedNxCommands = [
|
||||||
|
'affected',
|
||||||
|
'affected:apps',
|
||||||
|
'affected:libs',
|
||||||
|
'affected:build',
|
||||||
|
'affected:test',
|
||||||
|
'affected:e2e',
|
||||||
|
'affected:dep-graph',
|
||||||
|
'affected:lint',
|
||||||
|
'print-affected',
|
||||||
|
'dep-graph',
|
||||||
|
'format',
|
||||||
|
'format:check',
|
||||||
|
'format:write',
|
||||||
|
'workspace-schematic',
|
||||||
|
'workspace-lint',
|
||||||
|
'migrate',
|
||||||
|
'report',
|
||||||
|
'run-many',
|
||||||
|
'list',
|
||||||
|
'help',
|
||||||
|
'--help',
|
||||||
|
'--version'
|
||||||
|
];
|
||||||
@ -2,13 +2,7 @@ import { appRootPath } from '../utils/app-root';
|
|||||||
import { ProjectGraph } from '../core/project-graph';
|
import { ProjectGraph } from '../core/project-graph';
|
||||||
import { NxJson } from '../core/shared-interfaces';
|
import { NxJson } from '../core/shared-interfaces';
|
||||||
import { Task } from './tasks-runner';
|
import { Task } from './tasks-runner';
|
||||||
import {
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
||||||
existsSync,
|
|
||||||
mkdirSync,
|
|
||||||
readFileSync,
|
|
||||||
rmdirSync,
|
|
||||||
writeFileSync
|
|
||||||
} from 'fs';
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { Hasher } from './hasher';
|
import { Hasher } from './hasher';
|
||||||
import * as fsExtra from 'fs-extra';
|
import * as fsExtra from 'fs-extra';
|
||||||
@ -17,10 +11,28 @@ import { DefaultTasksRunnerOptions } from './tasks-runner-v2';
|
|||||||
export type CachedResult = { terminalOutput: string; outputsPath: string };
|
export type CachedResult = { terminalOutput: string; outputsPath: string };
|
||||||
export type TaskWithCachedResult = { task: Task; cachedResult: CachedResult };
|
export type TaskWithCachedResult = { task: Task; cachedResult: CachedResult };
|
||||||
|
|
||||||
|
class CacheConfig {
|
||||||
|
constructor(private readonly options: DefaultTasksRunnerOptions) {}
|
||||||
|
|
||||||
|
isCacheableTask(task: Task) {
|
||||||
|
return (
|
||||||
|
this.options.cacheableOperations &&
|
||||||
|
this.options.cacheableOperations.indexOf(task.target.target) > -1 &&
|
||||||
|
!this.longRunningTask(task)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private longRunningTask(task: Task) {
|
||||||
|
return task.overrides['watch'] !== undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Cache {
|
export class Cache {
|
||||||
root = appRootPath;
|
root = appRootPath;
|
||||||
cachePath = this.createCacheDir();
|
cachePath = this.createCacheDir();
|
||||||
|
terminalOutputsDir = this.createTerminalOutputsDir();
|
||||||
hasher = new Hasher(this.projectGraph, this.nxJson);
|
hasher = new Hasher(this.projectGraph, this.nxJson);
|
||||||
|
cacheConfig = new CacheConfig(this.options);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly projectGraph: ProjectGraph,
|
private readonly projectGraph: ProjectGraph,
|
||||||
@ -29,7 +41,7 @@ export class Cache {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async get(task: Task): Promise<CachedResult> {
|
async get(task: Task): Promise<CachedResult> {
|
||||||
if (!this.cacheable(task)) return null;
|
if (!this.cacheConfig.isCacheableTask(task)) return null;
|
||||||
|
|
||||||
const res = await this.getFromLocalDir(task);
|
const res = await this.getFromLocalDir(task);
|
||||||
|
|
||||||
@ -47,8 +59,9 @@ export class Cache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async put(task: Task, terminalOutput: string, folders: string[]) {
|
async put(task: Task, terminalOutputPath: string, folders: string[]) {
|
||||||
if (!this.cacheable(task)) return;
|
if (!this.cacheConfig.isCacheableTask(task)) return;
|
||||||
|
const terminalOutput = readFileSync(terminalOutputPath).toString();
|
||||||
const hash = await this.hasher.hash(task);
|
const hash = await this.hasher.hash(task);
|
||||||
const td = join(this.cachePath, hash);
|
const td = join(this.cachePath, hash);
|
||||||
const tdCommit = join(this.cachePath, `${hash}.commit`);
|
const tdCommit = join(this.cachePath, `${hash}.commit`);
|
||||||
@ -101,6 +114,10 @@ export class Cache {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async temporaryOutputPath(task: Task) {
|
||||||
|
return join(this.terminalOutputsDir, await this.hasher.hash(task));
|
||||||
|
}
|
||||||
|
|
||||||
private async getFromLocalDir(task: Task) {
|
private async getFromLocalDir(task: Task) {
|
||||||
const hash = await this.hasher.hash(task);
|
const hash = await this.hasher.hash(task);
|
||||||
const tdCommit = join(this.cachePath, `${hash}.commit`);
|
const tdCommit = join(this.cachePath, `${hash}.commit`);
|
||||||
@ -116,13 +133,6 @@ export class Cache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private cacheable(task: Task) {
|
|
||||||
return (
|
|
||||||
this.options.cacheableOperations &&
|
|
||||||
this.options.cacheableOperations.indexOf(task.target.target) > -1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private createCacheDir() {
|
private createCacheDir() {
|
||||||
let dir;
|
let dir;
|
||||||
if (this.options.cacheDirectory) {
|
if (this.options.cacheDirectory) {
|
||||||
@ -139,4 +149,10 @@ export class Cache {
|
|||||||
}
|
}
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createTerminalOutputsDir() {
|
||||||
|
const path = join(this.cachePath, 'terminalOutputs');
|
||||||
|
mkdirSync(path, { recursive: true });
|
||||||
|
return path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { readJsonFile } from '../utils/fileutils';
|
|||||||
import { getCommand, getCommandAsString } from './utils';
|
import { getCommand, getCommandAsString } from './utils';
|
||||||
import { cliCommand } from '../core/file-utils';
|
import { cliCommand } from '../core/file-utils';
|
||||||
import { ProjectGraph } from '../core/project-graph';
|
import { ProjectGraph } from '../core/project-graph';
|
||||||
import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces';
|
import { NxJson } from '../core/shared-interfaces';
|
||||||
|
|
||||||
export interface DefaultTasksRunnerOptions {
|
export interface DefaultTasksRunnerOptions {
|
||||||
parallel?: boolean;
|
parallel?: boolean;
|
||||||
|
|||||||
5
packages/workspace/src/tasks-runner/empty-reporter.ts
Normal file
5
packages/workspace/src/tasks-runner/empty-reporter.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export class EmptyReporter {
|
||||||
|
beforeRun() {}
|
||||||
|
|
||||||
|
printResults() {}
|
||||||
|
}
|
||||||
@ -1,13 +1,12 @@
|
|||||||
import { AffectedEventType, Task, TasksRunner } from './tasks-runner';
|
import { AffectedEventType, Task, TasksRunner } from './tasks-runner';
|
||||||
import { defaultTasksRunner } from './default-tasks-runner';
|
|
||||||
import { isRelativePath } from '../utils/fileutils';
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { appRootPath } from '../utils/app-root';
|
import { appRootPath } from '../utils/app-root';
|
||||||
import { DefaultReporter, ReporterArgs } from './default-reporter';
|
import { ReporterArgs } from './default-reporter';
|
||||||
import * as yargs from 'yargs';
|
import * as yargs from 'yargs';
|
||||||
import { ProjectGraph, ProjectGraphNode } from '../core/project-graph';
|
import { ProjectGraph, ProjectGraphNode } from '../core/project-graph';
|
||||||
import { Environment, NxJson } from '../core/shared-interfaces';
|
import { Environment, NxJson } from '../core/shared-interfaces';
|
||||||
import { NxArgs } from '@nrwl/workspace/src/command-line/utils';
|
import { NxArgs } from '@nrwl/workspace/src/command-line/utils';
|
||||||
|
import { isRelativePath } from '../utils/fileutils';
|
||||||
|
|
||||||
type RunArgs = yargs.Arguments & ReporterArgs;
|
type RunArgs = yargs.Arguments & ReporterArgs;
|
||||||
|
|
||||||
@ -16,9 +15,9 @@ export function runCommand<T extends RunArgs>(
|
|||||||
projectGraph: ProjectGraph,
|
projectGraph: ProjectGraph,
|
||||||
{ nxJson, workspace }: Environment,
|
{ nxJson, workspace }: Environment,
|
||||||
nxArgs: NxArgs,
|
nxArgs: NxArgs,
|
||||||
overrides: any
|
overrides: any,
|
||||||
|
reporter: any
|
||||||
) {
|
) {
|
||||||
const reporter = new DefaultReporter();
|
|
||||||
reporter.beforeRun(projectsToRun.map(p => p.name), nxArgs, overrides);
|
reporter.beforeRun(projectsToRun.map(p => p.name), nxArgs, overrides);
|
||||||
const tasks: Task[] = projectsToRun.map(project =>
|
const tasks: Task[] = projectsToRun.map(project =>
|
||||||
createTask({
|
createTask({
|
||||||
@ -122,15 +121,17 @@ export function getRunner(
|
|||||||
tasksOptions: unknown;
|
tasksOptions: unknown;
|
||||||
} {
|
} {
|
||||||
if (!nxJson.tasksRunnerOptions) {
|
if (!nxJson.tasksRunnerOptions) {
|
||||||
|
const t = require('./default-tasks-runner');
|
||||||
return {
|
return {
|
||||||
tasksRunner: defaultTasksRunner,
|
tasksRunner: t.defaultTasksRunner,
|
||||||
tasksOptions: overrides
|
tasksOptions: overrides
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!runner && !nxJson.tasksRunnerOptions.default) {
|
if (!runner && !nxJson.tasksRunnerOptions.default) {
|
||||||
|
const t = require('./default-tasks-runner');
|
||||||
return {
|
return {
|
||||||
tasksRunner: defaultTasksRunner,
|
tasksRunner: t.defaultTasksRunner,
|
||||||
tasksOptions: overrides
|
tasksOptions: overrides
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,15 +4,16 @@ import { NxJson } from '../core/shared-interfaces';
|
|||||||
import { ProjectGraph } from '../core/project-graph';
|
import { ProjectGraph } from '../core/project-graph';
|
||||||
import { AffectedEventType, Task } from './tasks-runner';
|
import { AffectedEventType, Task } from './tasks-runner';
|
||||||
import { getCommand, getOutputs } from './utils';
|
import { getCommand, getOutputs } from './utils';
|
||||||
import { basename } from 'path';
|
import { fork, spawn } from 'child_process';
|
||||||
import { spawn } from 'child_process';
|
|
||||||
import { DefaultTasksRunnerOptions } from './tasks-runner-v2';
|
import { DefaultTasksRunnerOptions } from './tasks-runner-v2';
|
||||||
import { output } from '../utils/output';
|
import { output } from '../utils/output';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { appRootPath } from '../utils/app-root';
|
||||||
|
|
||||||
export class TaskOrchestrator {
|
export class TaskOrchestrator {
|
||||||
|
workspaceRoot = appRootPath;
|
||||||
cache = new Cache(this.projectGraph, this.nxJson, this.options);
|
cache = new Cache(this.projectGraph, this.nxJson, this.options);
|
||||||
cli = cliCommand();
|
cli = cliCommand();
|
||||||
isYarn = basename(process.env.npm_execpath || 'npm').startsWith('yarn');
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly nxJson: NxJson,
|
private readonly nxJson: NxJson,
|
||||||
@ -40,7 +41,7 @@ export class TaskOrchestrator {
|
|||||||
if (left.length > 0) {
|
if (left.length > 0) {
|
||||||
const task = left.pop();
|
const task = left.pop();
|
||||||
return that
|
return that
|
||||||
.spawnProcess(task)
|
.forkProcess(task)
|
||||||
.then(code => {
|
.then(code => {
|
||||||
res.push({
|
res.push({
|
||||||
task,
|
task,
|
||||||
@ -103,39 +104,56 @@ export class TaskOrchestrator {
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
private spawnProcess(task: Task) {
|
private forkProcess(task: Task) {
|
||||||
const taskOutputs = getOutputs(this.projectGraph.nodes, task);
|
const taskOutputs = getOutputs(this.projectGraph.nodes, task);
|
||||||
return new Promise(res => {
|
return this.cache.temporaryOutputPath(task).then(outputPath => {
|
||||||
const command = this.isYarn ? 'yarn' : 'npm';
|
return new Promise((res, rej) => {
|
||||||
const commandArgs = this.isYarn
|
try {
|
||||||
? getCommand(this.cli, this.isYarn, task)
|
const p = fork(this.getCommand(), this.getCommandArgs(task), {
|
||||||
: ['run', ...getCommand(this.cli, this.isYarn, task)];
|
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
||||||
const p = spawn(command, commandArgs, {
|
env: { ...process.env, NX_TERMINAL_OUTPUT_PATH: outputPath }
|
||||||
stdio: [process.stdin, 'pipe', 'pipe'],
|
|
||||||
env: { ...process.env, FORCE_COLOR: 'true' }
|
|
||||||
});
|
|
||||||
|
|
||||||
let out = [];
|
|
||||||
|
|
||||||
p.stdout.on('data', data => {
|
|
||||||
out.push(data);
|
|
||||||
process.stdout.write(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
p.stderr.on('data', data => {
|
|
||||||
out.push(data);
|
|
||||||
process.stderr.write(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
p.on('close', code => {
|
|
||||||
if (code === 0) {
|
|
||||||
this.cache.put(task, out.join(''), taskOutputs).then(() => {
|
|
||||||
res(code);
|
|
||||||
});
|
});
|
||||||
} else {
|
p.on('close', code => {
|
||||||
res(code);
|
if (code === 0) {
|
||||||
|
this.cache.put(task, outputPath, taskOutputs).then(() => {
|
||||||
|
res(code);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res(code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
rej(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getCommand() {
|
||||||
|
return path.join(
|
||||||
|
this.workspaceRoot,
|
||||||
|
'node_modules',
|
||||||
|
'@nrwl',
|
||||||
|
'cli',
|
||||||
|
'lib',
|
||||||
|
'run-cli.js'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCommandArgs(task: Task) {
|
||||||
|
const args = Object.entries(task.overrides || {}).map(
|
||||||
|
([prop, value]) => `--${prop}=${value}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const config = task.target.configuration
|
||||||
|
? `:${task.target.configuration}`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return [
|
||||||
|
'run',
|
||||||
|
`${task.target.project}:${task.target.target}${config}`,
|
||||||
|
...args
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,6 @@ import {
|
|||||||
TaskCompleteEvent,
|
TaskCompleteEvent,
|
||||||
TasksRunner
|
TasksRunner
|
||||||
} from './tasks-runner';
|
} from './tasks-runner';
|
||||||
import { output } from '../utils/output';
|
|
||||||
import { readJsonFile } from '../utils/fileutils';
|
|
||||||
import { cliCommand } from '../core/file-utils';
|
|
||||||
import { ProjectGraph } from '../core/project-graph';
|
import { ProjectGraph } from '../core/project-graph';
|
||||||
import { NxJson } from '../core/shared-interfaces';
|
import { NxJson } from '../core/shared-interfaces';
|
||||||
import { TaskOrderer } from './task-orderer';
|
import { TaskOrderer } from './task-orderer';
|
||||||
@ -52,7 +49,6 @@ async function runAllTasks(
|
|||||||
options: DefaultTasksRunnerOptions,
|
options: DefaultTasksRunnerOptions,
|
||||||
context: { target: string; projectGraph: ProjectGraph; nxJson: NxJson }
|
context: { target: string; projectGraph: ProjectGraph; nxJson: NxJson }
|
||||||
): Promise<Array<{ task: Task; type: any; success: boolean }>> {
|
): Promise<Array<{ task: Task; type: any; success: boolean }>> {
|
||||||
assertPackageJsonScriptExists();
|
|
||||||
const stages = new TaskOrderer(
|
const stages = new TaskOrderer(
|
||||||
context.target,
|
context.target,
|
||||||
context.projectGraph
|
context.projectGraph
|
||||||
@ -91,25 +87,4 @@ function tasksToStatuses(tasks: Task[], success: boolean) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertPackageJsonScriptExists() {
|
|
||||||
const cli = cliCommand();
|
|
||||||
// Make sure the `package.json` has the `nx: "nx"`
|
|
||||||
const packageJson = readJsonFile('./package.json');
|
|
||||||
if (!packageJson.scripts || !packageJson.scripts[cli]) {
|
|
||||||
output.error({
|
|
||||||
title: `The "scripts" section of your 'package.json' must contain "${cli}": "${cli}"`,
|
|
||||||
bodyLines: [
|
|
||||||
output.colors.gray('...'),
|
|
||||||
' "scripts": {',
|
|
||||||
output.colors.gray(' ...'),
|
|
||||||
` "${cli}": "${cli}"`,
|
|
||||||
output.colors.gray(' ...'),
|
|
||||||
' }',
|
|
||||||
output.colors.gray('...')
|
|
||||||
]
|
|
||||||
});
|
|
||||||
return process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default tasksRunnerV2;
|
export default tasksRunnerV2;
|
||||||
|
|||||||
@ -19,4 +19,4 @@ jest --maxWorkers=1 ./build/e2e/run-many.test.js &&
|
|||||||
jest --maxWorkers=1 ./build/e2e/storybook.test.js &&
|
jest --maxWorkers=1 ./build/e2e/storybook.test.js &&
|
||||||
jest --maxWorkers=1 ./build/e2e/upgrade-module.test.js &&
|
jest --maxWorkers=1 ./build/e2e/upgrade-module.test.js &&
|
||||||
jest --maxWorkers=1 ./build/e2e/web.test.js &&
|
jest --maxWorkers=1 ./build/e2e/web.test.js &&
|
||||||
jest --maxWorkers=1 ./build/e2e/default-tasks-runner.test.js
|
jest --maxWorkers=1 ./build/e2e/tasks-runner-v2.test.js
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user