@eldrforge/kodrdriv 1.2.28 → 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 +271 -88
- 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/DynamicTaskPool.js +96 -9
- package/dist/execution/DynamicTaskPool.js.map +1 -1
- package/dist/execution/ResourceMonitor.js +26 -1
- package/dist/execution/ResourceMonitor.js.map +1 -1
- package/dist/execution/TreeExecutionAdapter.js +6 -3
- 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-FIXES-IMPLEMENTED.md +0 -405
- package/PARALLEL-PUBLISH-IMPROVEMENTS-IMPLEMENTED.md +0 -439
- 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
|
|
@@ -363,7 +349,7 @@ const extractPublishedVersion = async (packageDir, packageLogger)=>{
|
|
|
363
349
|
}
|
|
364
350
|
};
|
|
365
351
|
// Enhanced run function that can show output based on log level
|
|
366
|
-
const runWithLogging = async (command, packageLogger, options = {}, showOutput = 'none')=>{
|
|
352
|
+
const runWithLogging = async (command, packageLogger, options = {}, showOutput = 'none', logFilePath)=>{
|
|
367
353
|
const execPromise = util.promisify(exec);
|
|
368
354
|
// Ensure encoding is set to 'utf8' to get string output instead of Buffer
|
|
369
355
|
const execOptions = {
|
|
@@ -377,6 +363,24 @@ const runWithLogging = async (command, packageLogger, options = {}, showOutput =
|
|
|
377
363
|
} else if (showOutput === 'minimal') {
|
|
378
364
|
packageLogger.verbose(`Running: ${command}`);
|
|
379
365
|
}
|
|
366
|
+
// Helper to write to log file
|
|
367
|
+
const writeToLogFile = async (content)=>{
|
|
368
|
+
if (!logFilePath) return;
|
|
369
|
+
try {
|
|
370
|
+
const logDir = path__default.dirname(logFilePath);
|
|
371
|
+
await fs.mkdir(logDir, {
|
|
372
|
+
recursive: true
|
|
373
|
+
});
|
|
374
|
+
await fs.appendFile(logFilePath, content + '\n', 'utf-8');
|
|
375
|
+
} catch (err) {
|
|
376
|
+
packageLogger.warn(`Failed to write to log file ${logFilePath}: ${err.message}`);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
// Write command to log file
|
|
380
|
+
if (logFilePath) {
|
|
381
|
+
const timestamp = new Date().toISOString();
|
|
382
|
+
await writeToLogFile(`[${timestamp}] Executing: ${command}\n`);
|
|
383
|
+
}
|
|
380
384
|
try {
|
|
381
385
|
const result = await execPromise(command, execOptions);
|
|
382
386
|
if (showOutput === 'full') {
|
|
@@ -401,29 +405,62 @@ const runWithLogging = async (command, packageLogger, options = {}, showOutput =
|
|
|
401
405
|
});
|
|
402
406
|
}
|
|
403
407
|
}
|
|
408
|
+
// Write output to log file
|
|
409
|
+
if (logFilePath) {
|
|
410
|
+
const stdout = String(result.stdout);
|
|
411
|
+
const stderr = String(result.stderr);
|
|
412
|
+
if (stdout.trim()) {
|
|
413
|
+
await writeToLogFile(`\n=== STDOUT ===\n${stdout}`);
|
|
414
|
+
}
|
|
415
|
+
if (stderr.trim()) {
|
|
416
|
+
await writeToLogFile(`\n=== STDERR ===\n${stderr}`);
|
|
417
|
+
}
|
|
418
|
+
await writeToLogFile(`\n[${new Date().toISOString()}] Command completed successfully\n`);
|
|
419
|
+
}
|
|
404
420
|
// Ensure result is properly typed as strings
|
|
405
421
|
return {
|
|
406
422
|
stdout: String(result.stdout),
|
|
407
423
|
stderr: String(result.stderr)
|
|
408
424
|
};
|
|
409
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
|
|
410
443
|
if (showOutput === 'full' || showOutput === 'minimal') {
|
|
411
|
-
|
|
412
|
-
if (error.stdout && showOutput === 'full') {
|
|
444
|
+
if (error.stdout && error.stdout.trim() && showOutput === 'full') {
|
|
413
445
|
packageLogger.debug('STDOUT:');
|
|
414
446
|
packageLogger.debug(error.stdout);
|
|
415
|
-
packageLogger.info(`📤 STDOUT:`);
|
|
416
|
-
error.stdout.split('\n').forEach((line)=>{
|
|
417
|
-
if (line.trim()) packageLogger.info(`${line}`);
|
|
418
|
-
});
|
|
419
447
|
}
|
|
420
|
-
if (error.stderr && showOutput === 'full') {
|
|
448
|
+
if (error.stderr && error.stderr.trim() && showOutput === 'full') {
|
|
421
449
|
packageLogger.debug('STDERR:');
|
|
422
450
|
packageLogger.debug(error.stderr);
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
// Write error output to log file
|
|
454
|
+
if (logFilePath) {
|
|
455
|
+
await writeToLogFile(`\n[${new Date().toISOString()}] Command failed: ${error.message}`);
|
|
456
|
+
if (error.stdout) {
|
|
457
|
+
await writeToLogFile(`\n=== STDOUT ===\n${error.stdout}`);
|
|
458
|
+
}
|
|
459
|
+
if (error.stderr) {
|
|
460
|
+
await writeToLogFile(`\n=== STDERR ===\n${error.stderr}`);
|
|
461
|
+
}
|
|
462
|
+
if (error.stack) {
|
|
463
|
+
await writeToLogFile(`\n=== STACK TRACE ===\n${error.stack}`);
|
|
427
464
|
}
|
|
428
465
|
}
|
|
429
466
|
throw error;
|
|
@@ -473,6 +510,15 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
473
510
|
const packageLogger = createPackageLogger(packageName, index + 1, total, isDryRun);
|
|
474
511
|
const packageDir = packageInfo.path;
|
|
475
512
|
const logger = getLogger();
|
|
513
|
+
// Create log file path for publish commands
|
|
514
|
+
let logFilePath;
|
|
515
|
+
if (isBuiltInCommand && commandToRun.includes('publish')) {
|
|
516
|
+
var _commandToRun_split_;
|
|
517
|
+
const outputDir = runConfig.outputDirectory || 'output/kodrdriv';
|
|
518
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').split('.')[0];
|
|
519
|
+
const commandName = ((_commandToRun_split_ = commandToRun.split(' ')[1]) === null || _commandToRun_split_ === void 0 ? void 0 : _commandToRun_split_.split(' ')[0]) || 'command';
|
|
520
|
+
logFilePath = path__default.join(packageDir, outputDir, `${commandName}_${timestamp}.log`);
|
|
521
|
+
}
|
|
476
522
|
// Determine output level based on flags
|
|
477
523
|
// For publish commands, default to full output to show OpenAI progress and other details
|
|
478
524
|
// For other commands, require --verbose or --debug for output
|
|
@@ -495,6 +541,9 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
495
541
|
}
|
|
496
542
|
// Track if publish was skipped due to no changes
|
|
497
543
|
let publishWasSkipped = false;
|
|
544
|
+
// Track execution timing
|
|
545
|
+
const executionTimer = PerformanceTimer.start(packageLogger, `Package ${packageName} execution`);
|
|
546
|
+
let executionDuration;
|
|
498
547
|
try {
|
|
499
548
|
if (isDryRun && !isBuiltInCommand) {
|
|
500
549
|
// Handle inter-project dependency updates for publish commands in dry run mode
|
|
@@ -597,18 +646,53 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
597
646
|
}
|
|
598
647
|
}, `${packageName}: dependency updates`);
|
|
599
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
|
+
}
|
|
600
682
|
if (runConfig.debug || runConfig.verbose) {
|
|
601
683
|
if (isBuiltInCommand) {
|
|
602
684
|
packageLogger.info(`Executing built-in command: ${commandToRun}`);
|
|
603
685
|
} else {
|
|
604
|
-
packageLogger.info(`Executing command: ${
|
|
686
|
+
packageLogger.info(`Executing command: ${effectiveCommandToRun}`);
|
|
605
687
|
}
|
|
606
688
|
}
|
|
607
689
|
// For built-in commands, shell out to a separate kodrdriv process
|
|
608
690
|
// This preserves individual project configurations
|
|
609
691
|
if (isBuiltInCommand) {
|
|
610
|
-
// Extract the command name from "kodrdriv <command>"
|
|
611
|
-
|
|
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];
|
|
612
696
|
if (runConfig.debug) {
|
|
613
697
|
packageLogger.debug(`Shelling out to separate kodrdriv process for ${builtInCommandName} command`);
|
|
614
698
|
}
|
|
@@ -618,21 +702,35 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
618
702
|
packageLogger.info('⏱️ This may take several minutes (AI processing, PR creation, etc.)');
|
|
619
703
|
}
|
|
620
704
|
// Ensure dry-run propagates to subprocess even during overall dry-run mode
|
|
621
|
-
|
|
622
|
-
//
|
|
623
|
-
|
|
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;
|
|
624
714
|
if (builtInCommandName === 'publish') {
|
|
715
|
+
commandTimeoutMs = 1800000; // 30 minutes for publish commands
|
|
625
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
|
|
626
722
|
}
|
|
627
|
-
const commandPromise = runWithLogging(effectiveCommand, packageLogger, {}, showOutput);
|
|
723
|
+
const commandPromise = runWithLogging(effectiveCommand, packageLogger, {}, showOutput, logFilePath);
|
|
628
724
|
const commandTimeoutPromise = new Promise((_, reject)=>{
|
|
629
725
|
setTimeout(()=>reject(new Error(`Command timed out after ${commandTimeoutMs / 60000} minutes`)), commandTimeoutMs);
|
|
630
726
|
});
|
|
631
727
|
try {
|
|
728
|
+
const startTime = Date.now();
|
|
632
729
|
const { stdout, stderr } = await Promise.race([
|
|
633
730
|
commandPromise,
|
|
634
731
|
commandTimeoutPromise
|
|
635
732
|
]);
|
|
733
|
+
executionDuration = Date.now() - startTime;
|
|
636
734
|
// Detect explicit skip marker from publish to avoid propagating versions
|
|
637
735
|
// Check both stdout (where we now write it) and stderr (winston logger output, for backward compat)
|
|
638
736
|
if (builtInCommandName === 'publish' && (stdout && stdout.includes('KODRDRIV_PUBLISH_SKIPPED') || stderr && stderr.includes('KODRDRIV_PUBLISH_SKIPPED'))) {
|
|
@@ -649,7 +747,9 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
649
747
|
}
|
|
650
748
|
} else {
|
|
651
749
|
// For custom commands, use the existing logic
|
|
652
|
-
|
|
750
|
+
const startTime = Date.now();
|
|
751
|
+
await runWithLogging(effectiveCommandToRun, packageLogger, {}, showOutput, logFilePath);
|
|
752
|
+
executionDuration = Date.now() - startTime;
|
|
653
753
|
}
|
|
654
754
|
// Track published version after successful publish (skip during dry run)
|
|
655
755
|
if (!isDryRun && isBuiltInCommand && commandToRun.includes('publish')) {
|
|
@@ -677,11 +777,32 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
677
777
|
}
|
|
678
778
|
}
|
|
679
779
|
}
|
|
680
|
-
if (
|
|
681
|
-
|
|
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
|
+
}
|
|
682
798
|
} else {
|
|
683
|
-
|
|
684
|
-
|
|
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
|
+
}
|
|
685
806
|
}
|
|
686
807
|
} finally{
|
|
687
808
|
// Safely restore working directory
|
|
@@ -701,7 +822,7 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
701
822
|
}
|
|
702
823
|
}
|
|
703
824
|
}
|
|
704
|
-
// Show completion status
|
|
825
|
+
// Show completion status (for publish commands, this supplements the timing message above)
|
|
705
826
|
if (runConfig.debug || runConfig.verbose) {
|
|
706
827
|
if (publishWasSkipped) {
|
|
707
828
|
packageLogger.info(`⊘ Skipped (no code changes)`);
|
|
@@ -710,30 +831,65 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
710
831
|
}
|
|
711
832
|
} else if (isPublishCommand) {
|
|
712
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)` : '';
|
|
713
836
|
if (publishWasSkipped) {
|
|
714
837
|
logger.info(`[${index + 1}/${total}] ${packageName}: ⊘ Skipped (no code changes)`);
|
|
715
838
|
} else {
|
|
716
|
-
logger.info(`[${index + 1}/${total}] ${packageName}: ✅ Completed`);
|
|
839
|
+
logger.info(`[${index + 1}/${total}] ${packageName}: ✅ Completed${timeStr}`);
|
|
717
840
|
}
|
|
718
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
|
+
}
|
|
719
846
|
return {
|
|
720
847
|
success: true,
|
|
721
|
-
skippedNoChanges: publishWasSkipped
|
|
848
|
+
skippedNoChanges: publishWasSkipped,
|
|
849
|
+
logFile: logFilePath
|
|
722
850
|
};
|
|
723
851
|
} catch (error) {
|
|
724
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
|
+
}
|
|
725
861
|
if (runConfig.debug || runConfig.verbose) {
|
|
726
862
|
packageLogger.error(`❌ Execution failed: ${error.message}`);
|
|
727
863
|
} else {
|
|
728
864
|
logger.error(`[${index + 1}/${total}] ${packageName}: ❌ Failed - ${error.message}`);
|
|
729
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
|
+
}
|
|
730
885
|
// Check if this is a timeout error
|
|
731
886
|
const errorMessage = ((_error_message = error.message) === null || _error_message === void 0 ? void 0 : _error_message.toLowerCase()) || '';
|
|
732
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'));
|
|
733
888
|
return {
|
|
734
889
|
success: false,
|
|
735
890
|
error,
|
|
736
|
-
isTimeoutError
|
|
891
|
+
isTimeoutError,
|
|
892
|
+
logFile: logFilePath
|
|
737
893
|
};
|
|
738
894
|
}
|
|
739
895
|
};
|
|
@@ -868,7 +1024,7 @@ const execute = async (runConfig)=>{
|
|
|
868
1024
|
}
|
|
869
1025
|
// Handle audit-branches command
|
|
870
1026
|
if ((_runConfig_tree4 = runConfig.tree) === null || _runConfig_tree4 === void 0 ? void 0 : _runConfig_tree4.auditBranches) {
|
|
871
|
-
var _runConfig_tree16, _runConfig_tree17, _runConfig_publish;
|
|
1027
|
+
var _runConfig_tree16, _runConfig_tree17, _runConfig_publish, _runConfig_tree18;
|
|
872
1028
|
logger.info('🔍 Auditing branch state across all packages...');
|
|
873
1029
|
const directories = ((_runConfig_tree16 = runConfig.tree) === null || _runConfig_tree16 === void 0 ? void 0 : _runConfig_tree16.directories) || [
|
|
874
1030
|
process.cwd()
|
|
@@ -888,14 +1044,30 @@ const execute = async (runConfig)=>{
|
|
|
888
1044
|
path: pkg.path
|
|
889
1045
|
}));
|
|
890
1046
|
const { auditBranchState, formatAuditResults } = await import('../utils/branchState.js');
|
|
1047
|
+
const { getRemoteDefaultBranch } = await import('@eldrforge/git-tools');
|
|
891
1048
|
// For publish workflows, check branch consistency, merge conflicts, and existing PRs
|
|
892
1049
|
// Don't pass an expected branch - let the audit find the most common branch
|
|
893
|
-
|
|
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
|
+
}
|
|
894
1065
|
logger.info(`Checking for merge conflicts with '${targetBranch}' and existing pull requests...`);
|
|
895
1066
|
const auditResult = await auditBranchState(packages, undefined, {
|
|
896
1067
|
targetBranch,
|
|
897
1068
|
checkPR: true,
|
|
898
|
-
checkConflicts: true
|
|
1069
|
+
checkConflicts: true,
|
|
1070
|
+
concurrency: ((_runConfig_tree18 = runConfig.tree) === null || _runConfig_tree18 === void 0 ? void 0 : _runConfig_tree18.maxConcurrency) || 10
|
|
899
1071
|
});
|
|
900
1072
|
const formatted = formatAuditResults(auditResult);
|
|
901
1073
|
logger.info('\n' + formatted);
|
|
@@ -910,13 +1082,13 @@ const execute = async (runConfig)=>{
|
|
|
910
1082
|
const { loadRecoveryManager } = await import('../execution/RecoveryManager.js');
|
|
911
1083
|
// Handle status-parallel command
|
|
912
1084
|
if ((_runConfig_tree5 = runConfig.tree) === null || _runConfig_tree5 === void 0 ? void 0 : _runConfig_tree5.statusParallel) {
|
|
913
|
-
var
|
|
1085
|
+
var _runConfig_tree19, _runConfig_tree20;
|
|
914
1086
|
logger.info('📊 Checking parallel execution status...');
|
|
915
1087
|
// Need to build dependency graph first
|
|
916
|
-
const directories = ((
|
|
1088
|
+
const directories = ((_runConfig_tree19 = runConfig.tree) === null || _runConfig_tree19 === void 0 ? void 0 : _runConfig_tree19.directories) || [
|
|
917
1089
|
process.cwd()
|
|
918
1090
|
];
|
|
919
|
-
const excludedPatterns = ((
|
|
1091
|
+
const excludedPatterns = ((_runConfig_tree20 = runConfig.tree) === null || _runConfig_tree20 === void 0 ? void 0 : _runConfig_tree20.exclude) || [];
|
|
920
1092
|
let allPackageJsonPaths = [];
|
|
921
1093
|
for (const targetDirectory of directories){
|
|
922
1094
|
const packageJsonPaths = await scanForPackageJsonFiles(targetDirectory, excludedPatterns);
|
|
@@ -937,12 +1109,12 @@ const execute = async (runConfig)=>{
|
|
|
937
1109
|
}
|
|
938
1110
|
// Handle validate-state command
|
|
939
1111
|
if ((_runConfig_tree6 = runConfig.tree) === null || _runConfig_tree6 === void 0 ? void 0 : _runConfig_tree6.validateState) {
|
|
940
|
-
var
|
|
1112
|
+
var _runConfig_tree21, _runConfig_tree22;
|
|
941
1113
|
logger.info('🔍 Validating checkpoint state...');
|
|
942
|
-
const directories = ((
|
|
1114
|
+
const directories = ((_runConfig_tree21 = runConfig.tree) === null || _runConfig_tree21 === void 0 ? void 0 : _runConfig_tree21.directories) || [
|
|
943
1115
|
process.cwd()
|
|
944
1116
|
];
|
|
945
|
-
const excludedPatterns = ((
|
|
1117
|
+
const excludedPatterns = ((_runConfig_tree22 = runConfig.tree) === null || _runConfig_tree22 === void 0 ? void 0 : _runConfig_tree22.exclude) || [];
|
|
946
1118
|
let allPackageJsonPaths = [];
|
|
947
1119
|
for (const targetDirectory of directories){
|
|
948
1120
|
const packageJsonPaths = await scanForPackageJsonFiles(targetDirectory, excludedPatterns);
|
|
@@ -1011,10 +1183,10 @@ const execute = async (runConfig)=>{
|
|
|
1011
1183
|
}
|
|
1012
1184
|
// Handle continue mode
|
|
1013
1185
|
if (isContinue) {
|
|
1014
|
-
var
|
|
1186
|
+
var _runConfig_tree23;
|
|
1015
1187
|
// For parallel execution, the checkpoint is managed by DynamicTaskPool/CheckpointManager
|
|
1016
1188
|
// For sequential execution, we use the legacy context file
|
|
1017
|
-
const isParallelMode = (
|
|
1189
|
+
const isParallelMode = (_runConfig_tree23 = runConfig.tree) === null || _runConfig_tree23 === void 0 ? void 0 : _runConfig_tree23.parallel;
|
|
1018
1190
|
if (!isParallelMode) {
|
|
1019
1191
|
// Sequential execution: load legacy context
|
|
1020
1192
|
const savedContext = await loadExecutionContext(runConfig.outputDirectory);
|
|
@@ -1068,15 +1240,16 @@ const execute = async (runConfig)=>{
|
|
|
1068
1240
|
'branches',
|
|
1069
1241
|
'run',
|
|
1070
1242
|
'checkout',
|
|
1071
|
-
'updates'
|
|
1243
|
+
'updates',
|
|
1244
|
+
'precommit'
|
|
1072
1245
|
];
|
|
1073
1246
|
if (builtInCommand && !supportedBuiltInCommands.includes(builtInCommand)) {
|
|
1074
1247
|
throw new Error(`Unsupported built-in command: ${builtInCommand}. Supported commands: ${supportedBuiltInCommands.join(', ')}`);
|
|
1075
1248
|
}
|
|
1076
1249
|
// Handle run subcommand - convert space-separated scripts to npm run commands
|
|
1077
1250
|
if (builtInCommand === 'run') {
|
|
1078
|
-
var
|
|
1079
|
-
const packageArgument = (
|
|
1251
|
+
var _runConfig_tree24;
|
|
1252
|
+
const packageArgument = (_runConfig_tree24 = runConfig.tree) === null || _runConfig_tree24 === void 0 ? void 0 : _runConfig_tree24.packageArgument;
|
|
1080
1253
|
if (!packageArgument) {
|
|
1081
1254
|
throw new Error('run subcommand requires script names. Usage: kodrdriv tree run "clean build test"');
|
|
1082
1255
|
}
|
|
@@ -1148,9 +1321,9 @@ const execute = async (runConfig)=>{
|
|
|
1148
1321
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Analyzing workspaces at: ${directories.join(', ')}`);
|
|
1149
1322
|
}
|
|
1150
1323
|
try {
|
|
1151
|
-
var
|
|
1324
|
+
var _runConfig_tree25, _runConfig_tree26, _runConfig_tree27, _runConfig_tree28;
|
|
1152
1325
|
// Get exclusion patterns from config, fallback to empty array
|
|
1153
|
-
const excludedPatterns = ((
|
|
1326
|
+
const excludedPatterns = ((_runConfig_tree25 = runConfig.tree) === null || _runConfig_tree25 === void 0 ? void 0 : _runConfig_tree25.exclude) || [];
|
|
1154
1327
|
if (excludedPatterns.length > 0) {
|
|
1155
1328
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Using exclusion patterns: ${excludedPatterns.join(', ')}`);
|
|
1156
1329
|
}
|
|
@@ -1177,7 +1350,7 @@ const execute = async (runConfig)=>{
|
|
|
1177
1350
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Determining build order...`);
|
|
1178
1351
|
let buildOrder = topologicalSort(dependencyGraph);
|
|
1179
1352
|
// Handle start-from functionality if specified
|
|
1180
|
-
const startFrom = (
|
|
1353
|
+
const startFrom = (_runConfig_tree26 = runConfig.tree) === null || _runConfig_tree26 === void 0 ? void 0 : _runConfig_tree26.startFrom;
|
|
1181
1354
|
if (startFrom) {
|
|
1182
1355
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Looking for start package: ${startFrom}`);
|
|
1183
1356
|
// Resolve the actual package name (can be package name or directory name)
|
|
@@ -1234,7 +1407,7 @@ const execute = async (runConfig)=>{
|
|
|
1234
1407
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Starting execution from package '${startFrom}' (${buildOrder.length} of ${originalLength} packages remaining).`);
|
|
1235
1408
|
}
|
|
1236
1409
|
// Handle stop-at functionality if specified
|
|
1237
|
-
const stopAt = (
|
|
1410
|
+
const stopAt = (_runConfig_tree27 = runConfig.tree) === null || _runConfig_tree27 === void 0 ? void 0 : _runConfig_tree27.stopAt;
|
|
1238
1411
|
if (stopAt) {
|
|
1239
1412
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Looking for stop package: ${stopAt}`);
|
|
1240
1413
|
// Find the package that matches the stopAt directory name
|
|
@@ -1366,9 +1539,7 @@ const execute = async (runConfig)=>{
|
|
|
1366
1539
|
let maxConsumersLength = 'Consumers'.length;
|
|
1367
1540
|
const branchInfos = [];
|
|
1368
1541
|
// Create storage instance for consumer lookup
|
|
1369
|
-
const storage =
|
|
1370
|
-
log: ()=>{}
|
|
1371
|
-
});
|
|
1542
|
+
const storage = createStorage();
|
|
1372
1543
|
// Get globally linked packages once at the beginning
|
|
1373
1544
|
const globallyLinkedPackages = await getGloballyLinkedPackages();
|
|
1374
1545
|
// ANSI escape codes for progress display
|
|
@@ -1553,8 +1724,8 @@ const execute = async (runConfig)=>{
|
|
|
1553
1724
|
}
|
|
1554
1725
|
// Handle special "checkout" command that switches all packages to specified branch
|
|
1555
1726
|
if (builtInCommand === 'checkout') {
|
|
1556
|
-
var
|
|
1557
|
-
const targetBranch = (
|
|
1727
|
+
var _runConfig_tree29;
|
|
1728
|
+
const targetBranch = (_runConfig_tree29 = runConfig.tree) === null || _runConfig_tree29 === void 0 ? void 0 : _runConfig_tree29.packageArgument;
|
|
1558
1729
|
if (!targetBranch) {
|
|
1559
1730
|
throw new Error('checkout subcommand requires a branch name. Usage: kodrdriv tree checkout <branch-name>');
|
|
1560
1731
|
}
|
|
@@ -1733,12 +1904,12 @@ const execute = async (runConfig)=>{
|
|
|
1733
1904
|
returnOutput = `\nBuild order: ${buildOrder.join(' → ')}\n`;
|
|
1734
1905
|
}
|
|
1735
1906
|
// Execute command if provided (custom command or built-in command)
|
|
1736
|
-
const cmd = (
|
|
1907
|
+
const cmd = (_runConfig_tree28 = runConfig.tree) === null || _runConfig_tree28 === void 0 ? void 0 : _runConfig_tree28.cmd;
|
|
1737
1908
|
// Determine command to execute
|
|
1738
1909
|
let commandToRun;
|
|
1739
1910
|
let isBuiltInCommand = false;
|
|
1740
1911
|
if (builtInCommand) {
|
|
1741
|
-
var
|
|
1912
|
+
var _runConfig_tree30, _runConfig_tree31, _runConfig_tree32;
|
|
1742
1913
|
// Built-in command mode: shell out to kodrdriv subprocess
|
|
1743
1914
|
// Build command with propagated global options
|
|
1744
1915
|
const globalOptions = [];
|
|
@@ -1755,14 +1926,14 @@ const execute = async (runConfig)=>{
|
|
|
1755
1926
|
// Build the command with global options
|
|
1756
1927
|
const optionsString = globalOptions.length > 0 ? ` ${globalOptions.join(' ')}` : '';
|
|
1757
1928
|
// Add package argument for link/unlink/updates commands
|
|
1758
|
-
const packageArg = (
|
|
1929
|
+
const packageArg = (_runConfig_tree30 = runConfig.tree) === null || _runConfig_tree30 === void 0 ? void 0 : _runConfig_tree30.packageArgument;
|
|
1759
1930
|
const packageArgString = packageArg && (builtInCommand === 'link' || builtInCommand === 'unlink' || builtInCommand === 'updates') ? ` "${packageArg}"` : '';
|
|
1760
1931
|
// Add command-specific options
|
|
1761
1932
|
let commandSpecificOptions = '';
|
|
1762
|
-
if (builtInCommand === 'unlink' && ((
|
|
1933
|
+
if (builtInCommand === 'unlink' && ((_runConfig_tree31 = runConfig.tree) === null || _runConfig_tree31 === void 0 ? void 0 : _runConfig_tree31.cleanNodeModules)) {
|
|
1763
1934
|
commandSpecificOptions += ' --clean-node-modules';
|
|
1764
1935
|
}
|
|
1765
|
-
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) {
|
|
1766
1937
|
commandSpecificOptions += ` --externals ${runConfig.tree.externals.join(' ')}`;
|
|
1767
1938
|
}
|
|
1768
1939
|
commandToRun = `kodrdriv ${builtInCommand}${optionsString}${packageArgString}${commandSpecificOptions}`;
|
|
@@ -1772,7 +1943,7 @@ const execute = async (runConfig)=>{
|
|
|
1772
1943
|
commandToRun = cmd;
|
|
1773
1944
|
}
|
|
1774
1945
|
if (commandToRun) {
|
|
1775
|
-
var
|
|
1946
|
+
var _runConfig_tree33, _runConfig_tree34;
|
|
1776
1947
|
// Validate scripts for run command before execution
|
|
1777
1948
|
const scriptsToValidate = runConfig.__scriptsToValidate;
|
|
1778
1949
|
if (scriptsToValidate && scriptsToValidate.length > 0) {
|
|
@@ -1791,7 +1962,7 @@ const execute = async (runConfig)=>{
|
|
|
1791
1962
|
}
|
|
1792
1963
|
}
|
|
1793
1964
|
// Validate command for parallel execution if parallel mode is enabled
|
|
1794
|
-
if ((
|
|
1965
|
+
if ((_runConfig_tree33 = runConfig.tree) === null || _runConfig_tree33 === void 0 ? void 0 : _runConfig_tree33.parallel) {
|
|
1795
1966
|
const { CommandValidator } = await import('../execution/CommandValidator.js');
|
|
1796
1967
|
const validation = CommandValidator.validateForParallel(commandToRun, builtInCommand);
|
|
1797
1968
|
CommandValidator.logValidation(validation);
|
|
@@ -1802,11 +1973,12 @@ const execute = async (runConfig)=>{
|
|
|
1802
1973
|
throw new Error('Command validation failed for parallel execution');
|
|
1803
1974
|
}
|
|
1804
1975
|
// Apply recommended concurrency if not explicitly set
|
|
1805
|
-
if (!runConfig.tree.maxConcurrency
|
|
1976
|
+
if (!runConfig.tree.maxConcurrency) {
|
|
1806
1977
|
const os = await import('os');
|
|
1807
|
-
const recommended = CommandValidator.getRecommendedConcurrency(builtInCommand, os.cpus().length);
|
|
1978
|
+
const recommended = CommandValidator.getRecommendedConcurrency(builtInCommand, os.cpus().length, commandToRun);
|
|
1808
1979
|
if (recommended !== os.cpus().length) {
|
|
1809
|
-
|
|
1980
|
+
const reason = builtInCommand ? builtInCommand : `custom command "${commandToRun}"`;
|
|
1981
|
+
logger.info(`💡 Using recommended concurrency for ${reason}: ${recommended}`);
|
|
1810
1982
|
runConfig.tree.maxConcurrency = recommended;
|
|
1811
1983
|
}
|
|
1812
1984
|
}
|
|
@@ -1842,7 +2014,7 @@ const execute = async (runConfig)=>{
|
|
|
1842
2014
|
// If continuing, start from where we left off
|
|
1843
2015
|
const startIndex = isContinue && executionContext ? executionContext.completedPackages.length : 0;
|
|
1844
2016
|
// Check if parallel execution is enabled
|
|
1845
|
-
if ((
|
|
2017
|
+
if ((_runConfig_tree34 = runConfig.tree) === null || _runConfig_tree34 === void 0 ? void 0 : _runConfig_tree34.parallel) {
|
|
1846
2018
|
var _runConfig_tree_retry1, _runConfig_tree_retry2, _runConfig_tree_retry3, _runConfig_tree_retry4;
|
|
1847
2019
|
logger.info('🚀 Using parallel execution mode');
|
|
1848
2020
|
// If dry run, show preview instead of executing
|
|
@@ -1875,6 +2047,7 @@ const execute = async (runConfig)=>{
|
|
|
1875
2047
|
return formattedResult;
|
|
1876
2048
|
}
|
|
1877
2049
|
// Sequential execution
|
|
2050
|
+
const executionStartTime = Date.now();
|
|
1878
2051
|
for(let i = startIndex; i < buildOrder.length; i++){
|
|
1879
2052
|
const packageName = buildOrder[i];
|
|
1880
2053
|
// Skip if already completed (in continue mode)
|
|
@@ -1977,8 +2150,18 @@ const execute = async (runConfig)=>{
|
|
|
1977
2150
|
}
|
|
1978
2151
|
}
|
|
1979
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('═══════════════════════════════════════════════════════════');
|
|
1980
2159
|
const summary = `${isDryRun ? 'DRY RUN: ' : ''}All ${buildOrder.length} packages completed successfully! 🎉`;
|
|
1981
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('');
|
|
1982
2165
|
// Clean up context on successful completion
|
|
1983
2166
|
if (isBuiltInCommand && (builtInCommand === 'publish' || builtInCommand === 'run') && !isDryRun) {
|
|
1984
2167
|
await cleanupContext(runConfig.outputDirectory);
|