@eldrforge/kodrdriv 1.2.5 → 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 +27 -21
- 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 +224 -75
- 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
|
@@ -227,6 +227,84 @@ const cleanupContext = async (outputDirectory)=>{
|
|
|
227
227
|
logger.warn(`Warning: Failed to cleanup execution context: ${error.message}`);
|
|
228
228
|
}
|
|
229
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
|
+
};
|
|
230
308
|
// Extract published version from package.json after successful publish
|
|
231
309
|
const extractPublishedVersion = async (packageDir, packageLogger)=>{
|
|
232
310
|
const storage = create({
|
|
@@ -318,7 +396,7 @@ const createPackageLogger = (packageName, sequenceNumber, totalCount, isDryRun =
|
|
|
318
396
|
};
|
|
319
397
|
};
|
|
320
398
|
// Helper function to format subproject error output
|
|
321
|
-
const formatSubprojectError = (packageName, error)=>{
|
|
399
|
+
const formatSubprojectError = (packageName, error, _packageInfo, _position, _total)=>{
|
|
322
400
|
const lines = [];
|
|
323
401
|
lines.push(`❌ Command failed in package ${packageName}:`);
|
|
324
402
|
// Format the main error message with indentation
|
|
@@ -671,22 +749,36 @@ const executePackage = async (packageName, packageInfo, commandToRun, runConfig,
|
|
|
671
749
|
success: true
|
|
672
750
|
};
|
|
673
751
|
} catch (error) {
|
|
752
|
+
var _error_message;
|
|
674
753
|
if (runConfig.debug || runConfig.verbose) {
|
|
675
754
|
packageLogger.error(`❌ Execution failed: ${error.message}`);
|
|
676
755
|
} else {
|
|
677
756
|
logger.error(`[${index + 1}/${total}] ${packageName}: ❌ Failed - ${error.message}`);
|
|
678
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'));
|
|
679
761
|
return {
|
|
680
762
|
success: false,
|
|
681
|
-
error
|
|
763
|
+
error,
|
|
764
|
+
isTimeoutError
|
|
682
765
|
};
|
|
683
766
|
}
|
|
684
767
|
};
|
|
685
768
|
const execute = async (runConfig)=>{
|
|
686
|
-
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;
|
|
687
770
|
const logger = getLogger();
|
|
688
771
|
const isDryRun = runConfig.dryRun || false;
|
|
689
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
|
+
}
|
|
690
782
|
// Handle continue mode
|
|
691
783
|
if (isContinue) {
|
|
692
784
|
const savedContext = await loadExecutionContext(runConfig.outputDirectory);
|
|
@@ -725,24 +817,50 @@ const execute = async (runConfig)=>{
|
|
|
725
817
|
executionContext = null;
|
|
726
818
|
}
|
|
727
819
|
// Check if we're in built-in command mode (tree command with second argument)
|
|
728
|
-
const builtInCommand = (
|
|
820
|
+
const builtInCommand = (_runConfig_tree2 = runConfig.tree) === null || _runConfig_tree2 === void 0 ? void 0 : _runConfig_tree2.builtInCommand;
|
|
729
821
|
const supportedBuiltInCommands = [
|
|
730
822
|
'commit',
|
|
731
823
|
'publish',
|
|
732
824
|
'link',
|
|
733
825
|
'unlink',
|
|
734
826
|
'development',
|
|
735
|
-
'branches'
|
|
827
|
+
'branches',
|
|
828
|
+
'run'
|
|
736
829
|
];
|
|
737
830
|
if (builtInCommand && !supportedBuiltInCommands.includes(builtInCommand)) {
|
|
738
831
|
throw new Error(`Unsupported built-in command: ${builtInCommand}. Supported commands: ${supportedBuiltInCommands.join(', ')}`);
|
|
739
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
|
+
}
|
|
740
858
|
// Determine the target directories - either specified or current working directory
|
|
741
|
-
const directories = ((
|
|
859
|
+
const directories = ((_runConfig_tree3 = runConfig.tree) === null || _runConfig_tree3 === void 0 ? void 0 : _runConfig_tree3.directories) || [
|
|
742
860
|
process.cwd()
|
|
743
861
|
];
|
|
744
862
|
// Handle link status subcommand
|
|
745
|
-
if (builtInCommand === 'link' && ((
|
|
863
|
+
if (builtInCommand === 'link' && ((_runConfig_tree4 = runConfig.tree) === null || _runConfig_tree4 === void 0 ? void 0 : _runConfig_tree4.packageArgument) === 'status') {
|
|
746
864
|
// For tree link status, we want to show status across all packages
|
|
747
865
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Running link status across workspace...`);
|
|
748
866
|
// Create a config that will be passed to the link command
|
|
@@ -762,7 +880,7 @@ const execute = async (runConfig)=>{
|
|
|
762
880
|
}
|
|
763
881
|
}
|
|
764
882
|
// Handle unlink status subcommand
|
|
765
|
-
if (builtInCommand === 'unlink' && ((
|
|
883
|
+
if (builtInCommand === 'unlink' && ((_runConfig_tree5 = runConfig.tree) === null || _runConfig_tree5 === void 0 ? void 0 : _runConfig_tree5.packageArgument) === 'status') {
|
|
766
884
|
// For tree unlink status, we want to show status across all packages
|
|
767
885
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Running unlink status across workspace...`);
|
|
768
886
|
// Create a config that will be passed to the unlink command
|
|
@@ -787,9 +905,9 @@ const execute = async (runConfig)=>{
|
|
|
787
905
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Analyzing workspaces at: ${directories.join(', ')}`);
|
|
788
906
|
}
|
|
789
907
|
try {
|
|
790
|
-
var
|
|
908
|
+
var _runConfig_tree7, _runConfig_tree8, _runConfig_tree9, _runConfig_tree10;
|
|
791
909
|
// Get exclusion patterns from config, fallback to empty array
|
|
792
|
-
const excludedPatterns = ((
|
|
910
|
+
const excludedPatterns = ((_runConfig_tree7 = runConfig.tree) === null || _runConfig_tree7 === void 0 ? void 0 : _runConfig_tree7.exclude) || [];
|
|
793
911
|
if (excludedPatterns.length > 0) {
|
|
794
912
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Using exclusion patterns: ${excludedPatterns.join(', ')}`);
|
|
795
913
|
}
|
|
@@ -816,7 +934,7 @@ const execute = async (runConfig)=>{
|
|
|
816
934
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Determining build order...`);
|
|
817
935
|
let buildOrder = topologicalSort(dependencyGraph);
|
|
818
936
|
// Handle start-from functionality if specified
|
|
819
|
-
const startFrom = (
|
|
937
|
+
const startFrom = (_runConfig_tree8 = runConfig.tree) === null || _runConfig_tree8 === void 0 ? void 0 : _runConfig_tree8.startFrom;
|
|
820
938
|
if (startFrom) {
|
|
821
939
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Looking for start package: ${startFrom}`);
|
|
822
940
|
// Resolve the actual package name (can be package name or directory name)
|
|
@@ -862,63 +980,18 @@ const execute = async (runConfig)=>{
|
|
|
862
980
|
throw new Error(`Package directory '${startFrom}' not found. Available packages: ${availablePackages}`);
|
|
863
981
|
}
|
|
864
982
|
}
|
|
865
|
-
//
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
if (!reverseEdges.has(dep)) reverseEdges.set(dep, new Set());
|
|
870
|
-
reverseEdges.get(dep).add(pkg);
|
|
871
|
-
}
|
|
872
|
-
if (!reverseEdges.has(pkg)) reverseEdges.set(pkg, new Set());
|
|
873
|
-
}
|
|
874
|
-
// Step 1: collect the start package and all its transitive dependents (consumers)
|
|
875
|
-
const dependentsClosure = new Set();
|
|
876
|
-
const queueDependents = [
|
|
877
|
-
startPackageName
|
|
878
|
-
];
|
|
879
|
-
while(queueDependents.length > 0){
|
|
880
|
-
const current = queueDependents.shift();
|
|
881
|
-
if (dependentsClosure.has(current)) continue;
|
|
882
|
-
dependentsClosure.add(current);
|
|
883
|
-
const consumers = reverseEdges.get(current) || new Set();
|
|
884
|
-
for (const consumer of consumers){
|
|
885
|
-
if (!dependentsClosure.has(consumer)) queueDependents.push(consumer);
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
// Step 2: expand to include all forward dependencies required to build those packages
|
|
889
|
-
const relevantPackages = new Set(dependentsClosure);
|
|
890
|
-
const queueDependencies = Array.from(relevantPackages);
|
|
891
|
-
while(queueDependencies.length > 0){
|
|
892
|
-
const current = queueDependencies.shift();
|
|
893
|
-
const deps = dependencyGraph.edges.get(current) || new Set();
|
|
894
|
-
for (const dep of deps){
|
|
895
|
-
if (!relevantPackages.has(dep)) {
|
|
896
|
-
relevantPackages.add(dep);
|
|
897
|
-
queueDependencies.push(dep);
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
// Filter graph to only relevant packages
|
|
902
|
-
const filteredGraph = {
|
|
903
|
-
packages: new Map(),
|
|
904
|
-
edges: new Map()
|
|
905
|
-
};
|
|
906
|
-
for (const pkgName of relevantPackages){
|
|
907
|
-
const info = dependencyGraph.packages.get(pkgName);
|
|
908
|
-
filteredGraph.packages.set(pkgName, info);
|
|
909
|
-
const deps = dependencyGraph.edges.get(pkgName) || new Set();
|
|
910
|
-
const filteredDeps = new Set();
|
|
911
|
-
for (const dep of deps){
|
|
912
|
-
if (relevantPackages.has(dep)) filteredDeps.add(dep);
|
|
913
|
-
}
|
|
914
|
-
filteredGraph.edges.set(pkgName, filteredDeps);
|
|
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.`);
|
|
915
987
|
}
|
|
916
|
-
//
|
|
917
|
-
|
|
918
|
-
|
|
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).`);
|
|
919
992
|
}
|
|
920
993
|
// Handle stop-at functionality if specified
|
|
921
|
-
const stopAt = (
|
|
994
|
+
const stopAt = (_runConfig_tree9 = runConfig.tree) === null || _runConfig_tree9 === void 0 ? void 0 : _runConfig_tree9.stopAt;
|
|
922
995
|
if (stopAt) {
|
|
923
996
|
logger.verbose(`${isDryRun ? 'DRY RUN: ' : ''}Looking for stop package: ${stopAt}`);
|
|
924
997
|
// Find the package that matches the stopAt directory name
|
|
@@ -1277,12 +1350,12 @@ const execute = async (runConfig)=>{
|
|
|
1277
1350
|
returnOutput = `\nBuild order: ${buildOrder.join(' → ')}\n`;
|
|
1278
1351
|
}
|
|
1279
1352
|
// Execute command if provided (custom command or built-in command)
|
|
1280
|
-
const cmd = (
|
|
1353
|
+
const cmd = (_runConfig_tree10 = runConfig.tree) === null || _runConfig_tree10 === void 0 ? void 0 : _runConfig_tree10.cmd;
|
|
1281
1354
|
// Determine command to execute
|
|
1282
1355
|
let commandToRun;
|
|
1283
1356
|
let isBuiltInCommand = false;
|
|
1284
1357
|
if (builtInCommand) {
|
|
1285
|
-
var
|
|
1358
|
+
var _runConfig_tree11, _runConfig_tree12, _runConfig_tree13;
|
|
1286
1359
|
// Built-in command mode: shell out to kodrdriv subprocess
|
|
1287
1360
|
// Build command with propagated global options
|
|
1288
1361
|
const globalOptions = [];
|
|
@@ -1299,14 +1372,14 @@ const execute = async (runConfig)=>{
|
|
|
1299
1372
|
// Build the command with global options
|
|
1300
1373
|
const optionsString = globalOptions.length > 0 ? ` ${globalOptions.join(' ')}` : '';
|
|
1301
1374
|
// Add package argument for link/unlink commands
|
|
1302
|
-
const packageArg = (
|
|
1375
|
+
const packageArg = (_runConfig_tree11 = runConfig.tree) === null || _runConfig_tree11 === void 0 ? void 0 : _runConfig_tree11.packageArgument;
|
|
1303
1376
|
const packageArgString = packageArg && (builtInCommand === 'link' || builtInCommand === 'unlink') ? ` "${packageArg}"` : '';
|
|
1304
1377
|
// Add command-specific options
|
|
1305
1378
|
let commandSpecificOptions = '';
|
|
1306
|
-
if (builtInCommand === 'unlink' && ((
|
|
1379
|
+
if (builtInCommand === 'unlink' && ((_runConfig_tree12 = runConfig.tree) === null || _runConfig_tree12 === void 0 ? void 0 : _runConfig_tree12.cleanNodeModules)) {
|
|
1307
1380
|
commandSpecificOptions += ' --clean-node-modules';
|
|
1308
1381
|
}
|
|
1309
|
-
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) {
|
|
1310
1383
|
commandSpecificOptions += ` --externals ${runConfig.tree.externals.join(' ')}`;
|
|
1311
1384
|
}
|
|
1312
1385
|
commandToRun = `kodrdriv ${builtInCommand}${optionsString}${packageArgString}${commandSpecificOptions}`;
|
|
@@ -1316,6 +1389,23 @@ const execute = async (runConfig)=>{
|
|
|
1316
1389
|
commandToRun = cmd;
|
|
1317
1390
|
}
|
|
1318
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
|
+
}
|
|
1319
1409
|
// Create set of all package names for inter-project dependency detection
|
|
1320
1410
|
const allPackageNames = new Set(Array.from(dependencyGraph.packages.keys()));
|
|
1321
1411
|
// Initialize execution context if not continuing
|
|
@@ -1329,8 +1419,8 @@ const execute = async (runConfig)=>{
|
|
|
1329
1419
|
startTime: new Date(),
|
|
1330
1420
|
lastUpdateTime: new Date()
|
|
1331
1421
|
};
|
|
1332
|
-
// Save initial context
|
|
1333
|
-
if (isBuiltInCommand && builtInCommand === 'publish' && !isDryRun) {
|
|
1422
|
+
// Save initial context for commands that support continuation
|
|
1423
|
+
if (isBuiltInCommand && (builtInCommand === 'publish' || builtInCommand === 'run') && !isDryRun) {
|
|
1334
1424
|
await saveExecutionContext(executionContext, runConfig.outputDirectory);
|
|
1335
1425
|
}
|
|
1336
1426
|
}
|
|
@@ -1360,7 +1450,7 @@ const execute = async (runConfig)=>{
|
|
|
1360
1450
|
if (result.success) {
|
|
1361
1451
|
successCount++;
|
|
1362
1452
|
// Update context
|
|
1363
|
-
if (executionContext && isBuiltInCommand && builtInCommand === 'publish' && !isDryRun) {
|
|
1453
|
+
if (executionContext && isBuiltInCommand && (builtInCommand === 'publish' || builtInCommand === 'run') && !isDryRun) {
|
|
1364
1454
|
executionContext.completedPackages.push(packageName);
|
|
1365
1455
|
executionContext.publishedVersions = publishedVersions;
|
|
1366
1456
|
executionContext.lastUpdateTime = new Date();
|
|
@@ -1373,17 +1463,76 @@ const execute = async (runConfig)=>{
|
|
|
1373
1463
|
}
|
|
1374
1464
|
} else {
|
|
1375
1465
|
failedPackage = packageName;
|
|
1376
|
-
const formattedError = formatSubprojectError(packageName, result.error);
|
|
1466
|
+
const formattedError = formatSubprojectError(packageName, result.error, packageInfo, i + 1, buildOrder.length);
|
|
1377
1467
|
if (!isDryRun) {
|
|
1468
|
+
var _result_error;
|
|
1378
1469
|
packageLogger.error(`Execution failed`);
|
|
1379
1470
|
logger.error(formattedError);
|
|
1380
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
|
+
}
|
|
1381
1501
|
logger.error(`To resume from this point, run:`);
|
|
1382
1502
|
if (isBuiltInCommand) {
|
|
1383
1503
|
logger.error(` kodrdriv tree ${builtInCommand} --continue`);
|
|
1384
1504
|
} else {
|
|
1385
1505
|
logger.error(` kodrdriv tree --continue --cmd "${commandToRun}"`);
|
|
1386
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('');
|
|
1387
1536
|
throw new Error(`Command failed in package ${packageName}`);
|
|
1388
1537
|
}
|
|
1389
1538
|
break;
|
|
@@ -1393,7 +1542,7 @@ const execute = async (runConfig)=>{
|
|
|
1393
1542
|
const summary = `${isDryRun ? 'DRY RUN: ' : ''}All ${buildOrder.length} packages completed successfully! 🎉`;
|
|
1394
1543
|
logger.info(summary);
|
|
1395
1544
|
// Clean up context on successful completion
|
|
1396
|
-
if (isBuiltInCommand && builtInCommand === 'publish' && !isDryRun) {
|
|
1545
|
+
if (isBuiltInCommand && (builtInCommand === 'publish' || builtInCommand === 'run') && !isDryRun) {
|
|
1397
1546
|
await cleanupContext(runConfig.outputDirectory);
|
|
1398
1547
|
}
|
|
1399
1548
|
return returnOutput; // Don't duplicate the summary in return string
|