feat(core): show progress on ci if graph construction takes longer than expected (#29392)
Progress spinners currently only show up when the terminal is a tty. This updates it to show static text on CI, but at a longer duration
This commit is contained in:
parent
ba1641d7fd
commit
3c3d2e5f82
@ -77,10 +77,7 @@ import {
|
||||
FLUSH_SYNC_GENERATOR_CHANGES_TO_DISK,
|
||||
type HandleFlushSyncGeneratorChangesToDiskMessage,
|
||||
} from '../message-types/flush-sync-generator-changes-to-disk';
|
||||
import {
|
||||
DelayedSpinner,
|
||||
SHOULD_SHOW_SPINNERS,
|
||||
} from '../../utils/delayed-spinner';
|
||||
import { DelayedSpinner } from '../../utils/delayed-spinner';
|
||||
|
||||
const DAEMON_ENV_SETTINGS = {
|
||||
NX_PROJECT_GLOB_CACHE: 'false',
|
||||
@ -199,16 +196,13 @@ export class DaemonClient {
|
||||
sourceMaps: ConfigurationSourceMaps;
|
||||
}> {
|
||||
let spinner: DelayedSpinner;
|
||||
if (SHOULD_SHOW_SPINNERS) {
|
||||
// If the graph takes a while to load, we want to show a spinner.
|
||||
spinner = new DelayedSpinner(
|
||||
'Calculating the project graph on the Nx Daemon',
|
||||
500
|
||||
'Calculating the project graph on the Nx Daemon'
|
||||
).scheduleMessageUpdate(
|
||||
'Calculating the project graph on the Nx Daemon is taking longer than expected. Re-run with NX_DAEMON=false to see more details.',
|
||||
30_000
|
||||
{ ciDelay: 60_000, delay: 30_000 }
|
||||
);
|
||||
}
|
||||
try {
|
||||
const response = await this.sendToDaemonViaQueue({
|
||||
type: 'REQUEST_PROJECT_GRAPH',
|
||||
|
||||
@ -44,7 +44,7 @@ import {
|
||||
ConfigurationSourceMaps,
|
||||
mergeMetadata,
|
||||
} from './utils/project-configuration-utils';
|
||||
import { DelayedSpinner, SHOULD_SHOW_SPINNERS } from '../utils/delayed-spinner';
|
||||
import { DelayedSpinner } from '../utils/delayed-spinner';
|
||||
|
||||
let storedFileMap: FileMap | null = null;
|
||||
let storedAllWorkspaceFiles: FileData[] | null = null;
|
||||
@ -323,24 +323,28 @@ async function updateProjectGraphWithPlugins(
|
||||
return;
|
||||
}
|
||||
if (inProgressPlugins.size === 1) {
|
||||
return `Creating project graph dependencies with ${
|
||||
spinner.setMessage(
|
||||
`Creating project graph dependencies with ${
|
||||
inProgressPlugins.keys()[0]
|
||||
}`;
|
||||
}`
|
||||
);
|
||||
} else if (process.env.NX_VERBOSE_LOGGING === 'true') {
|
||||
return [
|
||||
spinner.setMessage(
|
||||
[
|
||||
`Creating project graph dependencies with ${inProgressPlugins.size} plugins`,
|
||||
...Array.from(inProgressPlugins).map((p) => ` - ${p}`),
|
||||
].join('\n');
|
||||
].join('\n')
|
||||
);
|
||||
} else {
|
||||
return `Creating project graph dependencies with ${inProgressPlugins.size} plugins`;
|
||||
spinner.setMessage(
|
||||
`Creating project graph dependencies with ${inProgressPlugins.size} plugins`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (SHOULD_SHOW_SPINNERS) {
|
||||
spinner = new DelayedSpinner(
|
||||
`Creating project graph dependencies with ${plugins.length} plugins`
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
createDependencyPlugins.map(async (plugin) => {
|
||||
@ -439,22 +443,26 @@ export async function applyProjectMetadata(
|
||||
return;
|
||||
}
|
||||
if (inProgressPlugins.size === 1) {
|
||||
return `Creating project metadata with ${inProgressPlugins.keys()[0]}`;
|
||||
spinner.setMessage(
|
||||
`Creating project metadata with ${inProgressPlugins.keys()[0]}`
|
||||
);
|
||||
} else if (process.env.NX_VERBOSE_LOGGING === 'true') {
|
||||
return [
|
||||
spinner.setMessage(
|
||||
[
|
||||
`Creating project metadata with ${inProgressPlugins.size} plugins`,
|
||||
...Array.from(inProgressPlugins).map((p) => ` - ${p}`),
|
||||
].join('\n');
|
||||
].join('\n')
|
||||
);
|
||||
} else {
|
||||
return `Creating project metadata with ${inProgressPlugins.size} plugins`;
|
||||
spinner.setMessage(
|
||||
`Creating project metadata with ${inProgressPlugins.size} plugins`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (SHOULD_SHOW_SPINNERS) {
|
||||
spinner = new DelayedSpinner(
|
||||
`Creating project metadata with ${plugins.length} plugins`
|
||||
);
|
||||
}
|
||||
|
||||
const promises = plugins.map(async (plugin) => {
|
||||
if (plugin.createMetadata) {
|
||||
|
||||
@ -31,11 +31,7 @@ import {
|
||||
} from '../error-types';
|
||||
import { CreateNodesResult } from '../plugins/public-api';
|
||||
import { isGlobPattern } from '../../utils/globs';
|
||||
import { isOnDaemon } from '../../daemon/is-on-daemon';
|
||||
import {
|
||||
DelayedSpinner,
|
||||
SHOULD_SHOW_SPINNERS,
|
||||
} from '../../utils/delayed-spinner';
|
||||
import { DelayedSpinner } from '../../utils/delayed-spinner';
|
||||
|
||||
export type SourceInformation = [file: string | null, plugin: string];
|
||||
export type ConfigurationSourceMaps = Record<
|
||||
@ -339,22 +335,26 @@ export async function createProjectConfigurations(
|
||||
}
|
||||
|
||||
if (inProgressPlugins.size === 1) {
|
||||
return `Creating project graph nodes with ${inProgressPlugins.keys()[0]}`;
|
||||
spinner.setMessage(
|
||||
`Creating project graph nodes with ${inProgressPlugins.keys()[0]}`
|
||||
);
|
||||
} else if (process.env.NX_VERBOSE_LOGGING === 'true') {
|
||||
return [
|
||||
spinner.setMessage(
|
||||
[
|
||||
`Creating project graph nodes with ${inProgressPlugins.size} plugins`,
|
||||
...Array.from(inProgressPlugins).map((p) => ` - ${p}`),
|
||||
].join('\n');
|
||||
].join('\n')
|
||||
);
|
||||
} else {
|
||||
return `Creating project graph nodes with ${inProgressPlugins.size} plugins`;
|
||||
spinner.setMessage(
|
||||
`Creating project graph nodes with ${inProgressPlugins.size} plugins`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (SHOULD_SHOW_SPINNERS) {
|
||||
spinner = new DelayedSpinner(
|
||||
`Creating project graph nodes with ${plugins.length} plugins`
|
||||
);
|
||||
}
|
||||
|
||||
const results: Array<ReturnType<LoadedNxPlugin['createNodes'][1]>> = [];
|
||||
const errors: Array<
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import * as ora from 'ora';
|
||||
import { isCI } from './is-ci';
|
||||
|
||||
export type DelayedSpinnerOptions = {
|
||||
delay?: number;
|
||||
ciDelay?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* A class that allows to delay the creation of a spinner, as well
|
||||
@ -10,18 +16,26 @@ export class DelayedSpinner {
|
||||
spinner: ora.Ora;
|
||||
timeouts: NodeJS.Timeout[] = [];
|
||||
initial: number = Date.now();
|
||||
lastMessage: string;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link DelayedSpinner} instance.
|
||||
*
|
||||
* @param message The message to display in the spinner
|
||||
* @param ms The number of milliseconds to wait before creating the spinner
|
||||
* @param opts The options for the spinner
|
||||
*/
|
||||
constructor(message: string, ms: number = 500) {
|
||||
constructor(message: string, opts?: DelayedSpinnerOptions) {
|
||||
opts = normalizeDelayedSpinnerOpts(opts);
|
||||
const delay = SHOULD_SHOW_SPINNERS ? opts.delay : opts.ciDelay;
|
||||
|
||||
this.timeouts.push(
|
||||
setTimeout(() => {
|
||||
if (!SHOULD_SHOW_SPINNERS) {
|
||||
console.warn(message);
|
||||
} else {
|
||||
this.spinner = ora(message);
|
||||
}, ms).unref()
|
||||
}
|
||||
this.lastMessage = message;
|
||||
}, delay).unref()
|
||||
);
|
||||
}
|
||||
|
||||
@ -32,7 +46,12 @@ export class DelayedSpinner {
|
||||
* @returns The {@link DelayedSpinner} instance
|
||||
*/
|
||||
setMessage(message: string) {
|
||||
if (this.spinner && SHOULD_SHOW_SPINNERS) {
|
||||
this.spinner.text = message;
|
||||
} else if (this.lastMessage && this.lastMessage !== message) {
|
||||
console.warn(message);
|
||||
this.lastMessage = message;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -40,15 +59,18 @@ export class DelayedSpinner {
|
||||
* Schedules an update to the message of the spinner. Useful for
|
||||
* changing the message after a certain amount of time has passed.
|
||||
*
|
||||
* @param message The message to display in the spinner
|
||||
* @param delay How long to wait before updating the message
|
||||
* @param opts The options for the update
|
||||
* @returns The {@link DelayedSpinner} instance
|
||||
*/
|
||||
scheduleMessageUpdate(message: string, delay: number) {
|
||||
scheduleMessageUpdate(message: string, opts?: DelayedSpinnerOptions) {
|
||||
opts = normalizeDelayedSpinnerOpts(opts);
|
||||
this.timeouts.push(
|
||||
setTimeout(() => {
|
||||
this.spinner.text = message;
|
||||
}, delay).unref()
|
||||
setTimeout(
|
||||
() => {
|
||||
this.setMessage(message);
|
||||
},
|
||||
SHOULD_SHOW_SPINNERS ? opts.delay : opts.ciDelay
|
||||
).unref()
|
||||
);
|
||||
return this;
|
||||
}
|
||||
@ -62,4 +84,13 @@ export class DelayedSpinner {
|
||||
}
|
||||
}
|
||||
|
||||
export const SHOULD_SHOW_SPINNERS = process.stdout.isTTY;
|
||||
const SHOULD_SHOW_SPINNERS = process.stdout.isTTY && !isCI();
|
||||
|
||||
function normalizeDelayedSpinnerOpts(
|
||||
opts: DelayedSpinnerOptions | null | undefined
|
||||
) {
|
||||
opts ??= {};
|
||||
opts.delay ??= 500;
|
||||
opts.ciDelay ??= 10_000;
|
||||
return opts;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user