UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

264 lines (263 loc) 12.5 kB
import { NxJsonConfiguration } from '../config/nx-json'; import { ProjectGraph } from '../config/project-graph'; import { Task, TaskGraph } from '../config/task-graph'; import { DaemonClient } from '../daemon/client/client'; import { TaskHasher } from '../hasher/task-hasher'; import { NxArgs } from '../utils/command-line-utils'; import { DefaultTasksRunnerOptions } from './default-tasks-runner'; import { TaskResult } from './life-cycle'; import { RunningTask } from './running-tasks/running-task'; import { SharedRunningTask } from './running-tasks/shared-running-task'; import { TaskStatus } from './tasks-runner'; import { Batch } from './tasks-schedule'; export declare class TaskOrchestrator { private readonly hasher; private readonly initiatingProject; private readonly initiatingTasks; private readonly projectGraph; private readonly taskGraph; private readonly nxJson; private readonly options; private readonly bail; private readonly daemon; private readonly outputStyle; private readonly fullTaskGraph; private taskDetails; private cache; private readonly tuiEnabled; private readonly projects; private forkedProcessTaskRunner; private runningTasksService; private taskInvocationTracker; private registeredInvocations; private tasksSchedule; private batchEnv; private reverseTaskDeps; private initializingTaskIds; private processedTasks; private cacheMissedHashes; private completedTasks; private waitingForTasks; private pendingDiscreteWorkers; private groups; private continuousTasksStarted; /** * How many folds each batch id has rendered. A batch that reports a strict * subset of its tasks is re-run under the same id, so one id can produce more * than one fold and the redirect lines have to point at the right one. */ private batchFoldRenders; private bailed; private resolveStopPromise; private stopRequested; private runningContinuousTasks; private runningRunCommandsTasks; private runningDiscreteTasks; private discreteTaskExitHandled; private continuousTaskExitHandled; private cleanupPromise; private signalHandlers; constructor(hasher: TaskHasher, initiatingProject: string | undefined, initiatingTasks: Task[], projectGraph: ProjectGraph, taskGraph: TaskGraph, nxJson: NxJsonConfiguration, options: NxArgs & DefaultTasksRunnerOptions, bail: boolean, daemon: DaemonClient, outputStyle: string, fullTaskGraph?: TaskGraph); init(): Promise<void>; run(): Promise<{ [k: string]: TaskStatus; }>; nextBatch(): Batch; /** * Coordinator loop. All batch operations (hashing, cache resolution) * happen on this single thread — no races. Cache misses are dispatched * as fire-and-forget workers. Workers signal completion via * scheduleNextTasksAndReleaseThreads which wakes all waiting loops. * * Safety: the dispatch phase (step 5) is fully synchronous — no * worker can run during it. So all tasks picked up by nextTask() * are guaranteed to be in processedTasks from step 1. */ private executeCoordinatorLoop; private executeContinuousTaskLoop; private processTask; processAllScheduledTasks(): void; /** * Registers a task invocation and checks for loops across nested Nx processes. * Uses the task_invocations DB table keyed by root PID. registerTask() throws * on unique constraint violation when a parent Nx process already registered * this task — indicating an infinite loop. */ private detectTaskInvocationLoop; private applyCachedResults; /** * Batch cache lookup + filter to successful entries. Handles both * local (one rarray SQL call) and remote (parallel HTTP retrievals) * inside DbCache.getBatch. */ private fetchCacheHits; /** * For each confirmed cache hit: decide whether to copy outputs from * the cache (skipping if the on-disk outputs already match the * recorded hash), copy in parallel, derive the task status, print * terminal output, and return the assembled results. */ private finalizeCacheHits; /** * Coordinator wrapper around {@link resolveCachedTasks}: peeks at * scheduledTasks (without removing anything from the schedule), * filters to cacheable hashed discrete candidates, and delegates the * cache fetch + lifecycle to the public method. Returns true if any * tasks were resolved. * * The coordinator relies on this running unconditionally (when cache * is enabled): tasks dispatched in step 5 via runTaskDirectly skip * their own cache lookup on the assumption that this has already * confirmed them as misses. Excluding cacheMissedHashes preserves that * invariant — every dispatched hash was queried exactly once — but * don't add other length-based bails. */ private resolveCachedTasksBulk; /** * Hash all batch tasks and resolve cache hits topologically. * * Walks the task graph level by level. Every task gets a preliminary hash * (so startTasks always has a valid hash for Cloud). Tasks with depsOutputs * whose deps weren't cached are ineligible for cache lookup but still * receive a preliminary hash — they'll be re-hashed after execution. */ private applyBatchCachedResults; private hashBatchTasks; applyFromCacheOrRunBatch(doNotSkipCache: boolean, batch: Batch, groupId: number): Promise<TaskResult[]>; private runBatch; /** * Rendering a batch's output must never change the batch's results. A throw * from the printer would otherwise land in `runBatch`'s own error handling: * on the resolved path it rewrites every task to `failure` with the printer's * stack as its output — reporting a green build red to the life cycles and Nx * Cloud — and on the crash path it escapes `runBatch`, replacing the built * failure results with the printer's error. Both call sites degrade to a * warning here instead. */ private renderBatchOutputSafely; /** * Prints a completed batch's output once, under log grouping. Live forwarding * was suppressed while grouping, so this is the only copy — which is why the * requested output style has to reach this path rather than stopping at the * life cycle. * * Two things are rendered, and they answer different questions. * * Every task always renders through the life cycle, exactly as in a non-batch * run - failures in full, successes collapsed to a line for run-many, plus the * initiating project in full for run-one. That is what attributes output to a * task, and it is the only place some of it exists: `@nx/jest` synthesizes * each task's `terminalOutput` from an aggregated result and never writes * those per-project summaries to the worker's stdio at all. * * The worker's whole captured log is rendered as a fold above them when the * run asked for full output, or when any task failed or was stopped. A * diagnostic that explains a failure is routinely one no task claimed: * `@nx/maven`'s batch impl writes its exit-code dump and failed-task outputs * to the worker's stderr via `console.error`, and the Maven JVM it spawns * points slf4j at `System.out` because its own stderr carries the result * protocol - two layers, two streams, both captured and neither attributed to * a task - and `@nx/gradle` emits configuration-phase errors before the first * `> Task :x:y` header tells it which task to attribute to. Both catch their * own crash and backfill task results, so the batch resolves and lands here * rather than in the caller's failure path. * * Rendering both duplicates some bytes, deliberately. `@nx/maven` and * `@nx/gradle` tee each task's output into the worker's stdio on the way to * `terminalOutput`, so a failing task's body appears in the fold and again in * its own block. That is bounded on the default style, where successes * collapse to a line each and a crashed batch backfills a short * `e.toString()` rather than a body, so the case with the largest fold * duplicates the least. Under a full-output style it is not bounded: every * task prints in full beside a log that already contains it, which is the * price of that style asking for everything. What it buys either way is * attribution the fold cannot express. The * alternative, letting the fold replace per-task rendering, silently dropped * `@nx/jest`'s summaries and is what this shape exists to avoid. * * A batch that never reported results is handled by the caller instead. */ private printGroupedBatchOutput; /** * Renders a batch's whole output as one fold, plus — unless `redirectLines` * is off — a line per task pointing at it. The fold is labelled with the * executor and a run-unique id (the same * executor can run more than one batch), rather than an arbitrary task. Safe * to write to `output` directly: grouping implies GitHub Actions implies a * non-TTY, static lifecycle. */ private printBatchFold; /** * Bulk-resolve cache hits for a set of tasks: fetch cached entries, * copy outputs as needed, fire lifecycle, and return the TaskResults * for the hits. Tasks that aren't in the cache (or aren't cacheable) * are silently omitted from the return value — callers are responsible * for running those via {@link runTaskDirectly}. * * Fires scheduleTask lifecycle for hits that haven't been through * processAllScheduledTasks yet. That's a coordinator gap-filler and * a no-op for callers that pre-process the schedule. * * The caller provides `groupId` — cache hits share one slot since they * don't actually compete for parallelism. */ resolveCachedTasks(doNotSkipCache: boolean, tasks: Task[], groupId: number): Promise<TaskResult[]>; /** * Fire a discrete-task worker and track it in pendingDiscreteWorkers until * it settles. Uses runTaskDirectly (not applyFromCacheOrRun*) because * resolveCachedTasksBulk already confirmed this task is a cache miss — * another lookup would re-query the DB and (for Nx Cloud users) repeat * the remote HTTP retrieval. */ private dispatchDiscreteWorker; /** * Route a worker rejection (e.g. remote cache errors) through the normal * failure path instead of letting it become an unhandled promise. Guard * against double-finalize: completeTasks() populates `completedTasks`, * so a rejection arriving after postRunSteps has already finalized the * task must not run postRunSteps again. */ private handleDiscreteWorkerFailure; /** * Spawn and wait on a task's child process, unconditionally — no cache * lookup. Callers must have already confirmed the task is a cache miss * (or disabled caching entirely). */ runTaskDirectly(doNotSkipCache: boolean, task: Task, groupId: number): Promise<TaskResult>; private runTask; private runTaskInForkedProcess; startContinuousTask(task: Task, groupId: number): Promise<RunningTask | SharedRunningTask>; private preRunSteps; private postRunSteps; private scheduleNextTasksAndReleaseThreads; private complete; /** * Unified task completion handler for a set of tasks. * - Calls endTasks() lifecycle hook (non-skipped only) * - Marks complete in scheduler * - Sets completedTasks * - Updates TUI status * - Skip dependent tasks */ private completeTasks; private pipeOutputCapture; private shouldCacheTaskResult; private closeGroup; private openGroup; private shouldCopyOutputsFromCacheBatch; private recordOutputsHashBatch; private handleContinuousTaskExit; private isContinuousTaskNeeded; private completeContinuousTask; private cleanup; private performCleanup; private setupSignalHandlers; waitForContinuousTaskExit(taskId: string): Promise<void>; dispose(): Promise<void>; private cleanUpUnneededContinuousTasks; } export declare function getThreadPoolSize(options: NxArgs & DefaultTasksRunnerOptions, taskGraph: TaskGraph): { discrete: number; continuous: number; total: number; };