kist
Version:
Package Pipeline Processor
122 lines (101 loc) • 4.46 kB
text/typescript
// ============================================================================
// Import
// ============================================================================
import { ConfigInterface } from "../../interface/ConfigInterface";
import { AbstractProcess } from "../abstract/AbstractProcess";
import { Stage } from "./Stage";
// ============================================================================
// Class
// ============================================================================
/**
* Represents the pipeline of stages to be executed.
* This class manages the execution flow of stages, including parallel
* execution, dependency handling, and applying global options for consistent
* pipeline behavior.
*/
export class Pipeline extends AbstractProcess {
// Parameters
// ========================================================================
/**
* List of stages to be executed in the pipeline.
*/
private stages: Stage[];
/**
* Global options that apply across the entire pipeline.
*/
private options?: ConfigInterface["options"];
// Constructor
// ========================================================================
/**
* Constructs a new Pipeline instance with the given configuration.
* Initializes stages and applies global options for execution control.
*
* @param config - The configuration object defining the stages, steps,
* and global options for the pipeline.
*/
constructor(private config: ConfigInterface) {
super();
this.stages = config.stages.map((stage) => new Stage(stage));
this.options = config.options;
this.logInfo("Pipeline instance created.");
}
// Methods
// ========================================================================
/**
* Runs the pipeline, executing stages based on their dependencies.
* Stages are run in parallel by default, but their execution respects
* defined dependencies. Applies global options for logging, error
* handling, and execution control.
*/
async run(): Promise<void> {
this.logInfo("Starting pipeline execution...");
// Track stages that have been completed
const completedStages = new Set<string>();
// Run stages with dependency management and parallel execution control
try {
this.logDebug("Pipeline execution started with debug logging.");
// Execute all stages with concurrency control
const stagePromises = this.stages.map((stage) =>
stage.execute(completedStages),
);
await this.runWithConcurrencyControl(stagePromises);
this.logInfo("Pipeline execution completed successfully.");
} catch (error) {
this.logError("Pipeline execution failed:", error);
// Halt pipeline if configured to do so on failure
if (this.options?.haltOnFailure !== false) {
this.logError("Halting pipeline due to failure.");
process.exit(1);
} else {
this.logWarn("Continuing pipeline execution despite errors.");
}
}
}
/**
* Runs the stage promises with concurrency control based on global
* options. Limits the number of parallel running stages if
* maxConcurrentStages is set in global options.
* @param stagePromises - An array of promises representing stage
* executions.
*/
private async runWithConcurrencyControl(
stagePromises: Promise<void>[],
): Promise<void> {
const maxConcurrentStages =
this.options?.maxConcurrentStages || stagePromises.length;
// Process stages with a concurrency limit
const executingStages = new Set<Promise<void>>();
for (const stagePromise of stagePromises) {
executingStages.add(stagePromise);
// Ensure stages are removed from the set once complete
stagePromise.finally(() => executingStages.delete(stagePromise));
// Enforce concurrency limit
if (executingStages.size >= (maxConcurrentStages as number)) {
// Wait until at least one stage completes
await Promise.race(executingStages);
}
}
// Wait for all remaining stages to complete
await Promise.all(executingStages);
}
}