@eldrforge/kodrdriv 0.0.39 → 0.0.40
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 +8 -5
- package/dist/arguments.js.map +1 -1
- package/dist/commands/publish-tree.js +223 -44
- package/dist/commands/publish-tree.js.map +1 -1
- package/dist/constants.js +3 -2
- package/dist/constants.js.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/dist/util/github.js +75 -46
- package/dist/util/github.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,35 @@ import { run } from '../util/child.js';
|
|
|
7
7
|
import { execute as execute$1 } from './publish.js';
|
|
8
8
|
import { safeJsonParse, validatePackageJson } from '../util/validation.js';
|
|
9
9
|
|
|
10
|
+
// Create a package-scoped logger that prefixes all messages
|
|
11
|
+
const createPackageLogger = (packageName, sequenceNumber, totalCount, isDryRun = false)=>{
|
|
12
|
+
const baseLogger = getLogger();
|
|
13
|
+
const prefix = `[${sequenceNumber}/${totalCount}] ${packageName}:`;
|
|
14
|
+
const dryRunPrefix = isDryRun ? 'DRY RUN: ' : '';
|
|
15
|
+
return {
|
|
16
|
+
info: (message, ...args)=>baseLogger.info(`${dryRunPrefix}${prefix} ${message}`, ...args),
|
|
17
|
+
warn: (message, ...args)=>baseLogger.warn(`${dryRunPrefix}${prefix} ${message}`, ...args),
|
|
18
|
+
error: (message, ...args)=>baseLogger.error(`${dryRunPrefix}${prefix} ${message}`, ...args),
|
|
19
|
+
debug: (message, ...args)=>baseLogger.debug(`${dryRunPrefix}${prefix} ${message}`, ...args),
|
|
20
|
+
verbose: (message, ...args)=>baseLogger.verbose(`${dryRunPrefix}${prefix} ${message}`, ...args),
|
|
21
|
+
silly: (message, ...args)=>baseLogger.silly(`${dryRunPrefix}${prefix} ${message}`, ...args)
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
// Execute an operation with package context logging for nested operations
|
|
25
|
+
const withPackageContext = async (packageName, sequenceNumber, totalCount, isDryRun, operation)=>{
|
|
26
|
+
const packageLogger = createPackageLogger(packageName, sequenceNumber, totalCount, isDryRun);
|
|
27
|
+
// For now, just execute the operation directly
|
|
28
|
+
// In the future, we could implement more sophisticated context passing
|
|
29
|
+
try {
|
|
30
|
+
packageLogger.verbose(`Starting nested operation...`);
|
|
31
|
+
const result = await operation();
|
|
32
|
+
packageLogger.verbose(`Nested operation completed`);
|
|
33
|
+
return result;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
packageLogger.error(`Nested operation failed: ${error.message}`);
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
10
39
|
// Helper function to format subproject error output
|
|
11
40
|
const formatSubprojectError = (packageName, error)=>{
|
|
12
41
|
const lines = [];
|
|
@@ -176,6 +205,105 @@ const topologicalSort = (graph)=>{
|
|
|
176
205
|
logger.verbose(`Topological sort completed. Build order determined for ${result.length} packages.`);
|
|
177
206
|
return result;
|
|
178
207
|
};
|
|
208
|
+
// Group packages into dependency levels for parallel execution
|
|
209
|
+
const groupPackagesByDependencyLevels = (graph, buildOrder)=>{
|
|
210
|
+
const logger = getLogger();
|
|
211
|
+
const { edges } = graph;
|
|
212
|
+
const levels = [];
|
|
213
|
+
const packageLevels = new Map();
|
|
214
|
+
// Calculate the dependency level for each package
|
|
215
|
+
const calculateLevel = (packageName)=>{
|
|
216
|
+
if (packageLevels.has(packageName)) {
|
|
217
|
+
return packageLevels.get(packageName);
|
|
218
|
+
}
|
|
219
|
+
const deps = edges.get(packageName) || new Set();
|
|
220
|
+
if (deps.size === 0) {
|
|
221
|
+
// No dependencies - this is level 0
|
|
222
|
+
packageLevels.set(packageName, 0);
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
// Level is 1 + max level of dependencies
|
|
226
|
+
let maxDepLevel = -1;
|
|
227
|
+
for (const dep of deps){
|
|
228
|
+
const depLevel = calculateLevel(dep);
|
|
229
|
+
maxDepLevel = Math.max(maxDepLevel, depLevel);
|
|
230
|
+
}
|
|
231
|
+
const level = maxDepLevel + 1;
|
|
232
|
+
packageLevels.set(packageName, level);
|
|
233
|
+
return level;
|
|
234
|
+
};
|
|
235
|
+
// Calculate levels for all packages
|
|
236
|
+
for (const packageName of buildOrder){
|
|
237
|
+
calculateLevel(packageName);
|
|
238
|
+
}
|
|
239
|
+
// Group packages by their levels
|
|
240
|
+
for (const packageName of buildOrder){
|
|
241
|
+
const level = packageLevels.get(packageName);
|
|
242
|
+
while(levels.length <= level){
|
|
243
|
+
levels.push([]);
|
|
244
|
+
}
|
|
245
|
+
levels[level].push(packageName);
|
|
246
|
+
}
|
|
247
|
+
logger.verbose(`Packages grouped into ${levels.length} dependency levels for parallel execution`);
|
|
248
|
+
for(let i = 0; i < levels.length; i++){
|
|
249
|
+
logger.verbose(` Level ${i}: ${levels[i].join(', ')}`);
|
|
250
|
+
}
|
|
251
|
+
return levels;
|
|
252
|
+
};
|
|
253
|
+
// Execute a single package and return execution result
|
|
254
|
+
const executePackage = async (packageName, packageInfo, commandToRun, shouldPublish, runConfig, isDryRun, index, total)=>{
|
|
255
|
+
const packageLogger = createPackageLogger(packageName, index + 1, total, isDryRun);
|
|
256
|
+
const packageDir = packageInfo.path;
|
|
257
|
+
packageLogger.info(`Starting execution...`);
|
|
258
|
+
packageLogger.verbose(`Working directory: ${packageDir}`);
|
|
259
|
+
try {
|
|
260
|
+
if (isDryRun) {
|
|
261
|
+
if (shouldPublish) {
|
|
262
|
+
packageLogger.info(`Would execute publish command directly`);
|
|
263
|
+
} else {
|
|
264
|
+
// Use main logger for the specific message tests expect
|
|
265
|
+
const logger = getLogger();
|
|
266
|
+
logger.info(`DRY RUN: Would execute: ${commandToRun}`);
|
|
267
|
+
packageLogger.info(`In directory: ${packageDir}`);
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
// Change to the package directory and run the command
|
|
271
|
+
const originalCwd = process.cwd();
|
|
272
|
+
try {
|
|
273
|
+
process.chdir(packageDir);
|
|
274
|
+
packageLogger.verbose(`Changed to directory: ${packageDir}`);
|
|
275
|
+
if (shouldPublish) {
|
|
276
|
+
packageLogger.info(`Starting publish process...`);
|
|
277
|
+
// Call publish command with package context so all nested logs are prefixed
|
|
278
|
+
await withPackageContext(packageName, index + 1, total, isDryRun, async ()=>{
|
|
279
|
+
await execute$1(runConfig);
|
|
280
|
+
});
|
|
281
|
+
packageLogger.info(`Publish completed successfully`);
|
|
282
|
+
} else {
|
|
283
|
+
packageLogger.info(`Executing command: ${commandToRun}`);
|
|
284
|
+
// Wrap command execution in package context
|
|
285
|
+
await withPackageContext(packageName, index + 1, total, isDryRun, async ()=>{
|
|
286
|
+
await run(commandToRun); // Non-null assertion since we're inside if (commandToRun)
|
|
287
|
+
});
|
|
288
|
+
packageLogger.info(`Command completed successfully`);
|
|
289
|
+
}
|
|
290
|
+
packageLogger.info(`✅ Execution completed successfully`);
|
|
291
|
+
} finally{
|
|
292
|
+
process.chdir(originalCwd);
|
|
293
|
+
packageLogger.verbose(`Restored working directory to: ${originalCwd}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
success: true
|
|
298
|
+
};
|
|
299
|
+
} catch (error) {
|
|
300
|
+
packageLogger.error(`❌ Execution failed: ${error.message}`);
|
|
301
|
+
return {
|
|
302
|
+
success: false,
|
|
303
|
+
error
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
};
|
|
179
307
|
const execute = async (runConfig)=>{
|
|
180
308
|
var _runConfig_publishTree;
|
|
181
309
|
const logger = getLogger();
|
|
@@ -184,7 +312,7 @@ const execute = async (runConfig)=>{
|
|
|
184
312
|
const targetDirectory = ((_runConfig_publishTree = runConfig.publishTree) === null || _runConfig_publishTree === void 0 ? void 0 : _runConfig_publishTree.directory) || process.cwd();
|
|
185
313
|
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Analyzing workspace at: ${targetDirectory}`);
|
|
186
314
|
try {
|
|
187
|
-
var _runConfig_publishTree1, _runConfig_publishTree2, _runConfig_publishTree3, _runConfig_publishTree4, _runConfig_publishTree5;
|
|
315
|
+
var _runConfig_publishTree1, _runConfig_publishTree2, _runConfig_publishTree3, _runConfig_publishTree4, _runConfig_publishTree5, _runConfig_publishTree6;
|
|
188
316
|
// Get exclusion patterns from config, fallback to empty array
|
|
189
317
|
const excludedPatterns = ((_runConfig_publishTree1 = runConfig.publishTree) === null || _runConfig_publishTree1 === void 0 ? void 0 : _runConfig_publishTree1.excludedPatterns) || [];
|
|
190
318
|
if (excludedPatterns.length > 0) {
|
|
@@ -253,7 +381,8 @@ const execute = async (runConfig)=>{
|
|
|
253
381
|
// Execute script, cmd, or publish if provided
|
|
254
382
|
const script = (_runConfig_publishTree3 = runConfig.publishTree) === null || _runConfig_publishTree3 === void 0 ? void 0 : _runConfig_publishTree3.script;
|
|
255
383
|
const cmd = (_runConfig_publishTree4 = runConfig.publishTree) === null || _runConfig_publishTree4 === void 0 ? void 0 : _runConfig_publishTree4.cmd;
|
|
256
|
-
const shouldPublish = (_runConfig_publishTree5 = runConfig.publishTree) === null || _runConfig_publishTree5 === void 0 ? void 0 : _runConfig_publishTree5.publish;
|
|
384
|
+
const shouldPublish = ((_runConfig_publishTree5 = runConfig.publishTree) === null || _runConfig_publishTree5 === void 0 ? void 0 : _runConfig_publishTree5.publish) || false;
|
|
385
|
+
const useParallel = ((_runConfig_publishTree6 = runConfig.publishTree) === null || _runConfig_publishTree6 === void 0 ? void 0 : _runConfig_publishTree6.parallel) || false;
|
|
257
386
|
// Handle conflicts between --script, --cmd, and --publish
|
|
258
387
|
// Priority order: --publish > --cmd > --script
|
|
259
388
|
let commandToRun;
|
|
@@ -280,56 +409,106 @@ const execute = async (runConfig)=>{
|
|
|
280
409
|
}
|
|
281
410
|
if (commandToRun || shouldPublish) {
|
|
282
411
|
const executionDescription = shouldPublish ? 'publish command' : `"${commandToRun}"`;
|
|
283
|
-
|
|
412
|
+
const parallelInfo = useParallel ? ' (with parallel execution)' : '';
|
|
413
|
+
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Executing ${actionName} ${executionDescription} in ${buildOrder.length} packages${parallelInfo}...`);
|
|
284
414
|
let successCount = 0;
|
|
285
415
|
let failedPackage = null;
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (shouldPublish) {
|
|
295
|
-
logger.info(`DRY RUN: Would execute publish command directly`);
|
|
296
|
-
} else {
|
|
297
|
-
logger.info(`DRY RUN: Would execute: ${commandToRun}`);
|
|
298
|
-
}
|
|
299
|
-
logger.info(`DRY RUN: In directory: ${packageDir}`);
|
|
416
|
+
if (useParallel) {
|
|
417
|
+
// Parallel execution: group packages by dependency levels
|
|
418
|
+
const dependencyLevels = groupPackagesByDependencyLevels(dependencyGraph, buildOrder);
|
|
419
|
+
for(let levelIndex = 0; levelIndex < dependencyLevels.length; levelIndex++){
|
|
420
|
+
const currentLevel = dependencyLevels[levelIndex];
|
|
421
|
+
if (currentLevel.length === 1) {
|
|
422
|
+
const packageName = currentLevel[0];
|
|
423
|
+
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Level ${levelIndex + 1}: Executing ${packageName}...`);
|
|
300
424
|
} else {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
425
|
+
logger.info(`${isDryRun ? 'DRY RUN: ' : ''}Level ${levelIndex + 1}: Executing ${currentLevel.length} packages in parallel: ${currentLevel.join(', ')}...`);
|
|
426
|
+
}
|
|
427
|
+
// Execute all packages in this level in parallel
|
|
428
|
+
const levelPromises = currentLevel.map((packageName)=>{
|
|
429
|
+
const packageInfo = dependencyGraph.packages.get(packageName);
|
|
430
|
+
const globalIndex = buildOrder.indexOf(packageName);
|
|
431
|
+
return executePackage(packageName, packageInfo, commandToRun, shouldPublish, runConfig, isDryRun, globalIndex, buildOrder.length);
|
|
432
|
+
});
|
|
433
|
+
// Wait for all packages in this level to complete
|
|
434
|
+
const results = await Promise.allSettled(levelPromises);
|
|
435
|
+
// Check results and handle errors
|
|
436
|
+
for(let i = 0; i < results.length; i++){
|
|
437
|
+
const result = results[i];
|
|
438
|
+
const packageName = currentLevel[i];
|
|
439
|
+
const globalIndex = buildOrder.indexOf(packageName);
|
|
440
|
+
const packageLogger = createPackageLogger(packageName, globalIndex + 1, buildOrder.length, isDryRun);
|
|
441
|
+
if (result.status === 'fulfilled') {
|
|
442
|
+
if (result.value.success) {
|
|
443
|
+
successCount++;
|
|
308
444
|
} else {
|
|
309
|
-
|
|
445
|
+
// Package failed
|
|
446
|
+
failedPackage = packageName;
|
|
447
|
+
const formattedError = formatSubprojectError(packageName, result.value.error);
|
|
448
|
+
if (!isDryRun) {
|
|
449
|
+
packageLogger.error(`Execution failed`);
|
|
450
|
+
logger.error(formattedError);
|
|
451
|
+
logger.error(`Failed after ${successCount} successful packages.`);
|
|
452
|
+
const packageDir = dependencyGraph.packages.get(packageName).path;
|
|
453
|
+
const packageDirName = path.basename(packageDir);
|
|
454
|
+
logger.error(`To resume from this package, run:`);
|
|
455
|
+
logger.error(` kodrdriv publish-tree --start-from ${packageDirName}`);
|
|
456
|
+
throw new Error(`Script failed in package ${packageName}`);
|
|
457
|
+
}
|
|
458
|
+
break;
|
|
310
459
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
460
|
+
} else {
|
|
461
|
+
// Promise was rejected
|
|
462
|
+
failedPackage = packageName;
|
|
463
|
+
if (!isDryRun) {
|
|
464
|
+
packageLogger.error(`Unexpected error: ${result.reason}`);
|
|
465
|
+
logger.error(`Failed after ${successCount} successful packages.`);
|
|
466
|
+
const packageDir = dependencyGraph.packages.get(packageName).path;
|
|
467
|
+
const packageDirName = path.basename(packageDir);
|
|
468
|
+
logger.error(`To resume from this package, run:`);
|
|
469
|
+
logger.error(` kodrdriv publish-tree --start-from ${packageDirName}`);
|
|
470
|
+
throw new Error(`Unexpected error in package ${packageName}`);
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
315
473
|
}
|
|
316
474
|
}
|
|
317
|
-
|
|
318
|
-
failedPackage
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
475
|
+
// If any package failed, stop execution
|
|
476
|
+
if (failedPackage) {
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
if (currentLevel.length > 1) {
|
|
480
|
+
logger.info(`✅ Level ${levelIndex + 1} completed: all ${currentLevel.length} packages finished successfully`);
|
|
481
|
+
} else if (currentLevel.length === 1 && successCount > 0) {
|
|
482
|
+
const packageName = currentLevel[0];
|
|
483
|
+
const globalIndex = buildOrder.indexOf(packageName);
|
|
484
|
+
const packageLogger = createPackageLogger(packageName, globalIndex + 1, buildOrder.length, isDryRun);
|
|
485
|
+
packageLogger.info(`✅ Level ${levelIndex + 1} completed successfully`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
// Sequential execution (original logic)
|
|
490
|
+
for(let i = 0; i < buildOrder.length; i++){
|
|
491
|
+
const packageName = buildOrder[i];
|
|
492
|
+
const packageInfo = dependencyGraph.packages.get(packageName);
|
|
493
|
+
const packageLogger = createPackageLogger(packageName, i + 1, buildOrder.length, isDryRun);
|
|
494
|
+
const result = await executePackage(packageName, packageInfo, commandToRun, shouldPublish, runConfig, isDryRun, i, buildOrder.length);
|
|
495
|
+
if (result.success) {
|
|
496
|
+
successCount++;
|
|
497
|
+
} else {
|
|
498
|
+
failedPackage = packageName;
|
|
499
|
+
const formattedError = formatSubprojectError(packageName, result.error);
|
|
500
|
+
if (!isDryRun) {
|
|
501
|
+
packageLogger.error(`Execution failed`);
|
|
502
|
+
logger.error(formattedError);
|
|
503
|
+
logger.error(`Failed after ${successCount} successful packages.`);
|
|
504
|
+
const packageDir = packageInfo.path;
|
|
505
|
+
const packageDirName = path.basename(packageDir);
|
|
506
|
+
logger.error(`To resume from this package, run:`);
|
|
507
|
+
logger.error(` kodrdriv publish-tree --start-from ${packageDirName}`);
|
|
508
|
+
throw new Error(`Script failed in package ${packageName}`);
|
|
509
|
+
}
|
|
510
|
+
break;
|
|
331
511
|
}
|
|
332
|
-
break;
|
|
333
512
|
}
|
|
334
513
|
}
|
|
335
514
|
if (!failedPackage) {
|