scripts-orchestrator 1.2.1 → 1.2.2

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
@@ -56,6 +56,144 @@ Create a configuration file (default: `scripts-orchestrator.config.js`) that def
56
56
  }
57
57
  ```
58
58
 
59
+ ## Example Configurations
60
+
61
+ Here are some practical examples of how to configure the orchestrator for different scenarios:
62
+
63
+ ### Basic Build and Test Pipeline
64
+ ```javascript
65
+ export default [
66
+ {
67
+ command: 'build',
68
+ description: 'Build the project',
69
+ status: 'enabled',
70
+ attempts: 1
71
+ },
72
+ {
73
+ command: 'test',
74
+ description: 'Run unit tests',
75
+ status: 'enabled',
76
+ attempts: 2,
77
+ should_retry: (output) => {
78
+ // Only retry if there are actual test failures
79
+ const testSummaryMatch = output.match(/Test Suites:.*?(\d+) failed/);
80
+ return testSummaryMatch && parseInt(testSummaryMatch[1]) > 0;
81
+ }
82
+ },
83
+ {
84
+ command: 'lint',
85
+ description: 'Run lint checks',
86
+ status: 'enabled'
87
+ }
88
+ ];
89
+ ```
90
+
91
+ ### Storybook Testing with Background Process
92
+ ```javascript
93
+ export default [
94
+ {
95
+ command: 'test-storybook',
96
+ description: 'Run Storybook tests',
97
+ status: 'enabled',
98
+ attempts: 2,
99
+ dependencies: [
100
+ {
101
+ command: 'storybook_silent',
102
+ background: true,
103
+ wait: 5000,
104
+ health_check: {
105
+ url: 'http://localhost:6006',
106
+ max_attempts: 20,
107
+ interval: 2000
108
+ }
109
+ }
110
+ ]
111
+ }
112
+ ];
113
+ ```
114
+
115
+ ### Playwright Testing with Development Server
116
+ ```javascript
117
+ export default [
118
+ {
119
+ command: 'playwright_ci',
120
+ description: 'Run Playwright tests',
121
+ status: 'enabled',
122
+ attempts: 1,
123
+ dependencies: [
124
+ {
125
+ command: 'dev',
126
+ background: true,
127
+ health_check: {
128
+ url: 'http://localhost:5173',
129
+ max_attempts: 20,
130
+ interval: 2000
131
+ }
132
+ }
133
+ ]
134
+ }
135
+ ];
136
+ ```
137
+
138
+ ### Full CI Pipeline with Multiple Checks
139
+ ```javascript
140
+ export default [
141
+ {
142
+ command: 'build',
143
+ description: 'Build the project',
144
+ status: 'enabled',
145
+ attempts: 1
146
+ },
147
+ {
148
+ command: 'test-ci',
149
+ description: 'Run unit tests',
150
+ status: 'enabled',
151
+ attempts: 2,
152
+ should_retry: (output) => {
153
+ const testSummaryMatch = output.match(/Test Suites:.*?(\d+) failed/);
154
+ const hasTestFailures = testSummaryMatch && parseInt(testSummaryMatch[1]) > 0;
155
+ const hasCoverageFailures = output.match(/Jest: "global" coverage threshold/);
156
+
157
+ // Only retry for actual test failures, not coverage issues
158
+ return hasTestFailures;
159
+ }
160
+ },
161
+ {
162
+ command: 'test-storybook',
163
+ description: 'Run Storybook tests',
164
+ status: 'enabled',
165
+ attempts: 2,
166
+ dependencies: [
167
+ {
168
+ command: 'storybook_silent',
169
+ background: true,
170
+ wait: 5000,
171
+ health_check: {
172
+ url: 'http://localhost:6006',
173
+ max_attempts: 20,
174
+ interval: 2000
175
+ }
176
+ }
177
+ ]
178
+ },
179
+ {
180
+ command: 'stylelint',
181
+ description: 'Run stylelint checks',
182
+ status: 'enabled'
183
+ },
184
+ {
185
+ command: 'lint',
186
+ description: 'Run lint checks',
187
+ status: 'enabled'
188
+ },
189
+ {
190
+ command: 'jscpd',
191
+ description: 'Run code duplication checks',
192
+ status: 'enabled'
193
+ }
194
+ ];
195
+ ```
196
+
59
197
  ## Command Types
60
198
 
61
199
  The orchestrator is completely agnostic to what commands it runs. It can execute any npm scripts. Common use cases include:
@@ -10,6 +10,18 @@ export class Orchestrator {
10
10
  this.logger = log;
11
11
  this.failedCommands = [];
12
12
  this.skippedCommands = [];
13
+ this.commandTimings = new Map();
14
+ }
15
+
16
+ formatDuration(ms) {
17
+ if (ms < 1000) return `${ms}ms`;
18
+ const seconds = Math.floor(ms / 1000);
19
+ const minutes = Math.floor(seconds / 60);
20
+ const remainingSeconds = seconds % 60;
21
+ if (minutes > 0) {
22
+ return `${minutes}m ${remainingSeconds}s`;
23
+ }
24
+ return `${seconds}s`;
13
25
  }
14
26
 
15
27
  async executeCommand(commandConfig, visited = new Set()) {
@@ -26,12 +38,15 @@ export class Orchestrator {
26
38
  health_check,
27
39
  } = commandConfig;
28
40
 
41
+ const startTime = Date.now();
42
+
29
43
  // Check for circular dependencies
30
44
  if (visited.has(command)) {
31
45
  this.logger.error(
32
46
  `Circular dependency detected: ${Array.from(visited).join(' -> ')} -> ${command}`,
33
47
  );
34
48
  this.failedCommands.push(command);
49
+ this.commandTimings.set(command, Date.now() - startTime);
35
50
  return false;
36
51
  }
37
52
  visited.add(command);
@@ -40,6 +55,7 @@ export class Orchestrator {
40
55
  if (status === 'disabled') {
41
56
  this.logger.warn(`Skipping: npm run ${command} (status: disabled)`);
42
57
  this.skippedCommands.push(command);
58
+ this.commandTimings.set(command, Date.now() - startTime);
43
59
  visited.delete(command);
44
60
  return true;
45
61
  }
@@ -56,6 +72,7 @@ export class Orchestrator {
56
72
  startedByScript: false,
57
73
  process_tracking,
58
74
  });
75
+ this.commandTimings.set(command, Date.now() - startTime);
59
76
  visited.delete(command);
60
77
  return true;
61
78
  }
@@ -67,6 +84,7 @@ export class Orchestrator {
67
84
  if (!dependencySuccess) {
68
85
  this.logger.error(`Skipping ${command} due to failed dependency`);
69
86
  this.skippedCommands.push(command);
87
+ this.commandTimings.set(command, Date.now() - startTime);
70
88
  visited.delete(command);
71
89
  return false;
72
90
  }
@@ -83,6 +101,7 @@ export class Orchestrator {
83
101
  `URL ${dependency.health_check.url} is not available after maximum attempts`,
84
102
  );
85
103
  this.skippedCommands.push(command);
104
+ this.commandTimings.set(command, Date.now() - startTime);
86
105
  visited.delete(command);
87
106
  return false;
88
107
  }
@@ -124,12 +143,16 @@ export class Orchestrator {
124
143
  this.logger.warn(
125
144
  `${command} failed but doesn't meet retry criteria. Skipping retry.`,
126
145
  );
146
+ this.failedCommands.push(command);
127
147
  break;
128
148
  }
129
149
  this.logger.error(`Attempt ${attempt}/${attempts} failed for ${command}`);
150
+ } else {
151
+ this.failedCommands.push(command);
130
152
  }
131
153
  }
132
154
 
155
+ this.commandTimings.set(command, Date.now() - startTime);
133
156
  visited.delete(command);
134
157
  return result;
135
158
  }
@@ -137,12 +160,15 @@ export class Orchestrator {
137
160
  summarizeResults() {
138
161
  this.logger.info('\nCommand Summary:');
139
162
  this.config.forEach(({ command }) => {
163
+ const duration = this.commandTimings.get(command);
164
+ const durationStr = duration ? ` (${this.formatDuration(duration)})` : '';
165
+
140
166
  if (this.failedCommands.includes(command)) {
141
- this.logger.error(`- ${command}: (See logs/scripts-orchestrator_${command}.log)`);
167
+ this.logger.error(`- ${command}: ❌${durationStr} (See logs/scripts-orchestrator_${command}.log)`);
142
168
  } else if (this.skippedCommands.includes(command)) {
143
- this.logger.warn(`- ${command}: ⚠️ (Skipped due to failed dependency)`);
169
+ this.logger.warn(`- ${command}: ⚠️${durationStr} (Skipped due to failed dependency)`);
144
170
  } else {
145
- this.logger.success(`- ${command}: ✅`);
171
+ this.logger.success(`- ${command}: ✅${durationStr}`);
146
172
  }
147
173
  });
148
174
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scripts-orchestrator",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
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",