@eldrforge/kodrdriv 1.2.4 → 1.2.6
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/arguments.js +3 -3
- package/dist/arguments.js.map +1 -1
- package/dist/commands/publish.js +156 -49
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/release.js +9 -9
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/tree.js +265 -47
- package/dist/commands/tree.js.map +1 -1
- package/dist/constants.js +4 -2
- package/dist/constants.js.map +1 -1
- package/dist/prompt/release.js +2 -2
- package/dist/prompt/release.js.map +1 -1
- package/dist/util/git.js +159 -8
- package/dist/util/git.js.map +1 -1
- package/dist/util/github.js +2 -2
- package/dist/util/github.js.map +1 -1
- package/package.json +2 -1
package/dist/commands/tree.js
CHANGED
|
@@ -114,6 +114,11 @@ const updateInterProjectDependencies = async (packageDir, publishedVersions, all
|
|
|
114
114
|
];
|
|
115
115
|
for (const publishedVersion of publishedVersions){
|
|
116
116
|
const { packageName, version } = publishedVersion;
|
|
117
|
+
// Do not propagate prerelease versions to consumers (often not available on registry)
|
|
118
|
+
if (typeof version === 'string' && version.includes('-')) {
|
|
119
|
+
packageLogger.verbose(`Skipping prerelease version for ${packageName}: ${version}`);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
117
122
|
// Only update if this is an inter-project dependency (exists in our build tree)
|
|
118
123
|
if (!allPackageNames.has(packageName)) {
|
|
119
124
|
continue;
|
|
@@ -222,6 +227,84 @@ const cleanupContext = async (outputDirectory)=>{
|
|
|
222
227
|
logger.warn(`Warning: Failed to cleanup execution context: ${error.message}`);
|
|
223
228
|
}
|
|
224
229
|
};
|
|
230
|
+
// Helper function to promote a package to completed status in the context
|
|
231
|
+
const promotePackageToCompleted = async (packageName, outputDirectory)=>{
|
|
232
|
+
const storage = create({
|
|
233
|
+
log: ()=>{}
|
|
234
|
+
});
|
|
235
|
+
const contextFilePath = getContextFilePath(outputDirectory);
|
|
236
|
+
try {
|
|
237
|
+
if (!await storage.exists(contextFilePath)) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const contextContent = await storage.readFile(contextFilePath, 'utf-8');
|
|
241
|
+
const contextData = safeJsonParse(contextContent, contextFilePath);
|
|
242
|
+
// Restore dates from ISO strings
|
|
243
|
+
const context = {
|
|
244
|
+
...contextData,
|
|
245
|
+
startTime: new Date(contextData.startTime),
|
|
246
|
+
lastUpdateTime: new Date(contextData.lastUpdateTime),
|
|
247
|
+
publishedVersions: contextData.publishedVersions.map((v)=>({
|
|
248
|
+
...v,
|
|
249
|
+
publishTime: new Date(v.publishTime)
|
|
250
|
+
}))
|
|
251
|
+
};
|
|
252
|
+
// Add package to completed list if not already there
|
|
253
|
+
if (!context.completedPackages.includes(packageName)) {
|
|
254
|
+
context.completedPackages.push(packageName);
|
|
255
|
+
context.lastUpdateTime = new Date();
|
|
256
|
+
await saveExecutionContext(context, outputDirectory);
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
const logger = getLogger();
|
|
260
|
+
logger.warn(`Warning: Failed to promote package to completed: ${error.message}`);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
// Helper function to validate that all packages have the required scripts
|
|
264
|
+
const validateScripts = async (packages, scripts)=>{
|
|
265
|
+
const logger = getLogger();
|
|
266
|
+
const missingScripts = new Map();
|
|
267
|
+
const storage = create({
|
|
268
|
+
log: ()=>{}
|
|
269
|
+
});
|
|
270
|
+
logger.debug(`Validating scripts: ${scripts.join(', ')}`);
|
|
271
|
+
for (const [packageName, packageInfo] of packages){
|
|
272
|
+
const packageJsonPath = path__default.join(packageInfo.path, 'package.json');
|
|
273
|
+
const missingForPackage = [];
|
|
274
|
+
try {
|
|
275
|
+
const packageJsonContent = await storage.readFile(packageJsonPath, 'utf-8');
|
|
276
|
+
const packageJson = safeJsonParse(packageJsonContent, packageJsonPath);
|
|
277
|
+
const validated = validatePackageJson(packageJson, packageJsonPath);
|
|
278
|
+
// Check if each required script exists
|
|
279
|
+
for (const script of scripts){
|
|
280
|
+
if (!validated.scripts || !validated.scripts[script]) {
|
|
281
|
+
missingForPackage.push(script);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (missingForPackage.length > 0) {
|
|
285
|
+
missingScripts.set(packageName, missingForPackage);
|
|
286
|
+
logger.debug(`Package ${packageName} missing scripts: ${missingForPackage.join(', ')}`);
|
|
287
|
+
}
|
|
288
|
+
} catch (error) {
|
|
289
|
+
logger.debug(`Error reading package.json for ${packageName}: ${error.message}`);
|
|
290
|
+
// If we can't read the package.json, assume all scripts are missing
|
|
291
|
+
missingScripts.set(packageName, scripts);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const valid = missingScripts.size === 0;
|
|
295
|
+
if (valid) {
|
|
296
|
+
logger.info(`✅ All packages have the required scripts: ${scripts.join(', ')}`);
|
|
297
|
+
} else {
|
|
298
|
+
logger.error(`❌ Script validation failed. Missing scripts:`);
|
|
299
|
+
for (const [packageName, missing] of missingScripts){
|
|
300
|
+
logger.error(` ${packageName}: ${missing.join(', ')}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
valid,
|
|
305
|
+
missingScripts
|
|
306
|
+
};
|
|
307
|
+
};
|
|
225
308
|
// Extract published version from package.json after successful publish
|
|
226
309
|
const extractPublishedVersion = async (packageDir, packageLogger)=>{
|
|
227
310
|
const storage = create({
|
|
@@ -313,7 +396,7 @@ const createPackageLogger = (packageName, sequenceNumber, totalCount, isDryRun =
|
|
|
313
396
|
};
|
|
314
397
|
};
|
|
315
398
|
// Helper function to format subproject error output
|
|
316
|
-
const formatSubprojectError = (packageName, error)=>{
|
|
399
|
+
const formatSubprojectError = (packageName, error, _packageInfo, _position, _total)=>{
|
|
317
400
|
const lines = [];
|
|
318
401
|
lines.push(`❌ Command failed in package ${packageName}:`);
|
|
319
402
|
// Format the main error message with indentation
|
|
@@ -592,6 +675,7 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
592
675
|
}
|
|
593
676
|
// For built-in commands, shell out to a separate kodrdriv process
|
|
594
677
|
// This preserves individual project configurations
|
|
678
|
+
let publishWasSkipped;
|
|
595
679
|
if (isBuiltInCommand) {
|
|
596
680
|
// Extract the command name from "kodrdriv <command>"
|
|
597
681
|
const builtInCommandName = commandToRun.replace('kodrdriv ', '');
|
|
@@ -601,28 +685,39 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
601
685
|
// Ensure dry-run propagates to subprocess even during overall dry-run mode
|
|
602
686
|
const effectiveCommand = runConfig.dryRun && !commandToRun.includes('--dry-run') ? `${commandToRun} --dry-run` : commandToRun;
|
|
603
687
|
// Use runWithLogging for built-in commands to capture all output
|
|
604
|
-
await runWithLogging(effectiveCommand, packageLogger, {}, showOutput);
|
|
688
|
+
const { stdout } = await runWithLogging(effectiveCommand, packageLogger, {}, showOutput);
|
|
689
|
+
// Detect explicit skip marker from publish to avoid propagating versions
|
|
690
|
+
if (builtInCommandName === 'publish' && stdout && stdout.includes('KODRDRIV_PUBLISH_SKIPPED')) {
|
|
691
|
+
packageLogger.info('Publish skipped for this package; will not record or propagate a version.');
|
|
692
|
+
publishWasSkipped = true;
|
|
693
|
+
}
|
|
605
694
|
} else {
|
|
606
695
|
// For custom commands, use the existing logic
|
|
607
696
|
await runWithLogging(commandToRun, packageLogger, {}, showOutput);
|
|
608
697
|
}
|
|
609
698
|
// Track published version after successful publish (skip during dry run)
|
|
610
699
|
if (!isDryRun && isBuiltInCommand && commandToRun.includes('publish')) {
|
|
611
|
-
|
|
612
|
-
if (
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
700
|
+
// If publish was skipped, do not record a version
|
|
701
|
+
if (publishWasSkipped) {
|
|
702
|
+
packageLogger.verbose('Skipping version tracking due to earlier skip.');
|
|
703
|
+
} else {
|
|
704
|
+
// Only record a published version if a new tag exists (avoid recording for skipped publishes)
|
|
705
|
+
const publishedVersion = await extractPublishedVersion(packageDir, packageLogger);
|
|
706
|
+
if (publishedVersion) {
|
|
707
|
+
let mutexLocked = false;
|
|
708
|
+
try {
|
|
709
|
+
await globalStateMutex.lock();
|
|
710
|
+
mutexLocked = true;
|
|
711
|
+
publishedVersions.push(publishedVersion);
|
|
712
|
+
packageLogger.info(`Tracked published version: ${publishedVersion.packageName}@${publishedVersion.version}`);
|
|
623
713
|
globalStateMutex.unlock();
|
|
714
|
+
mutexLocked = false;
|
|
715
|
+
} catch (error) {
|
|
716
|
+
if (mutexLocked) {
|
|
717
|
+
globalStateMutex.unlock();
|
|
718
|
+
}
|
|
719
|
+
throw error;
|
|
624
720
|
}
|
|
625
|
-
throw error;
|
|
626
721
|
}
|
|
627
722
|
}
|
|
628
723
|
}
|
|
@@ -654,22 +749,36 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
654
749
|
success: true
|
|
655
750
|
};
|
|
656
751
|
} catch (error) {
|
|
752
|
+
var _error_message;
|
|
657
753
|
if (runConfig.debug || runConfig.verbose) {
|
|
658
754
|
packageLogger.error(`❌ Execution failed: ${error.message}`);
|
|
659
755
|
} else {
|
|
660
756
|
logger.error(`[${index + 1}/${total}] ${packageName}: ❌ Failed - ${error.message}`);
|
|
661
757
|
}
|
|
758
|
+
// Check if this is a timeout error
|
|
759
|
+
const errorMessage = ((_error_message = error.message) === null || _error_message === void 0 ? void 0 : _error_message.toLowerCase()) || '';
|
|
760
|
+
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'));
|
|
662
761
|
return {
|
|
663
762
|
success: false,
|
|
664
|
-
error
|
|
763
|
+
error,
|
|
764
|
+
isTimeoutError
|
|
665
765
|
};
|
|
666
766
|
}
|
|
667
767
|
};
|
|
668
768
|
const execute = async (runConfig)=>{
|
|
669
|
-
var _runConfig_tree, _runConfig_tree1, _runConfig_tree2, _runConfig_tree3, _runConfig_tree4;
|
|
769
|
+
var _runConfig_tree, _runConfig_tree1, _runConfig_tree2, _runConfig_tree3, _runConfig_tree4, _runConfig_tree5;
|
|
670
770
|
const logger = getLogger();
|
|
671
771
|
const isDryRun = runConfig.dryRun || false;
|
|
672
772
|
const isContinue = ((_runConfig_tree = runConfig.tree) === null || _runConfig_tree === void 0 ? void 0 : _runConfig_tree.continue) || false;
|
|
773
|
+
const promotePackage = (_runConfig_tree1 = runConfig.tree) === null || _runConfig_tree1 === void 0 ? void 0 : _runConfig_tree1.promote;
|
|
774
|
+
// Handle promote mode
|
|
775
|
+
if (promotePackage) {
|
|
776
|
+
logger.info(`Promoting package '${promotePackage}' to completed status...`);
|
|
777
|
+
await promotePackageToCompleted(promotePackage, runConfig.outputDirectory);
|
|
778
|
+
logger.info(`✅ Package '${promotePackage}' has been marked as completed.`);
|
|
779
|
+
logger.info('You can now run the tree command with --continue to resume from the next package.');
|
|
780
|
+
return `Package '${promotePackage}' promoted to completed status.`;
|
|
781
|
+
}
|
|
673
782
|
// Handle continue mode
|
|
674
783
|
if (isContinue) {
|
|
675
784
|
const savedContext = await loadExecutionContext(runConfig.outputDirectory);
|
|
@@ -708,24 +817,50 @@ const execute = async (runConfig)=>{
|
|
|
708
817
|
executionContext = null;
|
|
709
818
|
}
|
|
710
819
|
// Check if we're in built-in command mode (tree command with second argument)
|
|
711
|
-
const builtInCommand = (
|
|
820
|
+
const builtInCommand = (_runConfig_tree2 = runConfig.tree) === null || _runConfig_tree2 === void 0 ? void 0 : _runConfig_tree2.builtInCommand;
|
|
712
821
|
const supportedBuiltInCommands = [
|
|
713
822
|
'commit',
|
|
714
823
|
'publish',
|
|
715
824
|
'link',
|
|
716
825
|
'unlink',
|
|
717
826
|
'development',
|
|
718
|
-
'branches'
|
|
827
|
+
'branches',
|
|
828
|
+
'run'
|
|
719
829
|
];
|
|
720
830
|
if (builtInCommand && !supportedBuiltInCommands.includes(builtInCommand)) {
|
|
721
831
|
throw new Error(`Unsupported built-in command: ${builtInCommand}. Supported commands: ${supportedBuiltInCommands.join(', ')}`);
|
|
722
832
|
}
|
|
833
|
+
// Handle run subcommand - convert space-separated scripts to npm run commands
|
|
834
|
+
if (builtInCommand === 'run') {
|
|
835
|
+
var _runConfig_tree6;
|
|
836
|
+
const packageArgument = (_runConfig_tree6 = runConfig.tree) === null || _runConfig_tree6 === void 0 ? void 0 : _runConfig_tree6.packageArgument;
|
|
837
|
+
if (!packageArgument) {
|
|
838
|
+
throw new Error('run subcommand requires script names. Usage: kodrdriv tree run "clean build test"');
|
|
839
|
+
}
|
|
840
|
+
// Split the package argument by spaces to get individual script names
|
|
841
|
+
const scripts = packageArgument.trim().split(/\s+/).filter((script)=>script.length > 0);
|
|
842
|
+
if (scripts.length === 0) {
|
|
843
|
+
throw new Error('run subcommand requires at least one script name. Usage: kodrdriv tree run "clean build test"');
|
|
844
|
+
}
|
|
845
|
+
// Convert to npm run commands joined with &&
|
|
846
|
+
const npmCommands = scripts.map((script)=>`npm run ${script}`).join(' && ');
|
|
847
|
+
// Set this as the custom command to run
|
|
848
|
+
runConfig.tree = {
|
|
849
|
+
...runConfig.tree,
|
|
850
|
+
cmd: npmCommands
|
|
851
|
+
};
|
|
852
|
+
// Clear the built-in command since we're now using custom command mode
|
|
853
|
+
runConfig.tree.builtInCommand = undefined;
|
|
854
|
+
logger.info(`Converting run subcommand to: ${npmCommands}`);
|
|
855
|
+
// Store scripts for later validation
|
|
856
|
+
runConfig.__scriptsToValidate = scripts;
|
|
857
|
+
}
|
|
723
858
|
// Determine the target directories - either specified or current working directory
|
|
724
|
-
const directories = ((
|
|
859
|
+
const directories = ((_runConfig_tree3 = runConfig.tree) === null || _runConfig_tree3 === void 0 ? void 0 : _runConfig_tree3.directories) || [
|
|
725
860
|
process.cwd()
|
|
726
861
|
];
|
|
727
862
|
// Handle link status subcommand
|
|
728
|
-
if (builtInCommand === 'link' && ((
|
|
863
|
+
if (builtInCommand === 'link' && ((_runConfig_tree4 = runConfig.tree) === null || _runConfig_tree4 === void 0 ? void 0 : _runConfig_tree4.packageArgument) === 'status') {
|
|
729
864
|
// For tree link status, we want to show status across all packages
|
|
730
865
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Running link status across workspace...`);
|
|
731
866
|
// Create a config that will be passed to the link command
|
|
@@ -745,7 +880,7 @@ const execute = async (runConfig)=>{
|
|
|
745
880
|
}
|
|
746
881
|
}
|
|
747
882
|
// Handle unlink status subcommand
|
|
748
|
-
if (builtInCommand === 'unlink' && ((
|
|
883
|
+
if (builtInCommand === 'unlink' && ((_runConfig_tree5 = runConfig.tree) === null || _runConfig_tree5 === void 0 ? void 0 : _runConfig_tree5.packageArgument) === 'status') {
|
|
749
884
|
// For tree unlink status, we want to show status across all packages
|
|
750
885
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Running unlink status across workspace...`);
|
|
751
886
|
// Create a config that will be passed to the unlink command
|
|
@@ -770,9 +905,9 @@ const execute = async (runConfig)=>{
|
|
|
770
905
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Analyzing workspaces at: ${directories.join(', ')}`);
|
|
771
906
|
}
|
|
772
907
|
try {
|
|
773
|
-
var
|
|
908
|
+
var _runConfig_tree7, _runConfig_tree8, _runConfig_tree9, _runConfig_tree10;
|
|
774
909
|
// Get exclusion patterns from config, fallback to empty array
|
|
775
|
-
const excludedPatterns = ((
|
|
910
|
+
const excludedPatterns = ((_runConfig_tree7 = runConfig.tree) === null || _runConfig_tree7 === void 0 ? void 0 : _runConfig_tree7.exclude) || [];
|
|
776
911
|
if (excludedPatterns.length > 0) {
|
|
777
912
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Using exclusion patterns: ${excludedPatterns.join(', ')}`);
|
|
778
913
|
}
|
|
@@ -799,16 +934,19 @@ const execute = async (runConfig)=>{
|
|
|
799
934
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Determining build order...`);
|
|
800
935
|
let buildOrder = topologicalSort(dependencyGraph);
|
|
801
936
|
// Handle start-from functionality if specified
|
|
802
|
-
const startFrom = (
|
|
937
|
+
const startFrom = (_runConfig_tree8 = runConfig.tree) === null || _runConfig_tree8 === void 0 ? void 0 : _runConfig_tree8.startFrom;
|
|
803
938
|
if (startFrom) {
|
|
804
939
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Looking for start package: ${startFrom}`);
|
|
805
|
-
//
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
const dirName = path__default.basename(
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
940
|
+
// Resolve the actual package name (can be package name or directory name)
|
|
941
|
+
let startPackageName = null;
|
|
942
|
+
for (const [pkgName, pkgInfo] of dependencyGraph.packages){
|
|
943
|
+
const dirName = path__default.basename(pkgInfo.path);
|
|
944
|
+
if (dirName === startFrom || pkgName === startFrom) {
|
|
945
|
+
startPackageName = pkgName;
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
if (!startPackageName) {
|
|
812
950
|
// Check if the package exists but was excluded across all directories
|
|
813
951
|
let allPackageJsonPathsForCheck = [];
|
|
814
952
|
for (const targetDirectory of directories){
|
|
@@ -842,14 +980,18 @@ const execute = async (runConfig)=>{
|
|
|
842
980
|
throw new Error(`Package directory '${startFrom}' not found. Available packages: ${availablePackages}`);
|
|
843
981
|
}
|
|
844
982
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
if (
|
|
848
|
-
|
|
983
|
+
// Find the start package in the build order and start execution from there
|
|
984
|
+
const startIndex = buildOrder.findIndex((pkgName)=>pkgName === startPackageName);
|
|
985
|
+
if (startIndex === -1) {
|
|
986
|
+
throw new Error(`Package '${startFrom}' not found in build order. This should not happen.`);
|
|
849
987
|
}
|
|
988
|
+
// Filter build order to start from the specified package
|
|
989
|
+
const originalLength = buildOrder.length;
|
|
990
|
+
buildOrder = buildOrder.slice(startIndex);
|
|
991
|
+
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Starting execution from package '${startFrom}' (${buildOrder.length} of ${originalLength} packages remaining).`);
|
|
850
992
|
}
|
|
851
993
|
// Handle stop-at functionality if specified
|
|
852
|
-
const stopAt = (
|
|
994
|
+
const stopAt = (_runConfig_tree9 = runConfig.tree) === null || _runConfig_tree9 === void 0 ? void 0 : _runConfig_tree9.stopAt;
|
|
853
995
|
if (stopAt) {
|
|
854
996
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Looking for stop package: ${stopAt}`);
|
|
855
997
|
// Find the package that matches the stopAt directory name
|
|
@@ -1208,12 +1350,12 @@ const execute = async (runConfig)=>{
|
|
|
1208
1350
|
returnOutput = `\nBuild order: ${buildOrder.join(' → ')}\n`;
|
|
1209
1351
|
}
|
|
1210
1352
|
// Execute command if provided (custom command or built-in command)
|
|
1211
|
-
const cmd = (
|
|
1353
|
+
const cmd = (_runConfig_tree10 = runConfig.tree) === null || _runConfig_tree10 === void 0 ? void 0 : _runConfig_tree10.cmd;
|
|
1212
1354
|
// Determine command to execute
|
|
1213
1355
|
let commandToRun;
|
|
1214
1356
|
let isBuiltInCommand = false;
|
|
1215
1357
|
if (builtInCommand) {
|
|
1216
|
-
var
|
|
1358
|
+
var _runConfig_tree11, _runConfig_tree12, _runConfig_tree13;
|
|
1217
1359
|
// Built-in command mode: shell out to kodrdriv subprocess
|
|
1218
1360
|
// Build command with propagated global options
|
|
1219
1361
|
const globalOptions = [];
|
|
@@ -1230,14 +1372,14 @@ const execute = async (runConfig)=>{
|
|
|
1230
1372
|
// Build the command with global options
|
|
1231
1373
|
const optionsString = globalOptions.length > 0 ? ` ${globalOptions.join(' ')}` : '';
|
|
1232
1374
|
// Add package argument for link/unlink commands
|
|
1233
|
-
const packageArg = (
|
|
1375
|
+
const packageArg = (_runConfig_tree11 = runConfig.tree) === null || _runConfig_tree11 === void 0 ? void 0 : _runConfig_tree11.packageArgument;
|
|
1234
1376
|
const packageArgString = packageArg && (builtInCommand === 'link' || builtInCommand === 'unlink') ? ` "${packageArg}"` : '';
|
|
1235
1377
|
// Add command-specific options
|
|
1236
1378
|
let commandSpecificOptions = '';
|
|
1237
|
-
if (builtInCommand === 'unlink' && ((
|
|
1379
|
+
if (builtInCommand === 'unlink' && ((_runConfig_tree12 = runConfig.tree) === null || _runConfig_tree12 === void 0 ? void 0 : _runConfig_tree12.cleanNodeModules)) {
|
|
1238
1380
|
commandSpecificOptions += ' --clean-node-modules';
|
|
1239
1381
|
}
|
|
1240
|
-
if ((builtInCommand === 'link' || builtInCommand === 'unlink') && ((
|
|
1382
|
+
if ((builtInCommand === 'link' || builtInCommand === 'unlink') && ((_runConfig_tree13 = runConfig.tree) === null || _runConfig_tree13 === void 0 ? void 0 : _runConfig_tree13.externals) && runConfig.tree.externals.length > 0) {
|
|
1241
1383
|
commandSpecificOptions += ` --externals ${runConfig.tree.externals.join(' ')}`;
|
|
1242
1384
|
}
|
|
1243
1385
|
commandToRun = `kodrdriv ${builtInCommand}${optionsString}${packageArgString}${commandSpecificOptions}`;
|
|
@@ -1247,6 +1389,23 @@ const execute = async (runConfig)=>{
|
|
|
1247
1389
|
commandToRun = cmd;
|
|
1248
1390
|
}
|
|
1249
1391
|
if (commandToRun) {
|
|
1392
|
+
// Validate scripts for run command before execution
|
|
1393
|
+
const scriptsToValidate = runConfig.__scriptsToValidate;
|
|
1394
|
+
if (scriptsToValidate && scriptsToValidate.length > 0) {
|
|
1395
|
+
logger.info(`🔍 Validating scripts before execution: ${scriptsToValidate.join(', ')}`);
|
|
1396
|
+
const validation = await validateScripts(dependencyGraph.packages, scriptsToValidate);
|
|
1397
|
+
if (!validation.valid) {
|
|
1398
|
+
logger.error('');
|
|
1399
|
+
logger.error('❌ Script validation failed. Cannot proceed with execution.');
|
|
1400
|
+
logger.error('');
|
|
1401
|
+
logger.error('💡 To fix this:');
|
|
1402
|
+
logger.error(' 1. Add the missing scripts to the package.json files');
|
|
1403
|
+
logger.error(' 2. Or exclude packages that don\'t need these scripts using --exclude');
|
|
1404
|
+
logger.error(' 3. Or run individual packages that have the required scripts');
|
|
1405
|
+
logger.error('');
|
|
1406
|
+
throw new Error('Script validation failed. See details above.');
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1250
1409
|
// Create set of all package names for inter-project dependency detection
|
|
1251
1410
|
const allPackageNames = new Set(Array.from(dependencyGraph.packages.keys()));
|
|
1252
1411
|
// Initialize execution context if not continuing
|
|
@@ -1260,8 +1419,8 @@ const execute = async (runConfig)=>{
|
|
|
1260
1419
|
startTime: new Date(),
|
|
1261
1420
|
lastUpdateTime: new Date()
|
|
1262
1421
|
};
|
|
1263
|
-
// Save initial context
|
|
1264
|
-
if (isBuiltInCommand && builtInCommand === 'publish' && !isDryRun) {
|
|
1422
|
+
// Save initial context for commands that support continuation
|
|
1423
|
+
if (isBuiltInCommand && (builtInCommand === 'publish' || builtInCommand === 'run') && !isDryRun) {
|
|
1265
1424
|
await saveExecutionContext(executionContext, runConfig.outputDirectory);
|
|
1266
1425
|
}
|
|
1267
1426
|
}
|
|
@@ -1291,7 +1450,7 @@ const execute = async (runConfig)=>{
|
|
|
1291
1450
|
if (result.success) {
|
|
1292
1451
|
successCount++;
|
|
1293
1452
|
// Update context
|
|
1294
|
-
if (executionContext && isBuiltInCommand && builtInCommand === 'publish' && !isDryRun) {
|
|
1453
|
+
if (executionContext && isBuiltInCommand && (builtInCommand === 'publish' || builtInCommand === 'run') && !isDryRun) {
|
|
1295
1454
|
executionContext.completedPackages.push(packageName);
|
|
1296
1455
|
executionContext.publishedVersions = publishedVersions;
|
|
1297
1456
|
executionContext.lastUpdateTime = new Date();
|
|
@@ -1304,17 +1463,76 @@ const execute = async (runConfig)=>{
|
|
|
1304
1463
|
}
|
|
1305
1464
|
} else {
|
|
1306
1465
|
failedPackage = packageName;
|
|
1307
|
-
const formattedError = formatSubprojectError(packageName, result.error);
|
|
1466
|
+
const formattedError = formatSubprojectError(packageName, result.error, packageInfo, i + 1, buildOrder.length);
|
|
1308
1467
|
if (!isDryRun) {
|
|
1468
|
+
var _result_error;
|
|
1309
1469
|
packageLogger.error(`Execution failed`);
|
|
1310
1470
|
logger.error(formattedError);
|
|
1311
1471
|
logger.error(`Failed after ${successCount} successful packages.`);
|
|
1472
|
+
// Special handling for timeout errors
|
|
1473
|
+
if (result.isTimeoutError) {
|
|
1474
|
+
logger.error('');
|
|
1475
|
+
logger.error('⏰ TIMEOUT DETECTED: This appears to be a timeout error.');
|
|
1476
|
+
logger.error(' This commonly happens when PR checks take longer than expected.');
|
|
1477
|
+
logger.error(' The execution context has been saved for recovery.');
|
|
1478
|
+
logger.error('');
|
|
1479
|
+
// Save context even on timeout for recovery
|
|
1480
|
+
if (executionContext && isBuiltInCommand && (builtInCommand === 'publish' || builtInCommand === 'run')) {
|
|
1481
|
+
executionContext.completedPackages.push(packageName);
|
|
1482
|
+
executionContext.publishedVersions = publishedVersions;
|
|
1483
|
+
executionContext.lastUpdateTime = new Date();
|
|
1484
|
+
await saveExecutionContext(executionContext, runConfig.outputDirectory);
|
|
1485
|
+
logger.info('💾 Execution context saved for recovery.');
|
|
1486
|
+
}
|
|
1487
|
+
// For publish commands, provide specific guidance about CI/CD setup
|
|
1488
|
+
if (builtInCommand === 'publish') {
|
|
1489
|
+
logger.error('');
|
|
1490
|
+
logger.error('💡 PUBLISH TIMEOUT TROUBLESHOOTING:');
|
|
1491
|
+
logger.error(' This project may not have CI/CD workflows configured.');
|
|
1492
|
+
logger.error(' Common solutions:');
|
|
1493
|
+
logger.error(' 1. Set up GitHub Actions workflows for this repository');
|
|
1494
|
+
logger.error(' 2. Use --sendit flag to skip user confirmation:');
|
|
1495
|
+
logger.error(` kodrdriv tree publish --sendit`);
|
|
1496
|
+
logger.error(' 3. Or manually promote this package:');
|
|
1497
|
+
logger.error(` kodrdriv tree publish --promote ${packageName}`);
|
|
1498
|
+
logger.error('');
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1312
1501
|
logger.error(`To resume from this point, run:`);
|
|
1313
1502
|
if (isBuiltInCommand) {
|
|
1314
1503
|
logger.error(` kodrdriv tree ${builtInCommand} --continue`);
|
|
1315
1504
|
} else {
|
|
1316
1505
|
logger.error(` kodrdriv tree --continue --cmd "${commandToRun}"`);
|
|
1317
1506
|
}
|
|
1507
|
+
// For timeout errors, provide additional recovery instructions
|
|
1508
|
+
if (result.isTimeoutError) {
|
|
1509
|
+
logger.error('');
|
|
1510
|
+
logger.error('🔧 RECOVERY OPTIONS:');
|
|
1511
|
+
if (builtInCommand === 'publish') {
|
|
1512
|
+
logger.error(' 1. Wait for the PR checks to complete, then run:');
|
|
1513
|
+
logger.error(` cd ${packageInfo.path}`);
|
|
1514
|
+
logger.error(` kodrdriv publish`);
|
|
1515
|
+
logger.error(' 2. After the individual publish completes, run:');
|
|
1516
|
+
logger.error(` kodrdriv tree ${builtInCommand} --continue`);
|
|
1517
|
+
} else {
|
|
1518
|
+
logger.error(' 1. Fix any issues in the package, then run:');
|
|
1519
|
+
logger.error(` cd ${packageInfo.path}`);
|
|
1520
|
+
logger.error(` ${commandToRun}`);
|
|
1521
|
+
logger.error(' 2. After the command completes successfully, run:');
|
|
1522
|
+
logger.error(` kodrdriv tree ${builtInCommand} --continue`);
|
|
1523
|
+
}
|
|
1524
|
+
logger.error(' 3. Or promote this package to completed status:');
|
|
1525
|
+
logger.error(` kodrdriv tree ${builtInCommand} --promote ${packageName}`);
|
|
1526
|
+
logger.error(' 4. Or manually edit .kodrdriv-context to mark this package as completed');
|
|
1527
|
+
}
|
|
1528
|
+
// Add clear error summary at the very end
|
|
1529
|
+
logger.error('');
|
|
1530
|
+
logger.error('📋 ERROR SUMMARY:');
|
|
1531
|
+
logger.error(` Project that failed: ${packageName}`);
|
|
1532
|
+
logger.error(` Location: ${packageInfo.path}`);
|
|
1533
|
+
logger.error(` Position in tree: ${i + 1} of ${buildOrder.length} packages`);
|
|
1534
|
+
logger.error(` What failed: ${((_result_error = result.error) === null || _result_error === void 0 ? void 0 : _result_error.message) || 'Unknown error'}`);
|
|
1535
|
+
logger.error('');
|
|
1318
1536
|
throw new Error(`Command failed in package ${packageName}`);
|
|
1319
1537
|
}
|
|
1320
1538
|
break;
|
|
@@ -1324,7 +1542,7 @@ const execute = async (runConfig)=>{
|
|
|
1324
1542
|
const summary = `${isDryRun ? 'DRY RUN: ' : ''}All ${buildOrder.length} packages completed successfully! 🎉`;
|
|
1325
1543
|
logger.info(summary);
|
|
1326
1544
|
// Clean up context on successful completion
|
|
1327
|
-
if (isBuiltInCommand && builtInCommand === 'publish' && !isDryRun) {
|
|
1545
|
+
if (isBuiltInCommand && (builtInCommand === 'publish' || builtInCommand === 'run') && !isDryRun) {
|
|
1328
1546
|
await cleanupContext(runConfig.outputDirectory);
|
|
1329
1547
|
}
|
|
1330
1548
|
return returnOutput; // Don't duplicate the summary in return string
|