scripts-orchestrator 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,6 +26,7 @@ npm install --save-dev scripts-orchestrator
26
26
  ## Features
27
27
 
28
28
  - **Parallel Execution**: Runs multiple commands concurrently for faster execution
29
+ - **Sequential Mode**: Option to run all commands sequentially for low CPU machines
29
30
  - **Dependency Management**: Handles command dependencies and ensures proper execution order
30
31
  - **Background Processes**: Supports running commands in the background with health checks
31
32
  - **Retry Mechanism**: Configurable retry attempts for failed commands
@@ -256,6 +257,15 @@ The orchestrator doesn't care what the commands do - it just ensures they run (i
256
257
 
257
258
  # Run with verbose logging
258
259
  npm run scripts-orchestrator -- --verbose
260
+
261
+ # Run in sequential mode (for low CPU machines)
262
+ npm run scripts-orchestrator -- --sequential
263
+
264
+ # Specify a custom log folder
265
+ npm run scripts-orchestrator -- --logFolder ./custom-logs
266
+
267
+ # Force execution even if git state is unchanged
268
+ npm run scripts-orchestrator -- --force
259
269
  ```
260
270
 
261
271
  ### Starting from a Specific Phase
@@ -338,6 +348,29 @@ npm run scripts-orchestrator -- --phases "build,test,optional-e2e,optional-perfo
338
348
  - The orchestrator validates that all specified phases exist
339
349
  - Commands in skipped optional phases are marked as "skipped" in the final summary
340
350
 
351
+ ### Sequential Mode
352
+
353
+ By default, the orchestrator runs commands within each phase in parallel for optimal performance. However, you can use the `--sequential` flag to run all commands sequentially, which is useful for low CPU machines or when you need to reduce resource consumption.
354
+
355
+ #### Usage
356
+ ```bash
357
+ # Run all commands sequentially instead of in parallel
358
+ npm run scripts-orchestrator -- --sequential
359
+ ```
360
+
361
+ When running in sequential mode:
362
+ - Commands within each phase are executed one at a time
363
+ - Phases still run sequentially (as they always do)
364
+ - If a command fails, the remaining commands in that phase are skipped
365
+ - Lower CPU and memory usage compared to parallel execution
366
+ - Longer total execution time
367
+
368
+ This is particularly useful for:
369
+ - CI/CD environments with limited resources
370
+ - Development machines with low CPU/memory
371
+ - Debugging individual command failures
372
+ - Avoiding resource contention between commands
373
+
341
374
  ## Error Handling
342
375
 
343
376
  - The script tracks failed and skipped commands
@@ -348,10 +381,35 @@ npm run scripts-orchestrator -- --phases "build,test,optional-e2e,optional-perfo
348
381
  ## Logging
349
382
 
350
383
  - Each command's output is logged to `scripts-orchestrator-logs/<command>.log` in the current working directory
384
+ - Main orchestrator logs are saved to `scripts-orchestrator-logs/orchestrator-main-<timestamp>.log`
351
385
  - Git commit hash is cached in `scripts-orchestrator-logs/.git-hash-cache` for skip detection
352
386
  - Provides real-time status updates during execution
353
387
  - Summarizes results at the end of execution
354
388
 
389
+ ### Custom Log Folder
390
+
391
+ You can customize the log folder location using either the command line or configuration file:
392
+
393
+ #### Method 1: Command Line Argument
394
+ ```bash
395
+ # Use a custom log folder
396
+ npm run scripts-orchestrator -- --logFolder ./my-custom-logs
397
+ ```
398
+
399
+ #### Method 2: Configuration File
400
+ ```javascript
401
+ export default {
402
+ log_folder: './my-custom-logs', // Custom log folder
403
+ phases: [
404
+ // ... your phases
405
+ ]
406
+ };
407
+ ```
408
+
409
+ **Note**: Command line arguments take precedence over configuration file settings.
410
+
411
+ All logs (command logs, main orchestrator logs, and git cache) will be stored in the specified folder.
412
+
355
413
  ## Git-Based Caching
356
414
 
357
415
  The orchestrator automatically tracks the git commit hash and repository state to optimize execution:
@@ -367,6 +425,21 @@ This feature is particularly useful in CI/CD pipelines where the same commit mig
367
425
 
368
426
  **Note**: The cache is only updated on successful execution. Failed runs will not update the cache, ensuring subsequent runs will retry.
369
427
 
428
+ ### Force Execution
429
+
430
+ You can bypass the git cache check and force execution even when the git state is unchanged by using the `--force` flag:
431
+
432
+ ```bash
433
+ # Force execution regardless of git state
434
+ npm run scripts-orchestrator -- --force
435
+ ```
436
+
437
+ This is useful when you want to:
438
+ - Re-run commands without making code changes
439
+ - Test configuration changes
440
+ - Debug issues without modifying the codebase
441
+ - Override the cache in CI/CD pipelines
442
+
370
443
  ## Exit Codes
371
444
 
372
445
  - `0`: All commands executed successfully
package/index.js CHANGED
@@ -31,6 +31,14 @@ const argv = yargs(hideBin(process.argv))
31
31
  type: 'string',
32
32
  description: 'Specify the directory for log files',
33
33
  })
34
+ .option('sequential', {
35
+ type: 'boolean',
36
+ description: 'Run all commands sequentially instead of in parallel (for low CPU machines)',
37
+ })
38
+ .option('force', {
39
+ type: 'boolean',
40
+ description: 'Force execution even if git state is unchanged',
41
+ })
34
42
  .help()
35
43
  .alias('h', 'help')
36
44
  .parse();
@@ -41,6 +49,8 @@ const configPath = args[0] || './scripts-orchestrator.config.js';
41
49
  let startPhase = argv.phase;
42
50
  let logFolder = argv.logFolder;
43
51
  const phases = argv.phases ? argv.phases.split(',').map(p => p.trim()) : null;
52
+ const sequential = argv.sequential || false;
53
+ const force = argv.force || false;
44
54
 
45
55
  // Validate config file exists
46
56
  if (!fs.existsSync(configPath)) {
@@ -64,8 +74,13 @@ if (!logFolder && commandsConfig.log_folder) {
64
74
  logFolder = commandsConfig.log_folder;
65
75
  }
66
76
 
77
+ // Set the log folder for the main orchestrator logs if specified
78
+ if (logFolder) {
79
+ log.setLogFolder(logFolder);
80
+ }
81
+
67
82
  // Create and run the orchestrator
68
- const orchestrator = new Orchestrator(commandsConfig, startPhase, logFolder, phases);
83
+ const orchestrator = new Orchestrator(commandsConfig, startPhase, logFolder, phases, sequential, force);
69
84
 
70
85
  // Enhanced signal handlers
71
86
  const handleSignal = async (signal) => {
package/lib/logger.js CHANGED
@@ -66,6 +66,19 @@ class Logger {
66
66
  }
67
67
  }
68
68
 
69
+ setLogFolder(newLogFolder) {
70
+ // Close existing stream if it exists
71
+ if (this.logStream) {
72
+ this.logStream.end();
73
+ }
74
+
75
+ // Update log folder
76
+ this.logFolder = newLogFolder;
77
+
78
+ // Reinitialize with new folder
79
+ this.initializeLogFile();
80
+ }
81
+
69
82
  writeToFile(message) {
70
83
  if (this.logStream) {
71
84
  // Strip ANSI color codes for file output
@@ -4,11 +4,13 @@ import { log } from './logger.js';
4
4
  import { GitCache } from './git-cache.js';
5
5
 
6
6
  export class Orchestrator {
7
- constructor(config, startPhase = null, logFolder = null, phases = null) {
7
+ constructor(config, startPhase = null, logFolder = null, phases = null, sequential = false, force = false) {
8
8
  this.config = config;
9
9
  this.startPhase = startPhase;
10
10
  this.logFolder = logFolder;
11
11
  this.phases = phases;
12
+ this.sequential = sequential;
13
+ this.force = force;
12
14
  this.processManager = processManager;
13
15
  this.healthCheck = healthCheck;
14
16
  this.logger = log;
@@ -239,11 +241,16 @@ export class Orchestrator {
239
241
 
240
242
  async run() {
241
243
  try {
242
- // Check if we should skip execution based on git state
243
- const shouldSkip = await this.gitCache.shouldSkipExecution();
244
- if (shouldSkip) {
245
- this.logger.success('šŸŽ‰ No changes detected, skipping execution!');
246
- process.exit(0);
244
+ // Check if we should skip execution based on git state (unless forced)
245
+ if (!this.force) {
246
+ const shouldSkip = await this.gitCache.shouldSkipExecution();
247
+ if (shouldSkip) {
248
+ this.logger.success('šŸŽ‰ No changes detected, skipping execution!');
249
+ this.logger.info('šŸ’” To force execution, use: --force');
250
+ process.exit(0);
251
+ }
252
+ } else {
253
+ this.logger.info('⚔ Force execution enabled, skipping git cache check');
247
254
  }
248
255
 
249
256
  let hasFailures = false;
@@ -252,14 +259,31 @@ export class Orchestrator {
252
259
 
253
260
  // Handle both old array format and new phases format
254
261
  if (Array.isArray(this.config)) {
255
- // Legacy: Run all commands in parallel
256
- const tasks = this.config.map((commandConfig) =>
257
- this.executeCommand(commandConfig),
258
- );
259
- const results = await Promise.all(tasks);
260
- hasFailures = results.some(result => !result);
262
+ // Legacy: Run all commands in parallel or sequential based on flag
263
+ if (this.sequential) {
264
+ this.logger.info('šŸ”„ Running in sequential mode');
265
+ const results = [];
266
+ for (const commandConfig of this.config) {
267
+ const result = await this.executeCommand(commandConfig);
268
+ results.push(result);
269
+ if (!result) {
270
+ hasFailures = true;
271
+ break; // Stop on first failure in sequential mode
272
+ }
273
+ }
274
+ } else {
275
+ const tasks = this.config.map((commandConfig) =>
276
+ this.executeCommand(commandConfig),
277
+ );
278
+ const results = await Promise.all(tasks);
279
+ hasFailures = results.some(result => !result);
280
+ }
261
281
  } else if (this.config.phases) {
262
- // New: Run phases sequentially, commands within phases in parallel
282
+ // New: Run phases sequentially, commands within phases in parallel or sequential based on flag
283
+ if (this.sequential) {
284
+ this.logger.info('šŸ”„ Running in sequential mode');
285
+ }
286
+
263
287
  for (const phase of this.config.phases) {
264
288
  // Check if we should start from this phase
265
289
  if (this.startPhase && !startPhaseFound) {
@@ -298,11 +322,26 @@ export class Orchestrator {
298
322
 
299
323
  this.logger.info(`\nšŸ”„ Starting phase: ${phase.name}`);
300
324
 
301
- const tasks = phase.parallel.map((commandConfig) =>
302
- this.executeCommand(commandConfig),
303
- );
325
+ let results;
326
+ if (this.sequential) {
327
+ // Run commands sequentially
328
+ results = [];
329
+ for (const commandConfig of phase.parallel) {
330
+ const result = await this.executeCommand(commandConfig);
331
+ results.push(result);
332
+ if (!result) {
333
+ // In sequential mode, stop phase execution on first failure
334
+ break;
335
+ }
336
+ }
337
+ } else {
338
+ // Run commands in parallel
339
+ const tasks = phase.parallel.map((commandConfig) =>
340
+ this.executeCommand(commandConfig),
341
+ );
342
+ results = await Promise.all(tasks);
343
+ }
304
344
 
305
- const results = await Promise.all(tasks);
306
345
  const phaseHasFailures = results.some(result => !result);
307
346
 
308
347
  if (phaseHasFailures) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scripts-orchestrator",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "description": "A powerful script orchestrator for running parallel commands with dependency management, background processes, and health checks",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",