Skip to content

Commit

Permalink
feat(core): add the flag to stop execution on first error
Browse files Browse the repository at this point in the history
  • Loading branch information
vsavkin committed May 28, 2022
1 parent 7ee93f0 commit 7bfceb6
Show file tree
Hide file tree
Showing 23 changed files with 183 additions and 33 deletions.
8 changes: 8 additions & 0 deletions docs/generated/cli/affected-apps.md
Expand Up @@ -81,6 +81,14 @@ Type: boolean

Show help

### nx-bail

Type: boolean

Default: false

Stop command execution after the first failed task

### ~~only-failed~~

Type: boolean
Expand Down
8 changes: 8 additions & 0 deletions docs/generated/cli/affected-graph.md
Expand Up @@ -123,6 +123,14 @@ Type: string

Bind the project graph server to a specific ip address.

### nx-bail

Type: boolean

Default: false

Stop command execution after the first failed task

### ~~only-failed~~

Type: boolean
Expand Down
8 changes: 8 additions & 0 deletions docs/generated/cli/affected-libs.md
Expand Up @@ -81,6 +81,14 @@ Type: boolean

Show help

### nx-bail

Type: boolean

Default: false

Stop command execution after the first failed task

### ~~only-failed~~

Type: boolean
Expand Down
8 changes: 8 additions & 0 deletions docs/generated/cli/affected.md
Expand Up @@ -99,6 +99,14 @@ Type: boolean

Show help

### nx-bail

Type: boolean

Default: false

Stop command execution after the first failed task

### ~~only-failed~~

Type: boolean
Expand Down
8 changes: 8 additions & 0 deletions docs/generated/cli/format-check.md
Expand Up @@ -67,6 +67,14 @@ Type: boolean

Format only libraries and applications files.

### nx-bail

Type: boolean

Default: false

Stop command execution after the first failed task

### ~~only-failed~~

Type: boolean
Expand Down
8 changes: 8 additions & 0 deletions docs/generated/cli/format-write.md
Expand Up @@ -67,6 +67,14 @@ Type: boolean

Format only libraries and applications files.

### nx-bail

Type: boolean

Default: false

Stop command execution after the first failed task

### ~~only-failed~~

Type: boolean
Expand Down
8 changes: 8 additions & 0 deletions docs/generated/cli/print-affected.md
Expand Up @@ -93,6 +93,14 @@ Type: boolean

Show help

### nx-bail

Type: boolean

Default: false

Stop command execution after the first failed task

### ~~only-failed~~

Type: boolean
Expand Down
8 changes: 8 additions & 0 deletions docs/generated/cli/run-many.md
Expand Up @@ -63,6 +63,14 @@ Type: boolean

Show help

### nx-bail

Type: boolean

Default: false

Stop command execution after the first failed task

### ~~only-failed~~

Type: boolean
Expand Down
1 change: 1 addition & 0 deletions docs/generated/devkit/index.md
Expand Up @@ -927,6 +927,7 @@ stored in the daemon process. To reset both run: `nx reset`.
| `options` | [`DefaultTasksRunnerOptions`](../../devkit/index#defaulttasksrunneroptions) |
| `context?` | `Object` |
| `context.initiatingProject?` | `string` |
| `context.nxArgs` | `NxArgs` |
| `context.nxJson` | [`NxJsonConfiguration`](../../devkit/index#nxjsonconfiguration)<`string`[] \| `"*"`\> |
| `context.projectGraph` | [`ProjectGraph`](../../devkit/index#projectgraph)<`any`\> |
| `context.target?` | `string` |
Expand Down
16 changes: 8 additions & 8 deletions docs/generated/packages/cli.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/generated/packages/devkit.json

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions e2e/cli/src/nx-bail.test.ts
@@ -0,0 +1,54 @@
import { newProject, runCLI, uniq, updateProjectConfig } from '@nrwl/e2e/utils';

describe('Nx Bail', () => {
beforeEach(() => newProject());

it('should stop executing all tasks when one of the tasks fails', async () => {
const myapp1 = uniq('a');
const myapp2 = uniq('b');
runCLI(`generate @nrwl/web:app ${myapp1}`);
runCLI(`generate @nrwl/web:app ${myapp2}`);
updateProjectConfig(myapp1, (c) => {
c.targets['error'] = {
executor: 'nx:run-commands',
options: {
command: 'echo boom1 && exit 1',
},
};
return c;
});
updateProjectConfig(myapp2, (c) => {
c.targets['error'] = {
executor: 'nx:run-commands',
options: {
command: 'echo boom2 && exit 1',
},
};
return c;
});

let withoutBail = runCLI(`run-many --target=error --all --parallel=1`, {
silenceError: true,
})
.split('\n')
.map((r) => r.trim())
.filter((r) => r);

withoutBail = withoutBail.slice(withoutBail.indexOf('Failed tasks:'));
expect(withoutBail).toContain(`- ${myapp1}:error`);
expect(withoutBail).toContain(`- ${myapp2}:error`);

let withBail = runCLI(
`run-many --target=error --all --parallel=1 --nx-bail`,
{
silenceError: true,
}
)
.split('\n')
.map((r) => r.trim())
.filter((r) => r);
withBail = withBail.slice(withBail.indexOf('Failed tasks:'));
expect(withBail).toContain(`- ${myapp1}:error`);
expect(withBail).not.toContain(`- ${myapp2}:error`);
});
});
6 changes: 3 additions & 3 deletions e2e/cli/src/output-style.test.ts
Expand Up @@ -3,18 +3,18 @@ import { newProject, runCLI, updateProjectConfig } from '@nrwl/e2e/utils';
describe('Output Style', () => {
beforeEach(() => newProject());

it('ttt should stream output', async () => {
it('should stream output', async () => {
const myapp = 'abcdefghijklmon';
runCLI(`generate @nrwl/web:app ${myapp}`);
updateProjectConfig(myapp, (c) => {
c.targets['inner'] = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
command: 'echo inner',
},
};
c.targets['echo'] = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
commands: ['echo 1', 'echo 2', `nx inner ${myapp}`],
parallel: false,
Expand Down
2 changes: 1 addition & 1 deletion e2e/utils/index.ts
Expand Up @@ -552,7 +552,7 @@ export function runCLI(
return r;
} catch (e) {
if (opts.silenceError) {
return e.stdout?.toString() + e.stderr?.toString();
return stripConsoleColors(e.stdout?.toString() + e.stderr?.toString());
} else {
logError(
`Original command: ${command}`,
Expand Down
15 changes: 15 additions & 0 deletions packages/nx/src/command-line/nx-commands.ts
Expand Up @@ -429,6 +429,11 @@ function withAffectedOptions(yargs: yargs.Argv): yargs.Argv {
.option('verbose', {
describe: 'Print additional error stack trace on failure',
})
.option('nx-bail', {
describe: 'Stop command execution after the first failed task',
type: 'boolean',
default: false,
})
.conflicts({
files: ['uncommitted', 'untracked', 'base', 'head', 'all'],
untracked: ['uncommitted', 'files', 'base', 'head', 'all'],
Expand Down Expand Up @@ -489,6 +494,11 @@ function withRunManyOptions(yargs: yargs.Argv): yargs.Argv {
.option('verbose', {
describe: 'Print additional error stack trace on failure',
})
.option('nx-bail', {
describe: 'Stop command execution after the first failed task',
type: 'boolean',
default: false,
})
.conflicts({
all: 'projects',
});
Expand Down Expand Up @@ -639,6 +649,11 @@ function withRunOneOptions(yargs: yargs.Argv) {
'stream-without-prefixes',
'compact',
],
})
.option('nx-bail', {
describe: 'Stop command execution after the first failed task',
type: 'boolean',
default: false,
});

if (executorShouldShowHelp) {
Expand Down
6 changes: 5 additions & 1 deletion packages/nx/src/tasks-runner/default-tasks-runner.ts
Expand Up @@ -7,6 +7,7 @@ import { LifeCycle } from './life-cycle';
import { ProjectGraph } from '../config/project-graph';
import { NxJsonConfiguration } from '../config/nx-json';
import { Task } from '../config/task-graph';
import { NxArgs } from '../utils/command-line-utils';

export interface RemoteCache {
retrieve: (hash: string, cacheDirectory: string) => Promise<boolean>;
Expand Down Expand Up @@ -35,6 +36,7 @@ export const defaultTasksRunner: TasksRunner<
initiatingProject?: string;
projectGraph: ProjectGraph;
nxJson: NxJsonConfiguration;
nxArgs: NxArgs;
}
): Promise<{ [id: string]: TaskStatus }> => {
if (
Expand Down Expand Up @@ -69,6 +71,7 @@ async function runAllTasks(
initiatingProject?: string;
projectGraph: ProjectGraph;
nxJson: NxJsonConfiguration;
nxArgs: NxArgs;
}
): Promise<{ [id: string]: TaskStatus }> {
const defaultTargetDependencies = context.nxJson.targetDependencies ?? {};
Expand Down Expand Up @@ -96,7 +99,8 @@ async function runAllTasks(
context.initiatingProject,
context.projectGraph,
taskGraph,
options
options,
context.nxArgs?.nxBail
);

return orchestrator.run();
Expand Down
Expand Up @@ -96,7 +96,9 @@ export class StaticRunManyTerminalOutputLifeCycle implements LifeCycle {
const skippedTasks = this.skippedTasks();
if (skippedTasks.length > 0) {
bodyLines.push(
output.dim('Tasks not run because their dependencies failed:'),
output.dim(
'Tasks not run because their dependencies failed or --nx-bail=true:'
),
'',
...skippedTasks.map((task) => `${output.dim('-')} ${task.id}`),
''
Expand Down
1 change: 1 addition & 0 deletions packages/nx/src/tasks-runner/run-command.ts
Expand Up @@ -137,6 +137,7 @@ export async function runCommand(
target: nxArgs.target,
projectGraph,
nxJson,
nxArgs,
}
);

Expand Down
31 changes: 21 additions & 10 deletions packages/nx/src/tasks-runner/task-orchestrator.ts
Expand Up @@ -40,14 +40,17 @@ export class TaskOrchestrator {

private groups = [];

private bailed = false;

// endregion internal state

constructor(
private readonly hasher: Hasher,
private readonly initiatingProject: string | undefined,
private readonly projectGraph: ProjectGraph,
private readonly taskGraph: TaskGraph,
private readonly options: DefaultTasksRunnerOptions
private readonly options: DefaultTasksRunnerOptions,
private readonly bail: boolean
) {}

async run() {
Expand Down Expand Up @@ -76,7 +79,7 @@ export class TaskOrchestrator {

private async executeNextBatchOfTasksUsingTaskSchedule() {
// completed all the tasks
if (!this.tasksSchedule.hasTasks()) {
if (!this.tasksSchedule.hasTasks() || this.bailed) {
return null;
}

Expand Down Expand Up @@ -404,15 +407,23 @@ export class TaskOrchestrator {
for (const { taskId, status } of taskResults) {
if (this.completedTasks[taskId] === undefined) {
this.completedTasks[taskId] = status;
}

if (status === 'failure' || status === 'skipped') {
this.complete(
this.reverseTaskDeps[taskId].map((depTaskId) => ({
taskId: depTaskId,
status: 'skipped',
}))
);
if (status === 'failure' || status === 'skipped') {
if (this.bail) {
// mark the execution as bailed which will stop all further execution
// only the tasks that are currently running will finish
this.bailed = true;
} else {
// only mark the packages that depend on the current task as skipped
// other tasks will continue to execute
this.complete(
this.reverseTaskDeps[taskId].map((depTaskId) => ({
taskId: depTaskId,
status: 'skipped',
}))
);
}
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/nx/src/tasks-runner/tasks-runner-v2.ts
Expand Up @@ -6,6 +6,7 @@ import { TaskStatus } from './tasks-runner';
import { ProjectGraph } from '../config/project-graph';
import { NxJsonConfiguration } from '../config/nx-json';
import { Task } from '../config/task-graph';
import { NxArgs } from '../utils/command-line-utils';

export { DefaultTasksRunnerOptions, RemoteCache } from './default-tasks-runner';

Expand All @@ -24,6 +25,7 @@ export const tasksRunnerV2 = (
initiatingProject?: string;
projectGraph: ProjectGraph;
nxJson: NxJsonConfiguration;
nxArgs: NxArgs;
}
): any => {
if (!options.lifeCycle.startCommand) {
Expand Down
2 changes: 2 additions & 0 deletions packages/nx/src/tasks-runner/tasks-runner.ts
@@ -1,6 +1,7 @@
import { NxJsonConfiguration } from '../config/nx-json';
import { ProjectGraph } from '../config/project-graph';
import { Task } from '../config/task-graph';
import { NxArgs } from '../utils/command-line-utils';

export type TaskStatus =
| 'success'
Expand All @@ -22,5 +23,6 @@ export type TasksRunner<T = unknown> = (
initiatingProject?: string | null;
projectGraph: ProjectGraph;
nxJson: NxJsonConfiguration;
nxArgs: NxArgs;
}
) => any | Promise<{ [id: string]: TaskStatus }>;

1 comment on commit 7bfceb6

@vercel
Copy link

@vercel vercel bot commented on 7bfceb6 May 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx.dev
nx-dev-git-master-nrwl.vercel.app
nx-five.vercel.app
nx-dev-nrwl.vercel.app

Please sign in to comment.