@eldrforge/kodrdriv 1.2.29 → 1.2.123
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/dist/application.js +16 -13
- package/dist/application.js.map +1 -1
- package/dist/arguments.js +5 -5
- package/dist/arguments.js.map +1 -1
- package/dist/commands/audio-review.js +2 -5
- package/dist/commands/audio-review.js.map +1 -1
- package/dist/commands/clean.js +2 -4
- package/dist/commands/clean.js.map +1 -1
- package/dist/commands/commit.js +3 -6
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/development.js +7 -7
- package/dist/commands/development.js.map +1 -1
- package/dist/commands/link.js +3 -7
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/precommit.js +99 -0
- package/dist/commands/precommit.js.map +1 -0
- package/dist/commands/publish.js +47 -32
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/release.js +3 -7
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/review.js +4 -6
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/tree.js +213 -84
- package/dist/commands/tree.js.map +1 -1
- package/dist/commands/unlink.js +3 -7
- package/dist/commands/unlink.js.map +1 -1
- package/dist/commands/updates.js +2 -4
- package/dist/commands/updates.js.map +1 -1
- package/dist/commands/versions.js +3 -7
- package/dist/commands/versions.js.map +1 -1
- package/dist/constants.js +4 -2
- package/dist/constants.js.map +1 -1
- package/dist/content/files.js +2 -4
- package/dist/content/files.js.map +1 -1
- package/dist/execution/CommandValidator.js +33 -1
- package/dist/execution/CommandValidator.js.map +1 -1
- package/dist/execution/ResourceMonitor.js +26 -1
- package/dist/execution/ResourceMonitor.js.map +1 -1
- package/dist/execution/TreeExecutionAdapter.js +2 -2
- package/dist/execution/TreeExecutionAdapter.js.map +1 -1
- package/dist/util/checkpointManager.js +2 -4
- package/dist/util/checkpointManager.js.map +1 -1
- package/dist/util/dependencyGraph.js +2 -4
- package/dist/util/dependencyGraph.js.map +1 -1
- package/dist/util/general.js +7 -107
- package/dist/util/general.js.map +1 -1
- package/dist/util/gitMutex.js +63 -18
- package/dist/util/gitMutex.js.map +1 -1
- package/dist/util/precommitOptimizations.js +310 -0
- package/dist/util/precommitOptimizations.js.map +1 -0
- package/dist/util/storageAdapter.js +2 -6
- package/dist/util/storageAdapter.js.map +1 -1
- package/dist/utils/branchState.js +178 -45
- package/dist/utils/branchState.js.map +1 -1
- package/package.json +6 -5
- package/AI-FRIENDLY-LOGGING-GUIDE.md +0 -237
- package/AI-LOGGING-MIGRATION-COMPLETE.md +0 -371
- package/ALREADY-PUBLISHED-PACKAGES-FIX.md +0 -264
- package/AUDIT-BRANCHES-PROGRESS-FIX.md +0 -90
- package/AUDIT-EXAMPLE-OUTPUT.md +0 -113
- package/CHECKPOINT-RECOVERY-FIX.md +0 -450
- package/LOGGING-MIGRATION-STATUS.md +0 -186
- package/MONOREPO-PUBLISH-IMPROVEMENTS.md +0 -281
- package/PARALLEL-EXECUTION-FIXES.md +0 -132
- package/PARALLEL-PUBLISH-DEBUGGING-GUIDE.md +0 -441
- package/PARALLEL-PUBLISH-FIXES-IMPLEMENTED.md +0 -405
- package/PARALLEL-PUBLISH-IMPROVEMENTS-IMPLEMENTED.md +0 -439
- package/PARALLEL-PUBLISH-LOGGING-FIXES.md +0 -274
- package/PARALLEL-PUBLISH-QUICK-REFERENCE.md +0 -375
- package/PARALLEL_EXECUTION_FIX.md +0 -146
- package/PUBLISH_IMPROVEMENTS_IMPLEMENTED.md +0 -294
- package/RECOVERY-FIXES.md +0 -72
- package/SUBMODULE-LOCK-FIX.md +0 -132
- package/VERSION-AUDIT-FIX.md +0 -333
- package/WORKFLOW-PRECHECK-IMPLEMENTATION.md +0 -239
- package/WORKFLOW-SKIP-SUMMARY.md +0 -121
- package/dist/util/safety.js +0 -166
- package/dist/util/safety.js.map +0 -1
- package/dist/util/stdin.js +0 -133
- package/dist/util/stdin.js.map +0 -1
- package/dist/util/storage.js +0 -187
- package/dist/util/storage.js.map +0 -1
package/dist/commands/tree.js
CHANGED
|
@@ -5,15 +5,17 @@ import { exec } from 'child_process';
|
|
|
5
5
|
import { safeJsonParse, validatePackageJson, getGloballyLinkedPackages, getGitStatusSummary, getLinkedDependencies, getLinkCompatibilityProblems, runSecure, run } from '@eldrforge/git-tools';
|
|
6
6
|
import util from 'util';
|
|
7
7
|
import { getLogger } from '../logging.js';
|
|
8
|
-
import {
|
|
8
|
+
import { createStorage } from '@eldrforge/shared';
|
|
9
9
|
import { getOutputPath } from '../util/general.js';
|
|
10
10
|
import { DEFAULT_OUTPUT_DIRECTORY } from '../constants.js';
|
|
11
11
|
import { execute as execute$3 } from './commit.js';
|
|
12
12
|
import { execute as execute$1 } from './link.js';
|
|
13
13
|
import { execute as execute$2 } from './unlink.js';
|
|
14
14
|
import { execute as execute$4 } from './updates.js';
|
|
15
|
-
import { runGitWithLock } from '../util/gitMutex.js';
|
|
15
|
+
import { isInGitRepository, runGitWithLock } from '../util/gitMutex.js';
|
|
16
16
|
import { scanForPackageJsonFiles, buildDependencyGraph, topologicalSort, parsePackageJson, shouldExclude } from '../util/dependencyGraph.js';
|
|
17
|
+
import { optimizePrecommitCommand, recordTestRun } from '../util/precommitOptimizations.js';
|
|
18
|
+
import { PerformanceTimer } from '../util/performance.js';
|
|
17
19
|
import { SimpleMutex } from '../util/mutex.js';
|
|
18
20
|
|
|
19
21
|
// Global state to track published versions during tree execution - protected by mutex
|
|
@@ -22,9 +24,7 @@ let executionContext = null;
|
|
|
22
24
|
const globalStateMutex = new SimpleMutex();
|
|
23
25
|
// Update inter-project dependencies in package.json based on published versions
|
|
24
26
|
const updateInterProjectDependencies = async (packageDir, publishedVersions, allPackageNames, packageLogger, isDryRun)=>{
|
|
25
|
-
const storage =
|
|
26
|
-
log: packageLogger.info
|
|
27
|
-
});
|
|
27
|
+
const storage = createStorage();
|
|
28
28
|
const packageJsonPath = path__default.join(packageDir, 'package.json');
|
|
29
29
|
if (!await storage.exists(packageJsonPath)) {
|
|
30
30
|
packageLogger.verbose('No package.json found, skipping dependency updates');
|
|
@@ -83,9 +83,7 @@ const updateInterProjectDependencies = async (packageDir, publishedVersions, all
|
|
|
83
83
|
};
|
|
84
84
|
// Detect scoped dependencies from package.json and run updates for them
|
|
85
85
|
const updateScopedDependencies = async (packageDir, packageLogger, isDryRun, runConfig)=>{
|
|
86
|
-
const storage =
|
|
87
|
-
log: packageLogger.info
|
|
88
|
-
});
|
|
86
|
+
const storage = createStorage();
|
|
89
87
|
const packageJsonPath = path__default.join(packageDir, 'package.json');
|
|
90
88
|
if (!await storage.exists(packageJsonPath)) {
|
|
91
89
|
packageLogger.verbose('No package.json found, skipping scoped dependency updates');
|
|
@@ -167,9 +165,7 @@ const getContextFilePath = (outputDirectory)=>{
|
|
|
167
165
|
};
|
|
168
166
|
// Save execution context to file
|
|
169
167
|
const saveExecutionContext = async (context, outputDirectory)=>{
|
|
170
|
-
const storage =
|
|
171
|
-
log: ()=>{}
|
|
172
|
-
}); // Silent storage for context operations
|
|
168
|
+
const storage = createStorage(); // Silent storage for context operations
|
|
173
169
|
const contextFilePath = getContextFilePath(outputDirectory);
|
|
174
170
|
try {
|
|
175
171
|
// Ensure output directory exists
|
|
@@ -193,9 +189,7 @@ const saveExecutionContext = async (context, outputDirectory)=>{
|
|
|
193
189
|
};
|
|
194
190
|
// Load execution context from file
|
|
195
191
|
const loadExecutionContext = async (outputDirectory)=>{
|
|
196
|
-
const storage =
|
|
197
|
-
log: ()=>{}
|
|
198
|
-
}); // Silent storage for context operations
|
|
192
|
+
const storage = createStorage(); // Silent storage for context operations
|
|
199
193
|
const contextFilePath = getContextFilePath(outputDirectory);
|
|
200
194
|
try {
|
|
201
195
|
if (!await storage.exists(contextFilePath)) {
|
|
@@ -221,9 +215,7 @@ const loadExecutionContext = async (outputDirectory)=>{
|
|
|
221
215
|
};
|
|
222
216
|
// Clean up context file
|
|
223
217
|
const cleanupContext = async (outputDirectory)=>{
|
|
224
|
-
const storage =
|
|
225
|
-
log: ()=>{}
|
|
226
|
-
}); // Silent storage for context operations
|
|
218
|
+
const storage = createStorage(); // Silent storage for context operations
|
|
227
219
|
const contextFilePath = getContextFilePath(outputDirectory);
|
|
228
220
|
try {
|
|
229
221
|
if (await storage.exists(contextFilePath)) {
|
|
@@ -237,9 +229,7 @@ const cleanupContext = async (outputDirectory)=>{
|
|
|
237
229
|
};
|
|
238
230
|
// Helper function to promote a package to completed status in the context
|
|
239
231
|
const promotePackageToCompleted = async (packageName, outputDirectory)=>{
|
|
240
|
-
const storage =
|
|
241
|
-
log: ()=>{}
|
|
242
|
-
});
|
|
232
|
+
const storage = createStorage();
|
|
243
233
|
const contextFilePath = getContextFilePath(outputDirectory);
|
|
244
234
|
try {
|
|
245
235
|
if (!await storage.exists(contextFilePath)) {
|
|
@@ -272,9 +262,7 @@ const promotePackageToCompleted = async (packageName, outputDirectory)=>{
|
|
|
272
262
|
const validateScripts = async (packages, scripts)=>{
|
|
273
263
|
const logger = getLogger();
|
|
274
264
|
const missingScripts = new Map();
|
|
275
|
-
const storage =
|
|
276
|
-
log: ()=>{}
|
|
277
|
-
});
|
|
265
|
+
const storage = createStorage();
|
|
278
266
|
logger.debug(`Validating scripts: ${scripts.join(', ')}`);
|
|
279
267
|
for (const [packageName, packageInfo] of packages){
|
|
280
268
|
const packageJsonPath = path__default.join(packageInfo.path, 'package.json');
|
|
@@ -317,9 +305,7 @@ const validateScripts = async (packages, scripts)=>{
|
|
|
317
305
|
// After kodrdriv publish, the release version is captured in the git tag,
|
|
318
306
|
// while package.json contains the next dev version
|
|
319
307
|
const extractPublishedVersion = async (packageDir, packageLogger)=>{
|
|
320
|
-
const storage =
|
|
321
|
-
log: packageLogger.info
|
|
322
|
-
});
|
|
308
|
+
const storage = createStorage();
|
|
323
309
|
const packageJsonPath = path__default.join(packageDir, 'package.json');
|
|
324
310
|
try {
|
|
325
311
|
// Get package name from package.json
|
|
@@ -437,23 +423,31 @@ const runWithLogging = async (command, packageLogger, options = {}, showOutput =
|
|
|
437
423
|
stderr: String(result.stderr)
|
|
438
424
|
};
|
|
439
425
|
} catch (error) {
|
|
426
|
+
// Always show error message
|
|
427
|
+
packageLogger.error(`Command failed: ${command}`);
|
|
428
|
+
// Always show stderr on failure (contains important error details like coverage failures)
|
|
429
|
+
if (error.stderr && error.stderr.trim()) {
|
|
430
|
+
packageLogger.error(`❌ STDERR:`);
|
|
431
|
+
error.stderr.split('\n').forEach((line)=>{
|
|
432
|
+
if (line.trim()) packageLogger.error(`${line}`);
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
// Show stdout on failure if available (may contain error context)
|
|
436
|
+
if (error.stdout && error.stdout.trim() && (showOutput === 'full' || showOutput === 'minimal')) {
|
|
437
|
+
packageLogger.info(`📤 STDOUT:`);
|
|
438
|
+
error.stdout.split('\n').forEach((line)=>{
|
|
439
|
+
if (line.trim()) packageLogger.info(`${line}`);
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
// Show full output in debug/verbose mode
|
|
440
443
|
if (showOutput === 'full' || showOutput === 'minimal') {
|
|
441
|
-
|
|
442
|
-
if (error.stdout && showOutput === 'full') {
|
|
444
|
+
if (error.stdout && error.stdout.trim() && showOutput === 'full') {
|
|
443
445
|
packageLogger.debug('STDOUT:');
|
|
444
446
|
packageLogger.debug(error.stdout);
|
|
445
|
-
packageLogger.info(`📤 STDOUT:`);
|
|
446
|
-
error.stdout.split('\n').forEach((line)=>{
|
|
447
|
-
if (line.trim()) packageLogger.info(`${line}`);
|
|
448
|
-
});
|
|
449
447
|
}
|
|
450
|
-
if (error.stderr && showOutput === 'full') {
|
|
448
|
+
if (error.stderr && error.stderr.trim() && showOutput === 'full') {
|
|
451
449
|
packageLogger.debug('STDERR:');
|
|
452
450
|
packageLogger.debug(error.stderr);
|
|
453
|
-
packageLogger.info(`❌ STDERR:`);
|
|
454
|
-
error.stderr.split('\n').forEach((line)=>{
|
|
455
|
-
if (line.trim()) packageLogger.info(`${line}`);
|
|
456
|
-
});
|
|
457
451
|
}
|
|
458
452
|
}
|
|
459
453
|
// Write error output to log file
|
|
@@ -547,6 +541,9 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
547
541
|
}
|
|
548
542
|
// Track if publish was skipped due to no changes
|
|
549
543
|
let publishWasSkipped = false;
|
|
544
|
+
// Track execution timing
|
|
545
|
+
const executionTimer = PerformanceTimer.start(packageLogger, `Package ${packageName} execution`);
|
|
546
|
+
let executionDuration;
|
|
550
547
|
try {
|
|
551
548
|
if (isDryRun && !isBuiltInCommand) {
|
|
552
549
|
// Handle inter-project dependency updates for publish commands in dry run mode
|
|
@@ -649,18 +646,53 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
649
646
|
}
|
|
650
647
|
}, `${packageName}: dependency updates`);
|
|
651
648
|
}
|
|
649
|
+
// Optimize precommit commands for custom commands (not built-in)
|
|
650
|
+
let effectiveCommandToRun = commandToRun;
|
|
651
|
+
let optimizationInfo = null;
|
|
652
|
+
if (!isBuiltInCommand && !isDryRun) {
|
|
653
|
+
const isPrecommitCommand = commandToRun.includes('precommit') || commandToRun.includes('pre-commit');
|
|
654
|
+
if (isPrecommitCommand) {
|
|
655
|
+
try {
|
|
656
|
+
const optimization = await optimizePrecommitCommand(packageDir, commandToRun);
|
|
657
|
+
effectiveCommandToRun = optimization.optimizedCommand;
|
|
658
|
+
optimizationInfo = {
|
|
659
|
+
skipped: optimization.skipped,
|
|
660
|
+
reasons: optimization.reasons
|
|
661
|
+
};
|
|
662
|
+
if (optimization.skipped.clean || optimization.skipped.test) {
|
|
663
|
+
const skippedParts = [];
|
|
664
|
+
if (optimization.skipped.clean) {
|
|
665
|
+
skippedParts.push(`clean (${optimization.reasons.clean})`);
|
|
666
|
+
}
|
|
667
|
+
if (optimization.skipped.test) {
|
|
668
|
+
skippedParts.push(`test (${optimization.reasons.test})`);
|
|
669
|
+
}
|
|
670
|
+
packageLogger.info(`⚡ Optimized: Skipped ${skippedParts.join(', ')}`);
|
|
671
|
+
if (runConfig.verbose || runConfig.debug) {
|
|
672
|
+
packageLogger.info(` Original: ${commandToRun}`);
|
|
673
|
+
packageLogger.info(` Optimized: ${effectiveCommandToRun}`);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
} catch (error) {
|
|
677
|
+
// If optimization fails, fall back to original command
|
|
678
|
+
logger.debug(`Precommit optimization failed for ${packageName}: ${error.message}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
652
682
|
if (runConfig.debug || runConfig.verbose) {
|
|
653
683
|
if (isBuiltInCommand) {
|
|
654
684
|
packageLogger.info(`Executing built-in command: ${commandToRun}`);
|
|
655
685
|
} else {
|
|
656
|
-
packageLogger.info(`Executing command: ${
|
|
686
|
+
packageLogger.info(`Executing command: ${effectiveCommandToRun}`);
|
|
657
687
|
}
|
|
658
688
|
}
|
|
659
689
|
// For built-in commands, shell out to a separate kodrdriv process
|
|
660
690
|
// This preserves individual project configurations
|
|
661
691
|
if (isBuiltInCommand) {
|
|
662
|
-
// Extract the command name from "kodrdriv <command>"
|
|
663
|
-
|
|
692
|
+
// Extract the command name from "kodrdriv <command> [args...]"
|
|
693
|
+
// Split by space and take the second element (after "kodrdriv")
|
|
694
|
+
const commandParts = commandToRun.replace(/^kodrdriv\s+/, '').split(/\s+/);
|
|
695
|
+
const builtInCommandName = commandParts[0];
|
|
664
696
|
if (runConfig.debug) {
|
|
665
697
|
packageLogger.debug(`Shelling out to separate kodrdriv process for ${builtInCommandName} command`);
|
|
666
698
|
}
|
|
@@ -670,21 +702,35 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
670
702
|
packageLogger.info('⏱️ This may take several minutes (AI processing, PR creation, etc.)');
|
|
671
703
|
}
|
|
672
704
|
// Ensure dry-run propagates to subprocess even during overall dry-run mode
|
|
673
|
-
|
|
674
|
-
//
|
|
675
|
-
|
|
705
|
+
let effectiveCommand = runConfig.dryRun && !commandToRun.includes('--dry-run') ? `${commandToRun} --dry-run` : commandToRun;
|
|
706
|
+
// For commit commands, ensure --sendit is used to avoid interactive prompts
|
|
707
|
+
// This prevents hanging when running via tree command
|
|
708
|
+
if (builtInCommandName === 'commit' && !effectiveCommand.includes('--sendit') && !runConfig.dryRun) {
|
|
709
|
+
effectiveCommand = `${effectiveCommand} --sendit`;
|
|
710
|
+
packageLogger.info('💡 Auto-adding --sendit flag to avoid interactive prompts in tree mode');
|
|
711
|
+
}
|
|
712
|
+
// Set timeout based on command type
|
|
713
|
+
let commandTimeoutMs;
|
|
676
714
|
if (builtInCommandName === 'publish') {
|
|
715
|
+
commandTimeoutMs = 1800000; // 30 minutes for publish commands
|
|
677
716
|
packageLogger.info(`⏰ Setting timeout of ${commandTimeoutMs / 60000} minutes for publish command`);
|
|
717
|
+
} else if (builtInCommandName === 'commit') {
|
|
718
|
+
commandTimeoutMs = 600000; // 10 minutes for commit commands (AI processing can take time)
|
|
719
|
+
packageLogger.info(`⏰ Setting timeout of ${commandTimeoutMs / 60000} minutes for commit command`);
|
|
720
|
+
} else {
|
|
721
|
+
commandTimeoutMs = 300000; // 5 minutes default for other commands
|
|
678
722
|
}
|
|
679
723
|
const commandPromise = runWithLogging(effectiveCommand, packageLogger, {}, showOutput, logFilePath);
|
|
680
724
|
const commandTimeoutPromise = new Promise((_, reject)=>{
|
|
681
725
|
setTimeout(()=>reject(new Error(`Command timed out after ${commandTimeoutMs / 60000} minutes`)), commandTimeoutMs);
|
|
682
726
|
});
|
|
683
727
|
try {
|
|
728
|
+
const startTime = Date.now();
|
|
684
729
|
const { stdout, stderr } = await Promise.race([
|
|
685
730
|
commandPromise,
|
|
686
731
|
commandTimeoutPromise
|
|
687
732
|
]);
|
|
733
|
+
executionDuration = Date.now() - startTime;
|
|
688
734
|
// Detect explicit skip marker from publish to avoid propagating versions
|
|
689
735
|
// Check both stdout (where we now write it) and stderr (winston logger output, for backward compat)
|
|
690
736
|
if (builtInCommandName === 'publish' && (stdout && stdout.includes('KODRDRIV_PUBLISH_SKIPPED') || stderr && stderr.includes('KODRDRIV_PUBLISH_SKIPPED'))) {
|
|
@@ -701,7 +747,9 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
701
747
|
}
|
|
702
748
|
} else {
|
|
703
749
|
// For custom commands, use the existing logic
|
|
704
|
-
|
|
750
|
+
const startTime = Date.now();
|
|
751
|
+
await runWithLogging(effectiveCommandToRun, packageLogger, {}, showOutput, logFilePath);
|
|
752
|
+
executionDuration = Date.now() - startTime;
|
|
705
753
|
}
|
|
706
754
|
// Track published version after successful publish (skip during dry run)
|
|
707
755
|
if (!isDryRun && isBuiltInCommand && commandToRun.includes('publish')) {
|
|
@@ -729,11 +777,32 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
729
777
|
}
|
|
730
778
|
}
|
|
731
779
|
}
|
|
732
|
-
if (
|
|
733
|
-
|
|
780
|
+
// Record test run if tests were executed (not skipped)
|
|
781
|
+
if (!isDryRun && !isBuiltInCommand && effectiveCommandToRun.includes('test') && (!optimizationInfo || !optimizationInfo.skipped.test)) {
|
|
782
|
+
try {
|
|
783
|
+
await recordTestRun(packageDir);
|
|
784
|
+
} catch (error) {
|
|
785
|
+
logger.debug(`Failed to record test run for ${packageName}: ${error.message}`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
// End timing and show duration
|
|
789
|
+
if (executionDuration !== undefined) {
|
|
790
|
+
executionTimer.end(`Package ${packageName} execution`);
|
|
791
|
+
const seconds = (executionDuration / 1000).toFixed(1);
|
|
792
|
+
if (runConfig.debug || runConfig.verbose) {
|
|
793
|
+
packageLogger.info(`⏱️ Execution time: ${seconds}s`);
|
|
794
|
+
} else if (!isPublishCommand) {
|
|
795
|
+
// Show timing in completion message (publish commands have their own completion message)
|
|
796
|
+
logger.info(`[${index + 1}/${total}] ${packageName}: ✅ Completed (${seconds}s)`);
|
|
797
|
+
}
|
|
734
798
|
} else {
|
|
735
|
-
|
|
736
|
-
|
|
799
|
+
executionTimer.end(`Package ${packageName} execution`);
|
|
800
|
+
if (runConfig.debug || runConfig.verbose) {
|
|
801
|
+
packageLogger.info(`Command completed successfully`);
|
|
802
|
+
} else if (!isPublishCommand) {
|
|
803
|
+
// Basic completion info (publish commands have their own completion message)
|
|
804
|
+
logger.info(`[${index + 1}/${total}] ${packageName}: ✅ Completed`);
|
|
805
|
+
}
|
|
737
806
|
}
|
|
738
807
|
} finally{
|
|
739
808
|
// Safely restore working directory
|
|
@@ -753,7 +822,7 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
753
822
|
}
|
|
754
823
|
}
|
|
755
824
|
}
|
|
756
|
-
// Show completion status
|
|
825
|
+
// Show completion status (for publish commands, this supplements the timing message above)
|
|
757
826
|
if (runConfig.debug || runConfig.verbose) {
|
|
758
827
|
if (publishWasSkipped) {
|
|
759
828
|
packageLogger.info(`⊘ Skipped (no code changes)`);
|
|
@@ -762,12 +831,18 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
762
831
|
}
|
|
763
832
|
} else if (isPublishCommand) {
|
|
764
833
|
// For publish commands, always show completion even without verbose
|
|
834
|
+
// Include timing if available
|
|
835
|
+
const timeStr = executionDuration !== undefined ? ` (${(executionDuration / 1000).toFixed(1)}s)` : '';
|
|
765
836
|
if (publishWasSkipped) {
|
|
766
837
|
logger.info(`[${index + 1}/${total}] ${packageName}: ⊘ Skipped (no code changes)`);
|
|
767
838
|
} else {
|
|
768
|
-
logger.info(`[${index + 1}/${total}] ${packageName}: ✅ Completed`);
|
|
839
|
+
logger.info(`[${index + 1}/${total}] ${packageName}: ✅ Completed${timeStr}`);
|
|
769
840
|
}
|
|
770
841
|
}
|
|
842
|
+
// Ensure timing is recorded even if there was an early return
|
|
843
|
+
if (executionDuration === undefined) {
|
|
844
|
+
executionDuration = executionTimer.end(`Package ${packageName} execution`);
|
|
845
|
+
}
|
|
771
846
|
return {
|
|
772
847
|
success: true,
|
|
773
848
|
skippedNoChanges: publishWasSkipped,
|
|
@@ -775,11 +850,38 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
775
850
|
};
|
|
776
851
|
} catch (error) {
|
|
777
852
|
var _error_message;
|
|
853
|
+
// Record timing even on error
|
|
854
|
+
if (executionDuration === undefined) {
|
|
855
|
+
executionDuration = executionTimer.end(`Package ${packageName} execution`);
|
|
856
|
+
const seconds = (executionDuration / 1000).toFixed(1);
|
|
857
|
+
if (runConfig.debug || runConfig.verbose) {
|
|
858
|
+
packageLogger.error(`⏱️ Execution time before failure: ${seconds}s`);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
778
861
|
if (runConfig.debug || runConfig.verbose) {
|
|
779
862
|
packageLogger.error(`❌ Execution failed: ${error.message}`);
|
|
780
863
|
} else {
|
|
781
864
|
logger.error(`[${index + 1}/${total}] ${packageName}: ❌ Failed - ${error.message}`);
|
|
782
865
|
}
|
|
866
|
+
// Always show stderr if available (contains important error details)
|
|
867
|
+
// Note: runWithLogging already logs stderr, but we show it here too for visibility
|
|
868
|
+
// when error is caught at this level (e.g., from timeout wrapper)
|
|
869
|
+
if (error.stderr && error.stderr.trim() && !runConfig.debug && !runConfig.verbose) {
|
|
870
|
+
// Extract key error lines from stderr (coverage failures, test failures, etc.)
|
|
871
|
+
const stderrLines = error.stderr.split('\n').filter((line)=>{
|
|
872
|
+
const trimmed = line.trim();
|
|
873
|
+
return trimmed && (trimmed.includes('ERROR:') || trimmed.includes('FAIL') || trimmed.includes('coverage') || trimmed.includes('threshold') || trimmed.includes('fatal:') || trimmed.startsWith('❌'));
|
|
874
|
+
});
|
|
875
|
+
if (stderrLines.length > 0) {
|
|
876
|
+
logger.error(` Error details:`);
|
|
877
|
+
stderrLines.slice(0, 10).forEach((line)=>{
|
|
878
|
+
logger.error(` ${line.trim()}`);
|
|
879
|
+
});
|
|
880
|
+
if (stderrLines.length > 10) {
|
|
881
|
+
logger.error(` ... and ${stderrLines.length - 10} more error lines (use --verbose to see full output)`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
783
885
|
// Check if this is a timeout error
|
|
784
886
|
const errorMessage = ((_error_message = error.message) === null || _error_message === void 0 ? void 0 : _error_message.toLowerCase()) || '';
|
|
785
887
|
const isTimeoutError = errorMessage && (errorMessage.includes('timeout waiting for pr') || errorMessage.includes('timeout waiting for release workflows') || errorMessage.includes('timeout reached') || errorMessage.includes('timeout') || errorMessage.includes('timed out') || errorMessage.includes('timed_out'));
|
|
@@ -922,7 +1024,7 @@ const execute = async (runConfig)=>{
|
|
|
922
1024
|
}
|
|
923
1025
|
// Handle audit-branches command
|
|
924
1026
|
if ((_runConfig_tree4 = runConfig.tree) === null || _runConfig_tree4 === void 0 ? void 0 : _runConfig_tree4.auditBranches) {
|
|
925
|
-
var _runConfig_tree16, _runConfig_tree17, _runConfig_publish;
|
|
1027
|
+
var _runConfig_tree16, _runConfig_tree17, _runConfig_publish, _runConfig_tree18;
|
|
926
1028
|
logger.info('🔍 Auditing branch state across all packages...');
|
|
927
1029
|
const directories = ((_runConfig_tree16 = runConfig.tree) === null || _runConfig_tree16 === void 0 ? void 0 : _runConfig_tree16.directories) || [
|
|
928
1030
|
process.cwd()
|
|
@@ -942,14 +1044,30 @@ const execute = async (runConfig)=>{
|
|
|
942
1044
|
path: pkg.path
|
|
943
1045
|
}));
|
|
944
1046
|
const { auditBranchState, formatAuditResults } = await import('../utils/branchState.js');
|
|
1047
|
+
const { getRemoteDefaultBranch } = await import('@eldrforge/git-tools');
|
|
945
1048
|
// For publish workflows, check branch consistency, merge conflicts, and existing PRs
|
|
946
1049
|
// Don't pass an expected branch - let the audit find the most common branch
|
|
947
|
-
|
|
1050
|
+
let targetBranch = (_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch;
|
|
1051
|
+
if (!targetBranch) {
|
|
1052
|
+
// Try to detect default branch from the first package that is a git repo
|
|
1053
|
+
const firstGitPkg = packages.find((pkg)=>isInGitRepository(pkg.path));
|
|
1054
|
+
if (firstGitPkg) {
|
|
1055
|
+
try {
|
|
1056
|
+
// Cast to any to avoid type mismatch with node_modules version
|
|
1057
|
+
targetBranch = await getRemoteDefaultBranch(firstGitPkg.path) || 'main';
|
|
1058
|
+
} catch {
|
|
1059
|
+
targetBranch = 'main';
|
|
1060
|
+
}
|
|
1061
|
+
} else {
|
|
1062
|
+
targetBranch = 'main';
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
948
1065
|
logger.info(`Checking for merge conflicts with '${targetBranch}' and existing pull requests...`);
|
|
949
1066
|
const auditResult = await auditBranchState(packages, undefined, {
|
|
950
1067
|
targetBranch,
|
|
951
1068
|
checkPR: true,
|
|
952
|
-
checkConflicts: true
|
|
1069
|
+
checkConflicts: true,
|
|
1070
|
+
concurrency: ((_runConfig_tree18 = runConfig.tree) === null || _runConfig_tree18 === void 0 ? void 0 : _runConfig_tree18.maxConcurrency) || 10
|
|
953
1071
|
});
|
|
954
1072
|
const formatted = formatAuditResults(auditResult);
|
|
955
1073
|
logger.info('\n' + formatted);
|
|
@@ -964,13 +1082,13 @@ const execute = async (runConfig)=>{
|
|
|
964
1082
|
const { loadRecoveryManager } = await import('../execution/RecoveryManager.js');
|
|
965
1083
|
// Handle status-parallel command
|
|
966
1084
|
if ((_runConfig_tree5 = runConfig.tree) === null || _runConfig_tree5 === void 0 ? void 0 : _runConfig_tree5.statusParallel) {
|
|
967
|
-
var
|
|
1085
|
+
var _runConfig_tree19, _runConfig_tree20;
|
|
968
1086
|
logger.info('📊 Checking parallel execution status...');
|
|
969
1087
|
// Need to build dependency graph first
|
|
970
|
-
const directories = ((
|
|
1088
|
+
const directories = ((_runConfig_tree19 = runConfig.tree) === null || _runConfig_tree19 === void 0 ? void 0 : _runConfig_tree19.directories) || [
|
|
971
1089
|
process.cwd()
|
|
972
1090
|
];
|
|
973
|
-
const excludedPatterns = ((
|
|
1091
|
+
const excludedPatterns = ((_runConfig_tree20 = runConfig.tree) === null || _runConfig_tree20 === void 0 ? void 0 : _runConfig_tree20.exclude) || [];
|
|
974
1092
|
let allPackageJsonPaths = [];
|
|
975
1093
|
for (const targetDirectory of directories){
|
|
976
1094
|
const packageJsonPaths = await scanForPackageJsonFiles(targetDirectory, excludedPatterns);
|
|
@@ -991,12 +1109,12 @@ const execute = async (runConfig)=>{
|
|
|
991
1109
|
}
|
|
992
1110
|
// Handle validate-state command
|
|
993
1111
|
if ((_runConfig_tree6 = runConfig.tree) === null || _runConfig_tree6 === void 0 ? void 0 : _runConfig_tree6.validateState) {
|
|
994
|
-
var
|
|
1112
|
+
var _runConfig_tree21, _runConfig_tree22;
|
|
995
1113
|
logger.info('🔍 Validating checkpoint state...');
|
|
996
|
-
const directories = ((
|
|
1114
|
+
const directories = ((_runConfig_tree21 = runConfig.tree) === null || _runConfig_tree21 === void 0 ? void 0 : _runConfig_tree21.directories) || [
|
|
997
1115
|
process.cwd()
|
|
998
1116
|
];
|
|
999
|
-
const excludedPatterns = ((
|
|
1117
|
+
const excludedPatterns = ((_runConfig_tree22 = runConfig.tree) === null || _runConfig_tree22 === void 0 ? void 0 : _runConfig_tree22.exclude) || [];
|
|
1000
1118
|
let allPackageJsonPaths = [];
|
|
1001
1119
|
for (const targetDirectory of directories){
|
|
1002
1120
|
const packageJsonPaths = await scanForPackageJsonFiles(targetDirectory, excludedPatterns);
|
|
@@ -1065,10 +1183,10 @@ const execute = async (runConfig)=>{
|
|
|
1065
1183
|
}
|
|
1066
1184
|
// Handle continue mode
|
|
1067
1185
|
if (isContinue) {
|
|
1068
|
-
var
|
|
1186
|
+
var _runConfig_tree23;
|
|
1069
1187
|
// For parallel execution, the checkpoint is managed by DynamicTaskPool/CheckpointManager
|
|
1070
1188
|
// For sequential execution, we use the legacy context file
|
|
1071
|
-
const isParallelMode = (
|
|
1189
|
+
const isParallelMode = (_runConfig_tree23 = runConfig.tree) === null || _runConfig_tree23 === void 0 ? void 0 : _runConfig_tree23.parallel;
|
|
1072
1190
|
if (!isParallelMode) {
|
|
1073
1191
|
// Sequential execution: load legacy context
|
|
1074
1192
|
const savedContext = await loadExecutionContext(runConfig.outputDirectory);
|
|
@@ -1122,15 +1240,16 @@ const execute = async (runConfig)=>{
|
|
|
1122
1240
|
'branches',
|
|
1123
1241
|
'run',
|
|
1124
1242
|
'checkout',
|
|
1125
|
-
'updates'
|
|
1243
|
+
'updates',
|
|
1244
|
+
'precommit'
|
|
1126
1245
|
];
|
|
1127
1246
|
if (builtInCommand && !supportedBuiltInCommands.includes(builtInCommand)) {
|
|
1128
1247
|
throw new Error(`Unsupported built-in command: ${builtInCommand}. Supported commands: ${supportedBuiltInCommands.join(', ')}`);
|
|
1129
1248
|
}
|
|
1130
1249
|
// Handle run subcommand - convert space-separated scripts to npm run commands
|
|
1131
1250
|
if (builtInCommand === 'run') {
|
|
1132
|
-
var
|
|
1133
|
-
const packageArgument = (
|
|
1251
|
+
var _runConfig_tree24;
|
|
1252
|
+
const packageArgument = (_runConfig_tree24 = runConfig.tree) === null || _runConfig_tree24 === void 0 ? void 0 : _runConfig_tree24.packageArgument;
|
|
1134
1253
|
if (!packageArgument) {
|
|
1135
1254
|
throw new Error('run subcommand requires script names. Usage: kodrdriv tree run "clean build test"');
|
|
1136
1255
|
}
|
|
@@ -1202,9 +1321,9 @@ const execute = async (runConfig)=>{
|
|
|
1202
1321
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Analyzing workspaces at: ${directories.join(', ')}`);
|
|
1203
1322
|
}
|
|
1204
1323
|
try {
|
|
1205
|
-
var
|
|
1324
|
+
var _runConfig_tree25, _runConfig_tree26, _runConfig_tree27, _runConfig_tree28;
|
|
1206
1325
|
// Get exclusion patterns from config, fallback to empty array
|
|
1207
|
-
const excludedPatterns = ((
|
|
1326
|
+
const excludedPatterns = ((_runConfig_tree25 = runConfig.tree) === null || _runConfig_tree25 === void 0 ? void 0 : _runConfig_tree25.exclude) || [];
|
|
1208
1327
|
if (excludedPatterns.length > 0) {
|
|
1209
1328
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Using exclusion patterns: ${excludedPatterns.join(', ')}`);
|
|
1210
1329
|
}
|
|
@@ -1231,7 +1350,7 @@ const execute = async (runConfig)=>{
|
|
|
1231
1350
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Determining build order...`);
|
|
1232
1351
|
let buildOrder = topologicalSort(dependencyGraph);
|
|
1233
1352
|
// Handle start-from functionality if specified
|
|
1234
|
-
const startFrom = (
|
|
1353
|
+
const startFrom = (_runConfig_tree26 = runConfig.tree) === null || _runConfig_tree26 === void 0 ? void 0 : _runConfig_tree26.startFrom;
|
|
1235
1354
|
if (startFrom) {
|
|
1236
1355
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Looking for start package: ${startFrom}`);
|
|
1237
1356
|
// Resolve the actual package name (can be package name or directory name)
|
|
@@ -1288,7 +1407,7 @@ const execute = async (runConfig)=>{
|
|
|
1288
1407
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Starting execution from package '${startFrom}' (${buildOrder.length} of ${originalLength} packages remaining).`);
|
|
1289
1408
|
}
|
|
1290
1409
|
// Handle stop-at functionality if specified
|
|
1291
|
-
const stopAt = (
|
|
1410
|
+
const stopAt = (_runConfig_tree27 = runConfig.tree) === null || _runConfig_tree27 === void 0 ? void 0 : _runConfig_tree27.stopAt;
|
|
1292
1411
|
if (stopAt) {
|
|
1293
1412
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Looking for stop package: ${stopAt}`);
|
|
1294
1413
|
// Find the package that matches the stopAt directory name
|
|
@@ -1420,9 +1539,7 @@ const execute = async (runConfig)=>{
|
|
|
1420
1539
|
let maxConsumersLength = 'Consumers'.length;
|
|
1421
1540
|
const branchInfos = [];
|
|
1422
1541
|
// Create storage instance for consumer lookup
|
|
1423
|
-
const storage =
|
|
1424
|
-
log: ()=>{}
|
|
1425
|
-
});
|
|
1542
|
+
const storage = createStorage();
|
|
1426
1543
|
// Get globally linked packages once at the beginning
|
|
1427
1544
|
const globallyLinkedPackages = await getGloballyLinkedPackages();
|
|
1428
1545
|
// ANSI escape codes for progress display
|
|
@@ -1607,8 +1724,8 @@ const execute = async (runConfig)=>{
|
|
|
1607
1724
|
}
|
|
1608
1725
|
// Handle special "checkout" command that switches all packages to specified branch
|
|
1609
1726
|
if (builtInCommand === 'checkout') {
|
|
1610
|
-
var
|
|
1611
|
-
const targetBranch = (
|
|
1727
|
+
var _runConfig_tree29;
|
|
1728
|
+
const targetBranch = (_runConfig_tree29 = runConfig.tree) === null || _runConfig_tree29 === void 0 ? void 0 : _runConfig_tree29.packageArgument;
|
|
1612
1729
|
if (!targetBranch) {
|
|
1613
1730
|
throw new Error('checkout subcommand requires a branch name. Usage: kodrdriv tree checkout <branch-name>');
|
|
1614
1731
|
}
|
|
@@ -1787,12 +1904,12 @@ const execute = async (runConfig)=>{
|
|
|
1787
1904
|
returnOutput = `\nBuild order: ${buildOrder.join(' → ')}\n`;
|
|
1788
1905
|
}
|
|
1789
1906
|
// Execute command if provided (custom command or built-in command)
|
|
1790
|
-
const cmd = (
|
|
1907
|
+
const cmd = (_runConfig_tree28 = runConfig.tree) === null || _runConfig_tree28 === void 0 ? void 0 : _runConfig_tree28.cmd;
|
|
1791
1908
|
// Determine command to execute
|
|
1792
1909
|
let commandToRun;
|
|
1793
1910
|
let isBuiltInCommand = false;
|
|
1794
1911
|
if (builtInCommand) {
|
|
1795
|
-
var
|
|
1912
|
+
var _runConfig_tree30, _runConfig_tree31, _runConfig_tree32;
|
|
1796
1913
|
// Built-in command mode: shell out to kodrdriv subprocess
|
|
1797
1914
|
// Build command with propagated global options
|
|
1798
1915
|
const globalOptions = [];
|
|
@@ -1809,14 +1926,14 @@ const execute = async (runConfig)=>{
|
|
|
1809
1926
|
// Build the command with global options
|
|
1810
1927
|
const optionsString = globalOptions.length > 0 ? ` ${globalOptions.join(' ')}` : '';
|
|
1811
1928
|
// Add package argument for link/unlink/updates commands
|
|
1812
|
-
const packageArg = (
|
|
1929
|
+
const packageArg = (_runConfig_tree30 = runConfig.tree) === null || _runConfig_tree30 === void 0 ? void 0 : _runConfig_tree30.packageArgument;
|
|
1813
1930
|
const packageArgString = packageArg && (builtInCommand === 'link' || builtInCommand === 'unlink' || builtInCommand === 'updates') ? ` "${packageArg}"` : '';
|
|
1814
1931
|
// Add command-specific options
|
|
1815
1932
|
let commandSpecificOptions = '';
|
|
1816
|
-
if (builtInCommand === 'unlink' && ((
|
|
1933
|
+
if (builtInCommand === 'unlink' && ((_runConfig_tree31 = runConfig.tree) === null || _runConfig_tree31 === void 0 ? void 0 : _runConfig_tree31.cleanNodeModules)) {
|
|
1817
1934
|
commandSpecificOptions += ' --clean-node-modules';
|
|
1818
1935
|
}
|
|
1819
|
-
if ((builtInCommand === 'link' || builtInCommand === 'unlink') && ((
|
|
1936
|
+
if ((builtInCommand === 'link' || builtInCommand === 'unlink') && ((_runConfig_tree32 = runConfig.tree) === null || _runConfig_tree32 === void 0 ? void 0 : _runConfig_tree32.externals) && runConfig.tree.externals.length > 0) {
|
|
1820
1937
|
commandSpecificOptions += ` --externals ${runConfig.tree.externals.join(' ')}`;
|
|
1821
1938
|
}
|
|
1822
1939
|
commandToRun = `kodrdriv ${builtInCommand}${optionsString}${packageArgString}${commandSpecificOptions}`;
|
|
@@ -1826,7 +1943,7 @@ const execute = async (runConfig)=>{
|
|
|
1826
1943
|
commandToRun = cmd;
|
|
1827
1944
|
}
|
|
1828
1945
|
if (commandToRun) {
|
|
1829
|
-
var
|
|
1946
|
+
var _runConfig_tree33, _runConfig_tree34;
|
|
1830
1947
|
// Validate scripts for run command before execution
|
|
1831
1948
|
const scriptsToValidate = runConfig.__scriptsToValidate;
|
|
1832
1949
|
if (scriptsToValidate && scriptsToValidate.length > 0) {
|
|
@@ -1845,7 +1962,7 @@ const execute = async (runConfig)=>{
|
|
|
1845
1962
|
}
|
|
1846
1963
|
}
|
|
1847
1964
|
// Validate command for parallel execution if parallel mode is enabled
|
|
1848
|
-
if ((
|
|
1965
|
+
if ((_runConfig_tree33 = runConfig.tree) === null || _runConfig_tree33 === void 0 ? void 0 : _runConfig_tree33.parallel) {
|
|
1849
1966
|
const { CommandValidator } = await import('../execution/CommandValidator.js');
|
|
1850
1967
|
const validation = CommandValidator.validateForParallel(commandToRun, builtInCommand);
|
|
1851
1968
|
CommandValidator.logValidation(validation);
|
|
@@ -1856,11 +1973,12 @@ const execute = async (runConfig)=>{
|
|
|
1856
1973
|
throw new Error('Command validation failed for parallel execution');
|
|
1857
1974
|
}
|
|
1858
1975
|
// Apply recommended concurrency if not explicitly set
|
|
1859
|
-
if (!runConfig.tree.maxConcurrency
|
|
1976
|
+
if (!runConfig.tree.maxConcurrency) {
|
|
1860
1977
|
const os = await import('os');
|
|
1861
|
-
const recommended = CommandValidator.getRecommendedConcurrency(builtInCommand, os.cpus().length);
|
|
1978
|
+
const recommended = CommandValidator.getRecommendedConcurrency(builtInCommand, os.cpus().length, commandToRun);
|
|
1862
1979
|
if (recommended !== os.cpus().length) {
|
|
1863
|
-
|
|
1980
|
+
const reason = builtInCommand ? builtInCommand : `custom command "${commandToRun}"`;
|
|
1981
|
+
logger.info(`💡 Using recommended concurrency for ${reason}: ${recommended}`);
|
|
1864
1982
|
runConfig.tree.maxConcurrency = recommended;
|
|
1865
1983
|
}
|
|
1866
1984
|
}
|
|
@@ -1896,7 +2014,7 @@ const execute = async (runConfig)=>{
|
|
|
1896
2014
|
// If continuing, start from where we left off
|
|
1897
2015
|
const startIndex = isContinue && executionContext ? executionContext.completedPackages.length : 0;
|
|
1898
2016
|
// Check if parallel execution is enabled
|
|
1899
|
-
if ((
|
|
2017
|
+
if ((_runConfig_tree34 = runConfig.tree) === null || _runConfig_tree34 === void 0 ? void 0 : _runConfig_tree34.parallel) {
|
|
1900
2018
|
var _runConfig_tree_retry1, _runConfig_tree_retry2, _runConfig_tree_retry3, _runConfig_tree_retry4;
|
|
1901
2019
|
logger.info('🚀 Using parallel execution mode');
|
|
1902
2020
|
// If dry run, show preview instead of executing
|
|
@@ -1929,6 +2047,7 @@ const execute = async (runConfig)=>{
|
|
|
1929
2047
|
return formattedResult;
|
|
1930
2048
|
}
|
|
1931
2049
|
// Sequential execution
|
|
2050
|
+
const executionStartTime = Date.now();
|
|
1932
2051
|
for(let i = startIndex; i < buildOrder.length; i++){
|
|
1933
2052
|
const packageName = buildOrder[i];
|
|
1934
2053
|
// Skip if already completed (in continue mode)
|
|
@@ -2031,8 +2150,18 @@ const execute = async (runConfig)=>{
|
|
|
2031
2150
|
}
|
|
2032
2151
|
}
|
|
2033
2152
|
if (!failedPackage) {
|
|
2153
|
+
const totalExecutionTime = Date.now() - executionStartTime;
|
|
2154
|
+
const totalSeconds = (totalExecutionTime / 1000).toFixed(1);
|
|
2155
|
+
const totalMinutes = (totalExecutionTime / 60000).toFixed(1);
|
|
2156
|
+
const timeDisplay = totalExecutionTime < 60000 ? `${totalSeconds}s` : `${totalMinutes}min (${totalSeconds}s)`;
|
|
2157
|
+
logger.info('');
|
|
2158
|
+
logger.info('═══════════════════════════════════════════════════════════');
|
|
2034
2159
|
const summary = `${isDryRun ? 'DRY RUN: ' : ''}All ${buildOrder.length} packages completed successfully! 🎉`;
|
|
2035
2160
|
logger.info(summary);
|
|
2161
|
+
logger.info(`⏱️ Total execution time: ${timeDisplay}`);
|
|
2162
|
+
logger.info(`📦 Packages processed: ${successCount}/${buildOrder.length}`);
|
|
2163
|
+
logger.info('═══════════════════════════════════════════════════════════');
|
|
2164
|
+
logger.info('');
|
|
2036
2165
|
// Clean up context on successful completion
|
|
2037
2166
|
if (isBuiltInCommand && (builtInCommand === 'publish' || builtInCommand === 'run') && !isDryRun) {
|
|
2038
2167
|
await cleanupContext(runConfig.outputDirectory);
|