Skip to content

Commit

Permalink
feat(core): prefix when using --output-style=stream
Browse files Browse the repository at this point in the history
  • Loading branch information
vsavkin committed May 25, 2022
1 parent 5378a02 commit 844679e
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 12 deletions.
2 changes: 1 addition & 1 deletion docs/generated/cli/affected.md
Expand Up @@ -113,7 +113,7 @@ Isolate projects which previously failed

Type: string

Choices: [dynamic, static, stream]
Choices: [dynamic, static, stream, stream-without-prefixes]

Defines how Nx emits outputs tasks logs

Expand Down
2 changes: 1 addition & 1 deletion docs/generated/cli/run-many.md
Expand Up @@ -77,7 +77,7 @@ Only run the target on projects which previously failed

Type: string

Choices: [dynamic, static, stream]
Choices: [dynamic, static, stream, stream-without-prefixes]

Defines how Nx emits outputs tasks logs

Expand Down
4 changes: 2 additions & 2 deletions docs/generated/packages/cli.json
Expand Up @@ -39,13 +39,13 @@
"name": "run-many",
"id": "run-many",
"file": "generated/cli/run-many",
"content": "---\ntitle: 'run-many - CLI command'\ndescription: 'Run target for multiple listed projects'\n---\n\n# run-many\n\nRun target for multiple listed projects\n\n## Usage\n\n```bash\nnx run-many\n```\n\n[Install `nx` globally](/getting-started/nx-setup#install-nx) to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpx nx`.\n\n### Examples\n\nTest all projects:\n\n```bash\nnx run-many --target=test --all\n```\n\nTest proj1 and proj2:\n\n```bash\nnx run-many --target=test --projects=proj1,proj2\n```\n\nTest proj1 and proj2 in parallel:\n\n```bash\nnx run-many --target=test --projects=proj1,proj2 --parallel=2\n```\n\n## Options\n\n### all\n\nType: boolean\n\nRun the target on all projects in the workspace\n\n### configuration\n\nType: string\n\nThis is the configuration to use when performing tasks on projects\n\n### exclude\n\nType: array\n\nDefault: []\n\nExclude certain projects from being processed\n\n### help\n\nType: boolean\n\nShow help\n\n### ~~only-failed~~\n\nType: boolean\n\nDefault: false\n\n**Deprecated:** The command to rerun failed projects will appear if projects fail. This now does nothing and will be removed in v15.\n\nOnly run the target on projects which previously failed\n\n### output-style\n\nType: string\n\nChoices: [dynamic, static, stream]\n\nDefines how Nx emits outputs tasks logs\n\n### parallel\n\nType: string\n\nMax number of parallel processes [default is 3]\n\n### projects\n\nType: string\n\nProjects to run (comma delimited)\n\n### runner\n\nType: string\n\nOverride the tasks runner in `nx.json`\n\n### skip-nx-cache\n\nType: boolean\n\nDefault: false\n\nRerun the tasks even when the results are available in the cache\n\n### target\n\nType: string\n\nTask to run for affected projects\n\n### verbose\n\nPrint additional error stack trace on failure\n\n### version\n\nType: boolean\n\nShow version number\n"
"content": "---\ntitle: 'run-many - CLI command'\ndescription: 'Run target for multiple listed projects'\n---\n\n# run-many\n\nRun target for multiple listed projects\n\n## Usage\n\n```bash\nnx run-many\n```\n\n[Install `nx` globally](/getting-started/nx-setup#install-nx) to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpx nx`.\n\n### Examples\n\nTest all projects:\n\n```bash\nnx run-many --target=test --all\n```\n\nTest proj1 and proj2:\n\n```bash\nnx run-many --target=test --projects=proj1,proj2\n```\n\nTest proj1 and proj2 in parallel:\n\n```bash\nnx run-many --target=test --projects=proj1,proj2 --parallel=2\n```\n\n## Options\n\n### all\n\nType: boolean\n\nRun the target on all projects in the workspace\n\n### configuration\n\nType: string\n\nThis is the configuration to use when performing tasks on projects\n\n### exclude\n\nType: array\n\nDefault: []\n\nExclude certain projects from being processed\n\n### help\n\nType: boolean\n\nShow help\n\n### ~~only-failed~~\n\nType: boolean\n\nDefault: false\n\n**Deprecated:** The command to rerun failed projects will appear if projects fail. This now does nothing and will be removed in v15.\n\nOnly run the target on projects which previously failed\n\n### output-style\n\nType: string\n\nChoices: [dynamic, static, stream, stream-without-prefixes]\n\nDefines how Nx emits outputs tasks logs\n\n### parallel\n\nType: string\n\nMax number of parallel processes [default is 3]\n\n### projects\n\nType: string\n\nProjects to run (comma delimited)\n\n### runner\n\nType: string\n\nOverride the tasks runner in `nx.json`\n\n### skip-nx-cache\n\nType: boolean\n\nDefault: false\n\nRerun the tasks even when the results are available in the cache\n\n### target\n\nType: string\n\nTask to run for affected projects\n\n### verbose\n\nPrint additional error stack trace on failure\n\n### version\n\nType: boolean\n\nShow version number\n"
},
{
"name": "affected",
"id": "affected",
"file": "generated/cli/affected",
"content": "---\ntitle: 'affected - CLI command'\ndescription: 'Run target for affected projects'\n---\n\n# affected\n\nRun target for affected projects\n\n## Usage\n\n```bash\nnx affected\n```\n\n[Install `nx` globally](/getting-started/nx-setup#install-nx) to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpx nx`.\n\n### Examples\n\nRun custom target for all affected projects:\n\n```bash\nnx affected --target=custom-target\n```\n\nRun tests in parallel:\n\n```bash\nnx affected --target=test --parallel=5\n```\n\nRun the test target for all projects:\n\n```bash\nnx affected --target=test --all\n```\n\nRun tests for all the projects affected by changing the index.ts file:\n\n```bash\nnx affected --target=test --files=libs/mylib/src/index.ts\n```\n\nRun tests for all the projects affected by the changes between main and HEAD (e.g., PR):\n\n```bash\nnx affected --target=test --base=main --head=HEAD\n```\n\nRun tests for all the projects affected by the last commit on main:\n\n```bash\nnx affected --target=test --base=main~1 --head=main\n```\n\n## Options\n\n### all\n\nType: boolean\n\nAll projects\n\n### base\n\nType: string\n\nBase of the current branch (usually main)\n\n### configuration\n\nType: string\n\nThis is the configuration to use when performing tasks on projects\n\n### exclude\n\nType: array\n\nDefault: []\n\nExclude certain projects from being processed\n\n### files\n\nType: array\n\nChange the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas\n\n### head\n\nType: string\n\nLatest commit of the current branch (usually HEAD)\n\n### help\n\nType: boolean\n\nShow help\n\n### ~~only-failed~~\n\nType: boolean\n\nDefault: false\n\n**Deprecated:** The command to rerun failed projects will appear if projects fail. This now does nothing and will be removed in v15.\n\nIsolate projects which previously failed\n\n### output-style\n\nType: string\n\nChoices: [dynamic, static, stream]\n\nDefines how Nx emits outputs tasks logs\n\n### parallel\n\nType: string\n\nMax number of parallel processes [default is 3]\n\n### runner\n\nType: string\n\nThis is the name of the tasks runner configured in nx.json\n\n### skip-nx-cache\n\nType: boolean\n\nDefault: false\n\nRerun the tasks even when the results are available in the cache\n\n### target\n\nType: string\n\nTask to run for affected projects\n\n### uncommitted\n\nType: boolean\n\nUncommitted changes\n\n### untracked\n\nType: boolean\n\nUntracked changes\n\n### verbose\n\nPrint additional error stack trace on failure\n\n### version\n\nType: boolean\n\nShow version number\n"
"content": "---\ntitle: 'affected - CLI command'\ndescription: 'Run target for affected projects'\n---\n\n# affected\n\nRun target for affected projects\n\n## Usage\n\n```bash\nnx affected\n```\n\n[Install `nx` globally](/getting-started/nx-setup#install-nx) to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpx nx`.\n\n### Examples\n\nRun custom target for all affected projects:\n\n```bash\nnx affected --target=custom-target\n```\n\nRun tests in parallel:\n\n```bash\nnx affected --target=test --parallel=5\n```\n\nRun the test target for all projects:\n\n```bash\nnx affected --target=test --all\n```\n\nRun tests for all the projects affected by changing the index.ts file:\n\n```bash\nnx affected --target=test --files=libs/mylib/src/index.ts\n```\n\nRun tests for all the projects affected by the changes between main and HEAD (e.g., PR):\n\n```bash\nnx affected --target=test --base=main --head=HEAD\n```\n\nRun tests for all the projects affected by the last commit on main:\n\n```bash\nnx affected --target=test --base=main~1 --head=main\n```\n\n## Options\n\n### all\n\nType: boolean\n\nAll projects\n\n### base\n\nType: string\n\nBase of the current branch (usually main)\n\n### configuration\n\nType: string\n\nThis is the configuration to use when performing tasks on projects\n\n### exclude\n\nType: array\n\nDefault: []\n\nExclude certain projects from being processed\n\n### files\n\nType: array\n\nChange the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas\n\n### head\n\nType: string\n\nLatest commit of the current branch (usually HEAD)\n\n### help\n\nType: boolean\n\nShow help\n\n### ~~only-failed~~\n\nType: boolean\n\nDefault: false\n\n**Deprecated:** The command to rerun failed projects will appear if projects fail. This now does nothing and will be removed in v15.\n\nIsolate projects which previously failed\n\n### output-style\n\nType: string\n\nChoices: [dynamic, static, stream, stream-without-prefixes]\n\nDefines how Nx emits outputs tasks logs\n\n### parallel\n\nType: string\n\nMax number of parallel processes [default is 3]\n\n### runner\n\nType: string\n\nThis is the name of the tasks runner configured in nx.json\n\n### skip-nx-cache\n\nType: boolean\n\nDefault: false\n\nRerun the tasks even when the results are available in the cache\n\n### target\n\nType: string\n\nTask to run for affected projects\n\n### uncommitted\n\nType: boolean\n\nUncommitted changes\n\n### untracked\n\nType: boolean\n\nUntracked changes\n\n### verbose\n\nPrint additional error stack trace on failure\n\n### version\n\nType: boolean\n\nShow version number\n"
},
{
"name": "affected:graph",
Expand Down
43 changes: 43 additions & 0 deletions e2e/cli/src/output-style.test.ts
@@ -0,0 +1,43 @@
import {
isNotWindows,
newProject,
readFile,
readJson,
runCLI,
runCLIAsync,
runCommand,
tmpProjPath,
uniq,
updateFile,
updateProjectConfig,
} from '@nrwl/e2e/utils';
import { renameSync } from 'fs';
import { packagesWeCareAbout } from 'nx/src/command-line/report';

describe('Output Style', () => {
beforeEach(() => newProject());

it('should stream output', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/web:app ${myapp}`);
updateProjectConfig(myapp, (c) => {
c.targets['counter'] = {
executor: '@nrwl/workspace:counter',
options: {
to: 2,
},
};
return c;
});

const withPrefixes = runCLI(
`counter ${myapp} --result=true --output-style=stream`
);
expect(withPrefixes).toContain(`[${myapp}`);

const noPrefixes = runCLI(
`counter ${myapp} --result=true --output-style=stream-without-prefixes`
);
expect(noPrefixes).not.toContain(`[${myapp}`);
});
});
25 changes: 23 additions & 2 deletions packages/nx/bin/run-executor.ts
@@ -1,5 +1,6 @@
import { appendFileSync, openSync, writeFileSync } from 'fs';
import { run } from '../src/command-line/run';
import { addCommandPrefixIfNeeded } from '../src/utils/add-command-prefix';

if (process.env.NX_TERMINAL_OUTPUT_PATH) {
setUpOutputWatching(
Expand All @@ -12,12 +13,14 @@ if (!process.env.NX_WORKSPACE_ROOT) {
console.error('Invalid Nx command invocation');
process.exit(1);
}
let projectName;
requireCli();

function requireCli() {
process.env.NX_CLI_SET = 'true';
try {
const args = JSON.parse(process.argv[2]);
projectName = args.targetDescription.project;
run(
process.cwd(),
process.env.NX_WORKSPACE_ROOT,
Expand Down Expand Up @@ -66,7 +69,16 @@ function setUpOutputWatching(captureStderr: boolean, streamOutput: boolean) {
onlyStdout.push(chunk);
appendFileSync(stdoutAndStderrLogFileHandle, chunk);
if (streamOutput) {
stdoutWrite.apply(process.stdout, [chunk, encoding, callback]);
const updatedChunk = addCommandPrefixIfNeeded(
projectName,
chunk,
encoding
);
stdoutWrite.apply(process.stdout, [
updatedChunk.content,
updatedChunk.encoding,
callback,
]);
} else {
callback();
}
Expand All @@ -79,7 +91,16 @@ function setUpOutputWatching(captureStderr: boolean, streamOutput: boolean) {
) => {
appendFileSync(stdoutAndStderrLogFileHandle, chunk);
if (streamOutput) {
stderrWrite.apply(process.stderr, [chunk, encoding, callback]);
const updatedChunk = addCommandPrefixIfNeeded(
projectName,
chunk,
encoding
);
stderrWrite.apply(process.stderr, [
updatedChunk.content,
updatedChunk.encoding,
callback,
]);
} else {
callback();
}
Expand Down
10 changes: 8 additions & 2 deletions packages/nx/src/command-line/nx-commands.ts
Expand Up @@ -543,7 +543,7 @@ function withOutputStyleOption(yargs: yargs.Argv): yargs.Argv {
return yargs.option('output-style', {
describe: 'Defines how Nx emits outputs tasks logs',
type: 'string',
choices: ['dynamic', 'static', 'stream'],
choices: ['dynamic', 'static', 'stream', 'stream-without-prefixes'],
});
}

Expand Down Expand Up @@ -628,7 +628,13 @@ function withRunOneOptions(yargs: yargs.Argv) {
.option('output-style', {
describe: 'Defines how Nx emits outputs tasks logs',
type: 'string',
choices: ['dynamic', 'static', 'stream', 'compact'],
choices: [
'dynamic',
'static',
'stream',
'stream-without-prefixes',
'compact',
],
});

if (executorShouldShowHelp) {
Expand Down
21 changes: 17 additions & 4 deletions packages/nx/src/tasks-runner/forked-process-task-runner.ts
Expand Up @@ -18,6 +18,7 @@ import {
} from './batch/batch-messages';
import { stripIndents } from '../utils/strip-indents';
import { Task } from '../config/task-graph';
import { addCommandPrefixIfNeeded } from '../utils/add-command-prefix';

const workerPath = join(__dirname, './batch/run-batch.js');

Expand Down Expand Up @@ -98,7 +99,10 @@ export class ForkedProcessTaskRunner {
{
streamOutput,
temporaryOutputPath,
}: { streamOutput: boolean; temporaryOutputPath: string }
}: {
streamOutput: boolean;
temporaryOutputPath: string;
}
) {
return new Promise<{ code: number; terminalOutput: string }>((res, rej) => {
try {
Expand Down Expand Up @@ -128,14 +132,20 @@ export class ForkedProcessTaskRunner {
let outWithErr = [];
p.stdout.on('data', (chunk) => {
if (streamOutput) {
process.stdout.write(chunk);
process.stdout.write(
addCommandPrefixIfNeeded(task.target.project, chunk, 'utf-8')
.content
);
}
out.push(chunk.toString());
outWithErr.push(chunk.toString());
});
p.stderr.on('data', (chunk) => {
if (streamOutput) {
process.stderr.write(chunk);
process.stderr.write(
addCommandPrefixIfNeeded(task.target.project, chunk, 'utf-8')
.content
);
}
outWithErr.push(chunk.toString());
});
Expand Down Expand Up @@ -168,7 +178,10 @@ export class ForkedProcessTaskRunner {
{
streamOutput,
temporaryOutputPath,
}: { streamOutput: boolean; temporaryOutputPath: string }
}: {
streamOutput: boolean;
temporaryOutputPath: string;
}
) {
return new Promise<{ code: number; terminalOutput: string }>((res, rej) => {
try {
Expand Down
4 changes: 4 additions & 0 deletions packages/nx/src/tasks-runner/run-command.ts
Expand Up @@ -105,6 +105,10 @@ export async function runCommand(
const projectNames = projectsToRun.map((t) => t.name);
if (nxArgs.outputStyle == 'stream') {
process.env.NX_STREAM_OUTPUT = 'true';
process.env.NX_PREFIX_OUTPUT = 'true';
}
if (nxArgs.outputStyle == 'stream-without-prefixes') {
process.env.NX_STREAM_OUTPUT = 'true';
}
const { lifeCycle, renderIsDone } = await getTerminalOutputLifeCycle(
initiatingProject,
Expand Down
1 change: 1 addition & 0 deletions packages/nx/src/tasks-runner/task-orchestrator.ts
Expand Up @@ -285,6 +285,7 @@ export class TaskOrchestrator {
this.initiatingProject,
this.options
);

const pipeOutput = this.pipeOutputCapture(task);

// execution
Expand Down
48 changes: 48 additions & 0 deletions packages/nx/src/utils/add-command-prefix.spec.ts
@@ -0,0 +1,48 @@
import { addPrefixToLines } from './add-command-prefix';
const stripAnsi = require('strip-ansi');

describe('addPrefixToLines', () => {
it('should add project name as a prefix', () => {
const prefixed = addPrefixToLines('myproj', ['one', 'two', 'three']);
expect(prefixed.map(stripAnsi)).toEqual([
'[myproj ] one',
'[myproj ] two',
'[myproj ] three',
]);
});

it('should handle project names which length = 15', () => {
const prefixed = addPrefixToLines('123456789012345', [
'one',
'two',
'three',
]);
expect(prefixed.map(stripAnsi)).toEqual([
'[123456789012345] one',
'[123456789012345] two',
'[123456789012345] three',
]);
});

it('should handle long project names', () => {
const prefixed = addPrefixToLines('12345678901234567890', [
'one',
'two',
'three',
]);
expect(prefixed.map(stripAnsi)).toEqual([
'[...901234567890] one',
'[...901234567890] two',
'[...901234567890] three',
]);
});

it('should not prefix last empty line', () => {
const prefixed = addPrefixToLines('myproj', ['one', 'two', '']);
expect(prefixed.map(stripAnsi)).toEqual([
'[myproj ] one',
'[myproj ] two',
'',
]);
});
});
63 changes: 63 additions & 0 deletions packages/nx/src/utils/add-command-prefix.ts
@@ -0,0 +1,63 @@
import * as chalk from 'chalk';

export function addCommandPrefixIfNeeded(
projectName: string,
chunk: any,
encoding: string
) {
if (process.env.NX_PREFIX_OUTPUT === 'true') {
const lines = (
typeof chunk === 'string' ? chunk : chunk.toString('utf-8')
).split('\n');
return {
content: addPrefixToLines(projectName, lines).join('\n'),
encoding: 'utf-8',
};
} else {
return { content: chunk, encoding: encoding };
}
}

export function addPrefixToLines(projectName: string, lines: string[]) {
const updatedLines = [];
for (let i = 0; i < lines.length; ++i) {
if (i === lines.length - 1 && lines[i] === '') {
updatedLines.push('');
} else {
updatedLines.push(`${projectNamePrefix(projectName)} ${lines[i]}`);
}
}
return updatedLines;
}

const colors = [
chalk.green,
chalk.greenBright,
chalk.red,
chalk.redBright,
chalk.cyan,
chalk.cyanBright,
chalk.yellow,
chalk.yellowBright,
chalk.magenta,
chalk.magentaBright,
];

function projectNamePrefix(projectName: string) {
const n = normalizeProjectName(projectName);
return colors[projectNameToIndex(projectName)](`[${n}]`);
}

function projectNameToIndex(projectName: string): number {
let code = 0;
for (let i = 0; i < projectName.length; ++i) {
code += projectName.charCodeAt(i);
}
return code % colors.length;
}

function normalizeProjectName(projectName: string): string {
return projectName.length > 15
? `...${projectName.substring(projectName.length - 12)}`
: `${projectName} `.substring(0, 15);
}

1 comment on commit 844679e

@vercel
Copy link

@vercel vercel bot commented on 844679e May 25, 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-five.vercel.app
nx-dev-git-master-nrwl.vercel.app
nx-dev-nrwl.vercel.app
nx.dev

Please sign in to comment.