diff --git a/projects/build-env/src/executors/bootstrap/bootstrap-env.ts b/projects/build-env/src/executors/bootstrap/bootstrap-env.ts index f9caf6e7..9f7f3ee2 100644 --- a/projects/build-env/src/executors/bootstrap/bootstrap-env.ts +++ b/projects/build-env/src/executors/bootstrap/bootstrap-env.ts @@ -1,12 +1,12 @@ import { join } from 'node:path'; import { startVerdaccioServer, - type StarVerdaccioOptions, + type StartVerdaccioOptions, type VercaddioServerResult, } from './verdaccio-registry'; import { writeFile } from 'node:fs/promises'; import { setupNpmWorkspace } from './npm'; -import { formatInfo } from '../../internal/logging'; +import { formatError, formatInfo } from '../../internal/logging'; import { VERDACCIO_REGISTRY_JSON } from './constants'; import { logger } from '@nx/devkit'; import { @@ -15,13 +15,11 @@ import { VERDACCIO_ENV_TOKEN, } from './npm'; -export type BootstrapEnvironmentOptions = Partial< - StarVerdaccioOptions & Environment -> & { - keepServerRunning?: boolean; - projectName: string; - environmentRoot: string; -}; +export type BootstrapEnvironmentOptions = StartVerdaccioOptions & + Environment & { + projectName: string; + environmentRoot: string; + }; export type BootstrapEnvironmentResult = Environment & { registry: VercaddioServerResult; @@ -29,43 +27,92 @@ export type BootstrapEnvironmentResult = Environment & { }; export async function bootstrapEnvironment( - options + options: BootstrapEnvironmentOptions ): Promise { - const { verbose, environmentRoot, storage } = options; - const registryResult = await startVerdaccioServer({ - storage: storage ?? join(environmentRoot, 'storage'), - verbose, - readyWhen: 'Environment ready under', - keepServerRunning: true, - ...options, - }); + const { verbose, environmentRoot, storage, ...rest } = options; + const parsedStorage = storage ?? join(environmentRoot, 'storage'); - await setupNpmWorkspace(environmentRoot, verbose); - const userconfig = join(environmentRoot, '.npmrc'); - configureRegistry({ ...registryResult.registry, userconfig }, verbose); + let registryResult; + try { + registryResult = await startVerdaccioServer({ + storage: parsedStorage, + verbose, + readyWhen: 'Environment ready under', + ...(rest as StartVerdaccioOptions), + }); + } catch (error) { + logger.error( + formatError( + `Error starting verdaccio registry: ${(error as Error).message}`, + VERDACCIO_ENV_TOKEN + ) + ); + throw error; + } - const activeRegistry: BootstrapEnvironmentResult = { - ...registryResult, - root: environmentRoot, - }; + try { + logger.info( + formatInfo( + `Setup NPM workspace in ${environmentRoot}`, + VERDACCIO_ENV_TOKEN + ) + ); + await setupNpmWorkspace(environmentRoot, verbose); - logger.info( - formatInfo( - `Save active verdaccio registry data to file: ${activeRegistry.root}`, - VERDACCIO_ENV_TOKEN - ) - ); - await writeFile( - join(activeRegistry.root, VERDACCIO_REGISTRY_JSON), - JSON.stringify(activeRegistry.registry, null, 2) - ); + const { registry } = registryResult; + const { url, port, host } = registry; + const userconfig = join(environmentRoot, '.npmrc'); + configureRegistry({ url, port, host, userconfig }, verbose); + } catch (error) { + logger.error( + formatError( + `Error configuring verdaccio NPM registry: ${(error as Error).message}`, + VERDACCIO_ENV_TOKEN + ) + ); + throw error; + } - logger.info( - formatInfo( - `Environment ready under: ${activeRegistry.root}`, - VERDACCIO_ENV_TOKEN - ) - ); + try { + logger.info( + formatInfo( + `Save active verdaccio registry data to file: ${join( + environmentRoot, + VERDACCIO_REGISTRY_JSON + )}`, + VERDACCIO_ENV_TOKEN + ) + ); + await writeFile( + join(environmentRoot, VERDACCIO_REGISTRY_JSON), + JSON.stringify(environmentRoot, null, 2) + ); // NOTICE: This is a "readyWhen" condition + logger.info( + formatInfo( + `Environment ready under: ${environmentRoot}`, + VERDACCIO_ENV_TOKEN + ) + ); + logger.info( + formatInfo( + `File saved: ${join(environmentRoot, VERDACCIO_REGISTRY_JSON)}`, + VERDACCIO_ENV_TOKEN + ) + ); - return activeRegistry; + return { + ...registryResult, + environmentRoot: environmentRoot, + }; + } catch (error) { + logger.error( + formatError( + `Error saving verdaccio registry data to ${environmentRoot}: ${ + (error as Error).message + }`, + VERDACCIO_ENV_TOKEN + ) + ); + throw new Error(`Error saving verdaccio registry data. ${error.message}`); + } } diff --git a/projects/build-env/src/executors/bootstrap/executor.ts b/projects/build-env/src/executors/bootstrap/executor.ts index d3793ed6..52cdbe06 100644 --- a/projects/build-env/src/executors/bootstrap/executor.ts +++ b/projects/build-env/src/executors/bootstrap/executor.ts @@ -8,7 +8,10 @@ import { formatInfo } from '../../internal/logging'; import { VERDACCIO_ENV_TOKEN } from './npm'; import { join } from 'node:path'; import { VERDACCIO_REGISTRY_JSON } from './constants'; -import { DEFAULT_STOP_VERDACCIO_TARGET } from '../../internal/constants'; +import { + DEFAULT_BOOTSTRAP_TARGET, + DEFAULT_STOP_VERDACCIO_TARGET, +} from '../../internal/constants'; export type BootstrapExecutorOutput = { success: boolean; @@ -19,12 +22,12 @@ export type BootstrapExecutorOutput = { export async function bootstrapExecutor( options: BootstrapExecutorOptions, context: ExecutorContext -) { +): Promise { const { configurationName, projectName } = context; const { keepServerRunning, environmentRoot } = options; logger.info( - `Execute @push-based/build-env:bootstrap with options: ${JSON.stringify( + `Execute @push-based/build-env:${DEFAULT_BOOTSTRAP_TARGET} with options: ${JSON.stringify( options, null, 2 @@ -53,7 +56,7 @@ export async function bootstrapExecutor( formatInfo(`Verdaccio server running under ${url}`, VERDACCIO_ENV_TOKEN) ); } else { - await runExecutor( + for await (const s of await runExecutor( { project: projectName, target: DEFAULT_STOP_VERDACCIO_TARGET, @@ -63,13 +66,14 @@ export async function bootstrapExecutor( filePath: join(environmentRoot, VERDACCIO_REGISTRY_JSON), }, context - ); + )) { + } } - return Promise.resolve({ + return { success: true, command: 'Bootstrapped environment successfully.', - } satisfies BootstrapExecutorOutput); + }; } export default bootstrapExecutor; diff --git a/projects/build-env/src/executors/bootstrap/executor.unit-test.ts b/projects/build-env/src/executors/bootstrap/executor.unit-test.ts index b0336994..96d6ccef 100644 --- a/projects/build-env/src/executors/bootstrap/executor.unit-test.ts +++ b/projects/build-env/src/executors/bootstrap/executor.unit-test.ts @@ -45,7 +45,7 @@ describe('runBootstrapExecutor', () => { storage: 'tmp/storage', url: 'http://localhost:4873', }, - root: `tmp/environments/${e2eProjectName}`, + environmentRoot: `tmp/environments/${e2eProjectName}`, stop: expect.any(Function), }); runExecutorSpy.mockResolvedValueOnce({ diff --git a/projects/build-env/src/executors/bootstrap/npm.ts b/projects/build-env/src/executors/bootstrap/npm.ts index a7abf832..f8c54419 100644 --- a/projects/build-env/src/executors/bootstrap/npm.ts +++ b/projects/build-env/src/executors/bootstrap/npm.ts @@ -3,7 +3,7 @@ import { join } from 'node:path'; import { ensureDirectoryExists } from '../../internal/file-system'; import { formatError, formatInfo } from '../../internal/logging'; import { logger } from '@nx/devkit'; -import type { VerdaccioProcessResult } from './verdaccio-registry'; +import type { VercaddioServerResult } from './verdaccio-registry'; import { objectToCliArgs } from '../../internal/terminal'; export const NPM_ENV_TOKEN = 'Npm Env: '; @@ -49,11 +49,11 @@ export async function setupNpmWorkspace( export const VERDACCIO_ENV_TOKEN = 'Verdaccio Env: '; export type Environment = { - root: string; + environmentRoot: string; }; export type ConfigureRegistryOptions = Pick< - VerdaccioProcessResult, + VercaddioServerResult, 'port' | 'host' | 'url' > & { userconfig?: string; @@ -93,7 +93,7 @@ export function configureRegistry( } export type UnconfigureRegistryOptions = Pick< - VerdaccioProcessResult, + VercaddioServerResult, 'port' | 'host' > & { userconfig?: string; diff --git a/projects/build-env/src/executors/bootstrap/verdaccio-registry.ts b/projects/build-env/src/executors/bootstrap/verdaccio-registry.ts index 97ebcf56..7bc907d9 100644 --- a/projects/build-env/src/executors/bootstrap/verdaccio-registry.ts +++ b/projects/build-env/src/executors/bootstrap/verdaccio-registry.ts @@ -71,7 +71,7 @@ export type VerdaccioExecuterOptions = { clear?: boolean; }; -export type StarVerdaccioOptions = VerdaccioExecuterOptions & +export type StartVerdaccioOptions = VerdaccioExecuterOptions & StarVerdaccioOnlyOptions; export async function startVerdaccioServer({ @@ -82,7 +82,7 @@ export async function startVerdaccioServer({ clear = true, verbose = true, ...opt -}: StarVerdaccioOptions): Promise { +}: StartVerdaccioOptions): Promise { let verdaccioIsRunning = false; const startServerPromise = () => diff --git a/projects/build-env/src/executors/npm-install/executor.ts b/projects/build-env/src/executors/npm-install/executor.ts index cb1e24a9..743e83be 100644 --- a/projects/build-env/src/executors/npm-install/executor.ts +++ b/projects/build-env/src/executors/npm-install/executor.ts @@ -36,6 +36,7 @@ export default async function runNpmInstallExecutor( fund: false, // avoid polluted terminal shrinkwrap: false, // avoid package-lock creation or update save: true, // save to package.json dependencies + userconfig: join('.npmrc'), // enforce local npmrc }), cwd: environmentRoot, verbose: true, diff --git a/projects/build-env/src/executors/setup/executor.ts b/projects/build-env/src/executors/setup/executor.ts index 8879c0bd..2c749061 100644 --- a/projects/build-env/src/executors/setup/executor.ts +++ b/projects/build-env/src/executors/setup/executor.ts @@ -28,12 +28,13 @@ export default async function runSetupEnvironmentExecutor( context: ExecutorContext ) { const { configurationName: configuration, projectName } = context; - + const { verbose, environmentRoot, keepServerRunning } = + terminalAndExecutorOptions; try { - const { verbose, environmentRoot, keepServerRunning } = - terminalAndExecutorOptions; - - await runExecutor( + logger.info( + `Bootstrapping environment for ${projectName} in ${environmentRoot}` + ); + for await (const s of await runExecutor( { project: projectName, target: DEFAULT_BOOTSTRAP_TARGET, @@ -42,12 +43,24 @@ export default async function runSetupEnvironmentExecutor( { ...terminalAndExecutorOptions, // we always want to keep the server running as in the following step we install packages - // the keepServerRunning option is only used to stop the server after the installation (or keep it running for debug reasons) + // the `keepServerRunning` passed in `options` is only used to stop the server after the installation (or keep it running for debug reasons) keepServerRunning: true, }, context - ); + )) { + } + } catch (error) { + logger.error(error); + return { + success: false, + command: `Fails executing target ${DEFAULT_BOOTSTRAP_TARGET}\n ${error.message}`, + }; + } + try { + logger.info(`Installing packages for ${projectName} in ${environmentRoot}`); + if (verbose) { + } await executeProcess({ command: 'nx', args: objectToCliArgs({ @@ -57,9 +70,17 @@ export default async function runSetupEnvironmentExecutor( cwd: process.cwd(), ...(verbose ? { verbose } : {}), }); + } catch (error) { + logger.error(error); + return { + success: false, + command: `Fails executing target ${DEFAULT_INSTALL_TARGET}\n ${error.message}`, + }; + } + try { if (!keepServerRunning) { - await await runExecutor( + for await (const s of await runExecutor( { project: projectName, target: DEFAULT_STOP_VERDACCIO_TARGET, @@ -70,7 +91,8 @@ export default async function runSetupEnvironmentExecutor( filePath: join(environmentRoot, VERDACCIO_REGISTRY_JSON), }, context - ); + )) { + } } else { const { url } = readJsonFile( join(environmentRoot, VERDACCIO_REGISTRY_JSON) @@ -78,7 +100,6 @@ export default async function runSetupEnvironmentExecutor( logger.info(`Verdaccio server kept running under : ${url}`); } } catch (error) { - // nx build-env cli-e2e logger.error(error); return { success: false, diff --git a/projects/build-env/src/internal/constants.ts b/projects/build-env/src/internal/constants.ts index 4e49b712..6301918a 100644 --- a/projects/build-env/src/internal/constants.ts +++ b/projects/build-env/src/internal/constants.ts @@ -2,7 +2,7 @@ import { join } from 'node:path'; export const DEFAULT_ENVIRONMENTS_OUTPUT_DIR = join('tmp', 'environments'); export const NPMRC_FILENAME = '.npmrc'; export const DEFAULT_START_VERDACCIO_TARGET = 'build-env-verdaccio-start'; -export const DEFAULT_BOOTSTRAP_TARGET = 'build-env-bootstrap'; +export const DEFAULT_BOOTSTRAP_TARGET = 'build-env-env-bootstrap'; export const DEFAULT_INSTALL_TARGET = 'build-env-env-install'; export const DEFAULT_NPM_PUBLISH_TARGET = 'build-env-release-publish'; export const DEFAULT_NPM_INSTALL_TARGET = 'build-env-release-install'; diff --git a/projects/build-env/src/plugin/build-env.plugin.ts b/projects/build-env/src/plugin/build-env.plugin.ts index 794db7b7..9dccdab4 100644 --- a/projects/build-env/src/plugin/build-env.plugin.ts +++ b/projects/build-env/src/plugin/build-env.plugin.ts @@ -16,7 +16,7 @@ import { DEFAULT_INSTALL_TARGET, DEFAULT_SETUP_TARGET, } from '../internal/constants'; -import type { StarVerdaccioOptions } from '../executors/bootstrap/verdaccio-registry'; +import type { StartVerdaccioOptions } from '../executors/bootstrap/verdaccio-registry'; import { VERDACCIO_REGISTRY_JSON } from '../executors/bootstrap/constants'; import { uniquePort } from '../executors/bootstrap/unique-port'; @@ -178,7 +178,7 @@ function verdaccioTargets( NormalizedCreateNodeOptions['environments'], 'environmentsDir' > & - StarVerdaccioOptions + StartVerdaccioOptions ): Record { const { name: environmentProject } = projectConfig; const { environmentsDir, ...verdaccioOptions } = options; @@ -227,7 +227,6 @@ function getEnvTargets( }, ], options: { environmentRoot }, - command: 'echo Dependencies installed!', }, // runs bootstrap-env, install-env and stop-verdaccio [DEFAULT_SETUP_TARGET]: { diff --git a/tooling/measures/nx-performance/audit/cache-size.audit.ts b/tooling/measures/nx-performance/audit/cache-size.audit.ts index 65fea72f..ca0d8c45 100644 --- a/tooling/measures/nx-performance/audit/cache-size.audit.ts +++ b/tooling/measures/nx-performance/audit/cache-size.audit.ts @@ -1,4 +1,10 @@ -import { Audit, AuditOutput, Table, Issue } from '@code-pushup/models'; +import { + Audit, + AuditOutput, + Table, + Issue, + TableRowObject, +} from '@code-pushup/models'; import { crawlFileSystem, executeProcess, @@ -41,19 +47,52 @@ export async function cacheSizeAudits( const cacheSizeResults = await projectTaskCacheSizeData(cacheSizeTasks); return cacheSizeResults.map( - ({ cacheSize, data, task, issues }): AuditOutput => ({ - slug: getCacheSizeAuditSlug(task), - score: scoreProjectTaskCacheSize(cacheSize, maxCacheSize), - value: cacheSize, - displayValue: formatBytes(cacheSize), - details: { - table: data, - issues, - }, - }) + ({ cacheSize, data, task, issues }): AuditOutput => { + const { rows, ...restTable } = data; + + return { + slug: getCacheSizeAuditSlug(task), + score: scoreProjectTaskCacheSize(cacheSize, maxCacheSize), + value: cacheSize, + displayValue: formatBytes(cacheSize), + details: { + table: { + ...restTable, + rows: prepareFileEntries(rows as FileSizeEntry[], { + maxFiles: 30, + }) as TableRowObject[], + }, + issues, + }, + }; + } ); } +export function prepareFileEntries( + rows: FileSizeEntry[], + options: { + maxFiles?: number; + } = {} +): (Omit & { size: string })[] { + const { maxFiles = 10 } = options; + const topSizes = rows + .sort((a, b) => b.size - a.size) + .slice(0, maxFiles) + .map(({ file, size }) => ({ + file, + size: formatBytes(size), + })); + + if (maxFiles >= rows.length) { + return topSizes; + } + return [ + ...topSizes, + { file: `and ${rows.length - maxFiles} more...`, size: '...' }, + ]; +} + export function scoreProjectTaskCacheSize( size: number, maxSize: number @@ -120,6 +159,11 @@ export async function projectTaskCacheSizeData( return results; } +export type FileSizeEntry = { + file: string; + size: number; +}; + export async function folderSize(options: { directory: string; pattern?: string | RegExp; @@ -159,8 +203,8 @@ export async function folderSize(options: { ], rows: fileSizes.map(({ file, size }) => ({ file, - size: formatBytes(size), - })), + size, + })) satisfies FileSizeEntry[], }, }; }