feat(exec): log prefix (#8151)

close #8065
This commit is contained in:
Khải
2024-06-03 06:25:21 +07:00
committed by GitHub
parent 9bfebc5b13
commit bc0618cf19
6 changed files with 179 additions and 7 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-script-runners": patch
"pnpm": patch
---
`exec` now also streams prefixed output when `--recursive` or `--parallel` is specified just as `run` does [#8065](https://github.com/pnpm/pnpm/issues/8065).

View File

@@ -47,6 +47,7 @@
"@pnpm/command": "workspace:*",
"@pnpm/common-cli-options-help": "workspace:*",
"@pnpm/config": "workspace:*",
"@pnpm/core-loggers": "workspace:*",
"@pnpm/crypto.base32-hash": "workspace:*",
"@pnpm/error": "workspace:*",
"@pnpm/lifecycle": "workspace:*",

View File

@@ -1,5 +1,6 @@
import path from 'path'
import { docsUrl, type RecursiveSummary, throwOnCommandFail, readProjectManifestOnly } from '@pnpm/cli-utils'
import { type LifecycleMessage, lifecycleLogger } from '@pnpm/core-loggers'
import { type Config, types } from '@pnpm/config'
import { makeNodeRequireOption } from '@pnpm/lifecycle'
import { logger } from '@pnpm/logger'
@@ -39,6 +40,7 @@ export function rcOptionsTypes (): Record<string, unknown> {
'use-node-version',
'unsafe-perm',
'workspace-concurrency',
'reporter-hide-prefix',
], types),
'shell-mode': Boolean,
'resume-from': String,
@@ -134,7 +136,7 @@ export async function handler (
resumeFrom?: string
reportSummary?: boolean
implicitlyFellbackFromRun?: boolean
} & Pick<Config, 'extraBinPaths' | 'extraEnv' | 'lockfileDir' | 'modulesDir' | 'dir' | 'userAgent' | 'recursive' | 'workspaceDir' | 'nodeOptions'>,
} & Pick<Config, 'extraBinPaths' | 'extraEnv' | 'lockfileDir' | 'modulesDir' | 'dir' | 'userAgent' | 'recursive' | 'reporterHidePrefix' | 'workspaceDir' | 'nodeOptions'>,
params: string[]
): Promise<{ exitCode: number }> {
// For backward compatibility
@@ -184,6 +186,7 @@ export async function handler (
'./node_modules/.bin',
...opts.extraBinPaths,
]
const reporterShowPrefix = opts.recursive && !opts.reporterHidePrefix
for (const chunk of chunks) {
// eslint-disable-next-line no-await-in-loop
await Promise.all(chunk.map(async (prefix: string) =>
@@ -205,12 +208,47 @@ export async function handler (
prependPaths,
userAgent: opts.userAgent,
})
await execa(params[0], params.slice(1), {
cwd: prefix,
env,
stdio: 'inherit',
shell: opts.shellMode ?? false,
})
const [cmd, ...args] = params
if (reporterShowPrefix) {
const manifest = await readProjectManifestOnly(prefix)
const child = execa(cmd, args, {
cwd: prefix,
env,
stdio: 'pipe',
shell: opts.shellMode ?? false,
})
const lifecycleOpts = {
wd: prefix,
depPath: manifest.name ?? path.relative(opts.dir, prefix),
stage: '(exec)',
} satisfies Partial<LifecycleMessage>
const logFn = (stdio: 'stdout' | 'stderr') => (data: unknown): void => {
for (const line of String(data).split('\n')) {
lifecycleLogger.debug({
...lifecycleOpts,
stdio,
line,
})
}
}
child.stdout!.on('data', logFn('stdout'))
child.stderr!.on('data', logFn('stderr'))
void child.once('close', exitCode => {
lifecycleLogger.debug({
...lifecycleOpts,
exitCode: exitCode ?? 1,
optional: false,
})
})
await child
} else {
await execa(cmd, args, {
cwd: prefix,
env,
stdio: 'inherit',
shell: opts.shellMode ?? false,
})
}
result[prefix].status = 'passed'
result[prefix].duration = getExecutionDuration(startTime)
} catch (err: any) { // eslint-disable-line

View File

@@ -0,0 +1,121 @@
import fs from 'fs'
import path from 'path'
import { readProjects } from '@pnpm/filter-workspace-packages'
import { logger } from '@pnpm/logger'
import { exec } from '@pnpm/plugin-commands-script-runners'
import { preparePackages } from '@pnpm/prepare'
import writeYamlFile from 'write-yaml-file'
import { DEFAULT_OPTS } from './utils'
jest.mock('@pnpm/logger', () => {
const debug = jest.fn()
return {
logger: () => ({ debug }),
}
})
const lifecycleLogger = logger('lifecycle')
afterEach(() => {
(lifecycleLogger.debug as jest.Mock).mockClear()
})
test('pnpm exec --recursive prints prefixes', async () => {
preparePackages([
{
location: 'packages/foo',
package: { name: 'foo' },
},
{
location: 'packages/bar',
package: { name: 'bar' },
},
])
await writeYamlFile('pnpm-workspace.yaml', {
packages: ['packages/*'],
})
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
const scriptFile = path.resolve('script.js')
fs.writeFileSync(scriptFile, `
console.log('hello from stdout')
console.error('hello from stderr')
console.log('name is ' + require(require('path').resolve('package.json')).name)
`)
await exec.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
recursive: true,
bail: true,
selectedProjectsGraph,
}, [process.execPath, scriptFile])
for (const name of ['foo', 'bar']) {
const loggerOpts = {
wd: path.resolve('packages', name),
depPath: name,
stage: '(exec)',
}
expect(lifecycleLogger.debug).toHaveBeenCalledWith({
...loggerOpts,
line: 'hello from stdout',
stdio: 'stdout',
})
expect(lifecycleLogger.debug).toHaveBeenCalledWith({
...loggerOpts,
line: 'hello from stderr',
stdio: 'stderr',
})
expect(lifecycleLogger.debug).toHaveBeenCalledWith({
...loggerOpts,
line: `name is ${name}`,
stdio: 'stdout',
})
expect(lifecycleLogger.debug).toHaveBeenCalledWith({
...loggerOpts,
optional: false,
exitCode: 0,
})
}
})
test('pnpm exec --recursive --reporter-hide-prefix does not print prefixes', async () => {
preparePackages([
{
location: 'packages/foo',
package: { name: 'foo' },
},
{
location: 'packages/bar',
package: { name: 'bar' },
},
])
await writeYamlFile('pnpm-workspace.yaml', {
packages: ['packages/*'],
})
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
const scriptFile = path.resolve('script.js')
fs.writeFileSync(scriptFile, `
console.log('hello from stdout')
console.error('hello from stderr')
console.log('name is ' + require(require('path').resolve('package.json')).name)
`)
await exec.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
recursive: true,
bail: true,
reporterHidePrefix: true,
selectedProjectsGraph,
}, [process.execPath, scriptFile])
expect(lifecycleLogger.debug).not.toHaveBeenCalled()
})

View File

@@ -27,6 +27,9 @@
{
"path": "../../config/config"
},
{
"path": "../../packages/core-loggers"
},
{
"path": "../../packages/crypto.base32-hash"
},

3
pnpm-lock.yaml generated
View File

@@ -1381,6 +1381,9 @@ importers:
'@pnpm/config':
specifier: workspace:*
version: link:../../config/config
'@pnpm/core-loggers':
specifier: workspace:*
version: link:../../packages/core-loggers
'@pnpm/crypto.base32-hash':
specifier: workspace:*
version: link:../../packages/crypto.base32-hash