@grunnverk/tree-execution 1.0.0
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/LICENSE +22 -0
- package/README.md +1576 -0
- package/dist/TreeExecutor.d.ts +113 -0
- package/dist/TreeExecutor.d.ts.map +1 -0
- package/dist/TreeExecutor.js +113 -0
- package/dist/TreeExecutor.js.map +1 -0
- package/dist/checkpoint/CheckpointManager.d.ts +18 -0
- package/dist/checkpoint/CheckpointManager.d.ts.map +1 -0
- package/dist/checkpoint/CheckpointManager.js +156 -0
- package/dist/checkpoint/CheckpointManager.js.map +1 -0
- package/dist/checkpoint/index.d.ts +5 -0
- package/dist/checkpoint/index.d.ts.map +1 -0
- package/dist/checkpoint/index.js +5 -0
- package/dist/checkpoint/index.js.map +1 -0
- package/dist/execution/CommandValidator.d.ts +25 -0
- package/dist/execution/CommandValidator.d.ts.map +1 -0
- package/dist/execution/CommandValidator.js +129 -0
- package/dist/execution/CommandValidator.js.map +1 -0
- package/dist/execution/DependencyChecker.d.ts +47 -0
- package/dist/execution/DependencyChecker.d.ts.map +1 -0
- package/dist/execution/DependencyChecker.js +95 -0
- package/dist/execution/DependencyChecker.js.map +1 -0
- package/dist/execution/DynamicTaskPool.d.ts +118 -0
- package/dist/execution/DynamicTaskPool.d.ts.map +1 -0
- package/dist/execution/DynamicTaskPool.js +658 -0
- package/dist/execution/DynamicTaskPool.js.map +1 -0
- package/dist/execution/RecoveryManager.d.ts +89 -0
- package/dist/execution/RecoveryManager.d.ts.map +1 -0
- package/dist/execution/RecoveryManager.js +592 -0
- package/dist/execution/RecoveryManager.js.map +1 -0
- package/dist/execution/ResourceMonitor.d.ts +73 -0
- package/dist/execution/ResourceMonitor.d.ts.map +1 -0
- package/dist/execution/ResourceMonitor.js +148 -0
- package/dist/execution/ResourceMonitor.js.map +1 -0
- package/dist/execution/Scheduler.d.ts +36 -0
- package/dist/execution/Scheduler.d.ts.map +1 -0
- package/dist/execution/Scheduler.js +83 -0
- package/dist/execution/Scheduler.js.map +1 -0
- package/dist/execution/TreeExecutionAdapter.d.ts +45 -0
- package/dist/execution/TreeExecutionAdapter.d.ts.map +1 -0
- package/dist/execution/TreeExecutionAdapter.js +260 -0
- package/dist/execution/TreeExecutionAdapter.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/tree.d.ts +13 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +2510 -0
- package/dist/tree.js.map +1 -0
- package/dist/types/config.d.ts +174 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/parallelExecution.d.ts +108 -0
- package/dist/types/parallelExecution.d.ts.map +1 -0
- package/dist/types/parallelExecution.js +2 -0
- package/dist/types/parallelExecution.js.map +1 -0
- package/dist/util/commandStubs.d.ts +22 -0
- package/dist/util/commandStubs.d.ts.map +1 -0
- package/dist/util/commandStubs.js +49 -0
- package/dist/util/commandStubs.js.map +1 -0
- package/dist/util/logger.d.ts +14 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +30 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/mutex.d.ts +38 -0
- package/dist/util/mutex.d.ts.map +1 -0
- package/dist/util/mutex.js +101 -0
- package/dist/util/mutex.js.map +1 -0
- package/dist/util/treeUtils.d.ts +46 -0
- package/dist/util/treeUtils.d.ts.map +1 -0
- package/dist/util/treeUtils.js +74 -0
- package/dist/util/treeUtils.js.map +1 -0
- package/guide/index.md +84 -0
- package/package.json +64 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TreeExecutor - Class-based tree command orchestration
|
|
3
|
+
*
|
|
4
|
+
* Refactored from tree.ts to use instance state instead of global state
|
|
5
|
+
* and dependency injection for commands.
|
|
6
|
+
*/
|
|
7
|
+
import type { TreeExecutionConfig } from './types/config.js';
|
|
8
|
+
/**
|
|
9
|
+
* Published version tracking
|
|
10
|
+
*/
|
|
11
|
+
export interface PublishedVersion {
|
|
12
|
+
packageName: string;
|
|
13
|
+
version: string;
|
|
14
|
+
publishTime: Date;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Tree execution context for persistence
|
|
18
|
+
*/
|
|
19
|
+
export interface TreeExecutionContext {
|
|
20
|
+
command: string;
|
|
21
|
+
originalConfig: TreeExecutionConfig;
|
|
22
|
+
publishedVersions: PublishedVersion[];
|
|
23
|
+
completedPackages: string[];
|
|
24
|
+
buildOrder: string[];
|
|
25
|
+
startTime: Date;
|
|
26
|
+
lastUpdateTime: Date;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Command executor interface for dependency injection
|
|
30
|
+
*/
|
|
31
|
+
export interface CommandExecutor {
|
|
32
|
+
execute(config: TreeExecutionConfig, mode?: string): Promise<any>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Command registry for built-in commands
|
|
36
|
+
*/
|
|
37
|
+
export interface CommandRegistry {
|
|
38
|
+
updates?: CommandExecutor;
|
|
39
|
+
commit?: CommandExecutor;
|
|
40
|
+
link?: CommandExecutor;
|
|
41
|
+
unlink?: CommandExecutor;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* TreeExecutor options
|
|
45
|
+
*/
|
|
46
|
+
export interface TreeExecutorOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Command registry for dependency injection
|
|
49
|
+
*/
|
|
50
|
+
commands?: CommandRegistry;
|
|
51
|
+
/**
|
|
52
|
+
* Custom logger (optional)
|
|
53
|
+
*/
|
|
54
|
+
logger?: any;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* TreeExecutor - Orchestrates tree command execution
|
|
58
|
+
*
|
|
59
|
+
* This class encapsulates all state that was previously global,
|
|
60
|
+
* making it testable and allowing multiple concurrent executions.
|
|
61
|
+
*/
|
|
62
|
+
export declare class TreeExecutor {
|
|
63
|
+
private publishedVersions;
|
|
64
|
+
private executionContext;
|
|
65
|
+
private stateMutex;
|
|
66
|
+
private commands;
|
|
67
|
+
private logger;
|
|
68
|
+
constructor(options?: TreeExecutorOptions);
|
|
69
|
+
/**
|
|
70
|
+
* Get published versions (thread-safe)
|
|
71
|
+
*/
|
|
72
|
+
getPublishedVersions(): Promise<PublishedVersion[]>;
|
|
73
|
+
/**
|
|
74
|
+
* Add published version (thread-safe)
|
|
75
|
+
*/
|
|
76
|
+
addPublishedVersion(version: PublishedVersion): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Get execution context (thread-safe)
|
|
79
|
+
* Returns a deep copy to prevent external modifications
|
|
80
|
+
*/
|
|
81
|
+
getExecutionContext(): Promise<TreeExecutionContext | null>;
|
|
82
|
+
/**
|
|
83
|
+
* Set execution context (thread-safe)
|
|
84
|
+
*/
|
|
85
|
+
setExecutionContext(context: TreeExecutionContext | null): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Reset state (for testing)
|
|
88
|
+
*/
|
|
89
|
+
reset(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Execute tree command
|
|
92
|
+
*
|
|
93
|
+
* This will be the main entry point, delegating to the execute function
|
|
94
|
+
* from tree.ts but with instance state instead of global state.
|
|
95
|
+
*
|
|
96
|
+
* @param config - Tree execution configuration
|
|
97
|
+
* @returns Result message
|
|
98
|
+
*/
|
|
99
|
+
execute(config: TreeExecutionConfig): Promise<string>;
|
|
100
|
+
/**
|
|
101
|
+
* Get command executor
|
|
102
|
+
*/
|
|
103
|
+
getCommand(name: keyof CommandRegistry): CommandExecutor | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Set command executor (for testing/injection)
|
|
106
|
+
*/
|
|
107
|
+
setCommand(name: keyof CommandRegistry, executor: CommandExecutor): void;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create a default TreeExecutor instance
|
|
111
|
+
*/
|
|
112
|
+
export declare function createTreeExecutor(options?: TreeExecutorOptions): TreeExecutor;
|
|
113
|
+
//# sourceMappingURL=TreeExecutor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TreeExecutor.d.ts","sourceRoot":"","sources":["../src/TreeExecutor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAG7D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,IAAI,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,mBAAmB,CAAC;IACpC,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,IAAI,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,MAAM,EAAE,mBAAmB,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CACrE;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,MAAM,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC;;OAEG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B;;OAEG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC;CAChB;AAED;;;;;GAKG;AACH,qBAAa,YAAY;IAErB,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,gBAAgB,CAAqC;IAC7D,OAAO,CAAC,UAAU,CAAc;IAGhC,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,MAAM,CAAM;gBAER,OAAO,GAAE,mBAAwB;IAM7C;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAMzD;;OAEG;IACG,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnE;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAcjE;;OAEG;IACG,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9E;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;;;;;;OAQG;IACG,OAAO,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;IAU3D;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,eAAe,GAAG,eAAe,GAAG,SAAS;IAIpE;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,eAAe,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI;CAG3E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,YAAY,CAE9E"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TreeExecutor - Class-based tree command orchestration
|
|
3
|
+
*
|
|
4
|
+
* Refactored from tree.ts to use instance state instead of global state
|
|
5
|
+
* and dependency injection for commands.
|
|
6
|
+
*/
|
|
7
|
+
import { SimpleMutex } from './util/mutex.js';
|
|
8
|
+
/**
|
|
9
|
+
* TreeExecutor - Orchestrates tree command execution
|
|
10
|
+
*
|
|
11
|
+
* This class encapsulates all state that was previously global,
|
|
12
|
+
* making it testable and allowing multiple concurrent executions.
|
|
13
|
+
*/
|
|
14
|
+
export class TreeExecutor {
|
|
15
|
+
// Instance state (previously global)
|
|
16
|
+
publishedVersions = [];
|
|
17
|
+
executionContext = null;
|
|
18
|
+
stateMutex;
|
|
19
|
+
// Dependency injection
|
|
20
|
+
commands;
|
|
21
|
+
logger;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.commands = options.commands || {};
|
|
24
|
+
this.logger = options.logger;
|
|
25
|
+
this.stateMutex = new SimpleMutex();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get published versions (thread-safe)
|
|
29
|
+
*/
|
|
30
|
+
async getPublishedVersions() {
|
|
31
|
+
return await this.stateMutex.runExclusive(async () => {
|
|
32
|
+
return [...this.publishedVersions];
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Add published version (thread-safe)
|
|
37
|
+
*/
|
|
38
|
+
async addPublishedVersion(version) {
|
|
39
|
+
await this.stateMutex.runExclusive(async () => {
|
|
40
|
+
this.publishedVersions.push(version);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get execution context (thread-safe)
|
|
45
|
+
* Returns a deep copy to prevent external modifications
|
|
46
|
+
*/
|
|
47
|
+
async getExecutionContext() {
|
|
48
|
+
return await this.stateMutex.runExclusive(async () => {
|
|
49
|
+
if (!this.executionContext)
|
|
50
|
+
return null;
|
|
51
|
+
// Return deep copy to prevent external modification
|
|
52
|
+
return {
|
|
53
|
+
...this.executionContext,
|
|
54
|
+
publishedVersions: [...this.executionContext.publishedVersions],
|
|
55
|
+
completedPackages: [...this.executionContext.completedPackages],
|
|
56
|
+
buildOrder: [...this.executionContext.buildOrder]
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Set execution context (thread-safe)
|
|
62
|
+
*/
|
|
63
|
+
async setExecutionContext(context) {
|
|
64
|
+
await this.stateMutex.runExclusive(async () => {
|
|
65
|
+
this.executionContext = context;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Reset state (for testing)
|
|
70
|
+
*/
|
|
71
|
+
async reset() {
|
|
72
|
+
await this.stateMutex.runExclusive(async () => {
|
|
73
|
+
this.publishedVersions = [];
|
|
74
|
+
this.executionContext = null;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Execute tree command
|
|
79
|
+
*
|
|
80
|
+
* This will be the main entry point, delegating to the execute function
|
|
81
|
+
* from tree.ts but with instance state instead of global state.
|
|
82
|
+
*
|
|
83
|
+
* @param config - Tree execution configuration
|
|
84
|
+
* @returns Result message
|
|
85
|
+
*/
|
|
86
|
+
async execute(config) {
|
|
87
|
+
// Import the execute function from tree.ts
|
|
88
|
+
// We'll need to refactor tree.ts to accept TreeExecutor instance
|
|
89
|
+
const { execute } = await import('./tree.js');
|
|
90
|
+
// For now, this is a placeholder
|
|
91
|
+
// We'll refactor tree.ts to accept TreeExecutor in the next step
|
|
92
|
+
return await execute(config);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get command executor
|
|
96
|
+
*/
|
|
97
|
+
getCommand(name) {
|
|
98
|
+
return this.commands[name];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Set command executor (for testing/injection)
|
|
102
|
+
*/
|
|
103
|
+
setCommand(name, executor) {
|
|
104
|
+
this.commands[name] = executor;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Create a default TreeExecutor instance
|
|
109
|
+
*/
|
|
110
|
+
export function createTreeExecutor(options) {
|
|
111
|
+
return new TreeExecutor(options);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=TreeExecutor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TreeExecutor.js","sourceRoot":"","sources":["../src/TreeExecutor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ParallelExecutionCheckpoint } from '../types/index.js';
|
|
2
|
+
export declare class CheckpointManager {
|
|
3
|
+
private checkpointPath;
|
|
4
|
+
private lockPath;
|
|
5
|
+
private tempPath;
|
|
6
|
+
private logger;
|
|
7
|
+
private storage;
|
|
8
|
+
constructor(outputDirectory?: string);
|
|
9
|
+
save(checkpoint: ParallelExecutionCheckpoint): Promise<void>;
|
|
10
|
+
load(): Promise<ParallelExecutionCheckpoint | null>;
|
|
11
|
+
backup(): Promise<void>;
|
|
12
|
+
cleanup(): Promise<void>;
|
|
13
|
+
private acquireLock;
|
|
14
|
+
private validateCheckpoint;
|
|
15
|
+
private isCompatibleVersion;
|
|
16
|
+
private loadBackup;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=CheckpointManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CheckpointManager.d.ts","sourceRoot":"","sources":["../../src/checkpoint/CheckpointManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AASrE,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,OAAO,CAAmB;gBAEtB,eAAe,GAAE,MAAsB;IAM7C,IAAI,CAAC,UAAU,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB5D,IAAI,IAAI,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC;IAoCnD,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IASvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAahB,WAAW;IAqCzB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,mBAAmB;YAOb,UAAU;CAa3B"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import { getLogger } from '../util/logger.js';
|
|
4
|
+
import { createStorage } from '@grunnverk/shared';
|
|
5
|
+
const CHECKPOINT_VERSION = '1.0.0';
|
|
6
|
+
export class CheckpointManager {
|
|
7
|
+
checkpointPath;
|
|
8
|
+
lockPath;
|
|
9
|
+
tempPath;
|
|
10
|
+
logger = getLogger();
|
|
11
|
+
storage = createStorage();
|
|
12
|
+
constructor(outputDirectory = process.cwd()) {
|
|
13
|
+
this.checkpointPath = path.join(outputDirectory, '.kodrdriv-parallel-context.json');
|
|
14
|
+
this.lockPath = `${this.checkpointPath}.lock`;
|
|
15
|
+
this.tempPath = `${this.checkpointPath}.tmp`;
|
|
16
|
+
}
|
|
17
|
+
async save(checkpoint) {
|
|
18
|
+
const lock = await this.acquireLock();
|
|
19
|
+
try {
|
|
20
|
+
// Set version and timestamp
|
|
21
|
+
checkpoint.version = CHECKPOINT_VERSION;
|
|
22
|
+
checkpoint.lastUpdated = new Date().toISOString();
|
|
23
|
+
// Validate before saving
|
|
24
|
+
this.validateCheckpoint(checkpoint);
|
|
25
|
+
// Write to temp file
|
|
26
|
+
const serialized = JSON.stringify(checkpoint, null, 2);
|
|
27
|
+
await fs.writeFile(this.tempPath, serialized, 'utf-8');
|
|
28
|
+
// Atomic rename
|
|
29
|
+
await fs.rename(this.tempPath, this.checkpointPath);
|
|
30
|
+
this.logger.debug(`Checkpoint saved: ${this.checkpointPath}`);
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
await lock.release();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async load() {
|
|
37
|
+
if (!await this.storage.exists(this.checkpointPath)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const lock = await this.acquireLock();
|
|
41
|
+
try {
|
|
42
|
+
const content = await fs.readFile(this.checkpointPath, 'utf-8');
|
|
43
|
+
const checkpoint = JSON.parse(content);
|
|
44
|
+
// Validate
|
|
45
|
+
this.validateCheckpoint(checkpoint);
|
|
46
|
+
// Check version
|
|
47
|
+
if (!this.isCompatibleVersion(checkpoint.version)) {
|
|
48
|
+
throw new Error(`Incompatible checkpoint version: ${checkpoint.version}`);
|
|
49
|
+
}
|
|
50
|
+
return checkpoint;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
this.logger.error(`CHECKPOINT_LOAD_FAILED: Failed to load checkpoint file | Error: ${error.message} | Impact: Cannot resume execution`);
|
|
54
|
+
// Try backup
|
|
55
|
+
const backup = await this.loadBackup();
|
|
56
|
+
if (backup) {
|
|
57
|
+
this.logger.info('CHECKPOINT_RECOVERED_BACKUP: Recovered from backup checkpoint | Source: backup | Status: loaded');
|
|
58
|
+
return backup;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
await lock.release();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async backup() {
|
|
67
|
+
if (!await this.storage.exists(this.checkpointPath)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const backupPath = `${this.checkpointPath}.backup`;
|
|
71
|
+
await fs.copyFile(this.checkpointPath, backupPath);
|
|
72
|
+
}
|
|
73
|
+
async cleanup() {
|
|
74
|
+
const files = [
|
|
75
|
+
this.checkpointPath,
|
|
76
|
+
this.lockPath,
|
|
77
|
+
this.tempPath,
|
|
78
|
+
`${this.checkpointPath}.backup`
|
|
79
|
+
];
|
|
80
|
+
await Promise.all(files.map(file => fs.unlink(file).catch(() => { })));
|
|
81
|
+
}
|
|
82
|
+
async acquireLock() {
|
|
83
|
+
const maxWaitMs = 30000;
|
|
84
|
+
const startTime = Date.now();
|
|
85
|
+
while (true) {
|
|
86
|
+
try {
|
|
87
|
+
const fileHandle = await fs.open(this.lockPath, 'wx');
|
|
88
|
+
try {
|
|
89
|
+
const pid = process.pid;
|
|
90
|
+
const timestamp = new Date().toISOString();
|
|
91
|
+
await fileHandle.writeFile(`${pid}\n${timestamp}`);
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
await fileHandle.close();
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
release: async () => {
|
|
98
|
+
await fs.unlink(this.lockPath).catch(() => { });
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error.code !== 'EEXIST') {
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
const elapsed = Date.now() - startTime;
|
|
107
|
+
if (elapsed > maxWaitMs) {
|
|
108
|
+
this.logger.warn('CHECKPOINT_LOCK_STALE: Breaking stale checkpoint lock | Reason: Lock expired | Action: Force break lock');
|
|
109
|
+
await fs.unlink(this.lockPath).catch(() => { });
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
validateCheckpoint(checkpoint) {
|
|
117
|
+
if (!checkpoint.executionId) {
|
|
118
|
+
throw new Error('Invalid checkpoint: missing executionId');
|
|
119
|
+
}
|
|
120
|
+
if (!checkpoint.state) {
|
|
121
|
+
throw new Error('Invalid checkpoint: missing state');
|
|
122
|
+
}
|
|
123
|
+
// Validate state consistency
|
|
124
|
+
const allPackages = new Set([
|
|
125
|
+
...checkpoint.state.pending,
|
|
126
|
+
...checkpoint.state.ready,
|
|
127
|
+
...checkpoint.state.running.map(r => r.name),
|
|
128
|
+
...checkpoint.state.completed,
|
|
129
|
+
...checkpoint.state.failed.map(f => f.name),
|
|
130
|
+
...checkpoint.state.skipped
|
|
131
|
+
]);
|
|
132
|
+
if (allPackages.size !== checkpoint.buildOrder.length) {
|
|
133
|
+
this.logger.warn('CHECKPOINT_INCONSISTENCY: Checkpoint state inconsistency detected | Issue: State validation failed | Impact: May need manual recovery');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
isCompatibleVersion(version) {
|
|
137
|
+
// Simple major version check
|
|
138
|
+
const [major] = version.split('.');
|
|
139
|
+
const [expectedMajor] = CHECKPOINT_VERSION.split('.');
|
|
140
|
+
return major === expectedMajor;
|
|
141
|
+
}
|
|
142
|
+
async loadBackup() {
|
|
143
|
+
const backupPath = `${this.checkpointPath}.backup`;
|
|
144
|
+
if (!await this.storage.exists(backupPath)) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const content = await fs.readFile(backupPath, 'utf-8');
|
|
149
|
+
return JSON.parse(content);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=CheckpointManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CheckpointManager.js","sourceRoot":"","sources":["../../src/checkpoint/CheckpointManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/checkpoint/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/checkpoint/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ValidationResult {
|
|
2
|
+
valid: boolean;
|
|
3
|
+
issues: string[];
|
|
4
|
+
warnings: string[];
|
|
5
|
+
recommendations: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* CommandValidator checks if commands are safe for parallel execution
|
|
9
|
+
*/
|
|
10
|
+
export declare class CommandValidator {
|
|
11
|
+
private static logger;
|
|
12
|
+
/**
|
|
13
|
+
* Validate a command for parallel execution
|
|
14
|
+
*/
|
|
15
|
+
static validateForParallel(command: string, builtInCommand?: string): ValidationResult;
|
|
16
|
+
/**
|
|
17
|
+
* Log validation results
|
|
18
|
+
*/
|
|
19
|
+
static logValidation(result: ValidationResult): void;
|
|
20
|
+
/**
|
|
21
|
+
* Get recommended concurrency for a command type
|
|
22
|
+
*/
|
|
23
|
+
static getRecommendedConcurrency(builtInCommand?: string, cpuCount?: number, command?: string): number;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=CommandValidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandValidator.d.ts","sourceRoot":"","sources":["../../src/execution/CommandValidator.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;GAEG;AACH,qBAAa,gBAAgB;IACzB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAe;IAEpC;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,gBAAgB;IAoEtF;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAuBpD;;OAEG;IACH,MAAM,CAAC,yBAAyB,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;CAuC5G"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { getLogger } from '../util/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* CommandValidator checks if commands are safe for parallel execution
|
|
4
|
+
*/
|
|
5
|
+
export class CommandValidator {
|
|
6
|
+
static logger = getLogger();
|
|
7
|
+
/**
|
|
8
|
+
* Validate a command for parallel execution
|
|
9
|
+
*/
|
|
10
|
+
static validateForParallel(command, builtInCommand) {
|
|
11
|
+
const issues = [];
|
|
12
|
+
const warnings = [];
|
|
13
|
+
const recommendations = [];
|
|
14
|
+
// Check for inherently unsafe operations
|
|
15
|
+
const unsafePatterns = [
|
|
16
|
+
{ pattern: /git\s+checkout/, message: 'Branch switching is not safe for parallel execution' },
|
|
17
|
+
{ pattern: /git\s+switch/, message: 'Branch switching is not safe for parallel execution' },
|
|
18
|
+
{ pattern: /git\s+rebase/, message: 'Rebase operations should not run in parallel' },
|
|
19
|
+
{ pattern: /git\s+merge/, message: 'Merge operations should not run in parallel' },
|
|
20
|
+
{ pattern: /rm\s+-rf\s+\//, message: 'Dangerous deletion commands detected' },
|
|
21
|
+
{ pattern: /sudo/, message: 'Sudo commands should not run in parallel' },
|
|
22
|
+
{ pattern: /format/, message: 'Format commands may be destructive' }
|
|
23
|
+
];
|
|
24
|
+
for (const { pattern, message } of unsafePatterns) {
|
|
25
|
+
if (pattern.test(command)) {
|
|
26
|
+
issues.push(message);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Check for potentially problematic operations
|
|
30
|
+
const warningPatterns = [
|
|
31
|
+
{ pattern: /npm\s+(link|unlink)/, message: 'npm link/unlink may conflict in parallel execution' },
|
|
32
|
+
{ pattern: /npm\s+install/, message: 'npm install in parallel may cause lock file conflicts' },
|
|
33
|
+
{ pattern: /npm\s+ci/, message: 'npm ci in parallel may cause lock file conflicts' },
|
|
34
|
+
{ pattern: /package-lock\.json/, message: 'Operations modifying package-lock.json may conflict' },
|
|
35
|
+
{ pattern: /node_modules/, message: 'Operations in node_modules may conflict' }
|
|
36
|
+
];
|
|
37
|
+
for (const { pattern, message } of warningPatterns) {
|
|
38
|
+
if (pattern.test(command)) {
|
|
39
|
+
warnings.push(message);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Built-in command specific checks
|
|
43
|
+
if (builtInCommand === 'commit') {
|
|
44
|
+
warnings.push('Parallel commits: Recommend max concurrency of 2 to avoid conflicts');
|
|
45
|
+
recommendations.push('Use: --max-concurrency 2');
|
|
46
|
+
}
|
|
47
|
+
if (builtInCommand === 'publish') {
|
|
48
|
+
warnings.push('Parallel publish: PR checks may take significant time');
|
|
49
|
+
warnings.push('Version propagation happens automatically between dependency levels');
|
|
50
|
+
recommendations.push('Use: --max-concurrency 2-3 for publish operations');
|
|
51
|
+
recommendations.push('Monitor with: kodrdriv tree --status-parallel');
|
|
52
|
+
}
|
|
53
|
+
if (builtInCommand === 'link' || builtInCommand === 'unlink') {
|
|
54
|
+
warnings.push('Link operations may have filesystem race conditions');
|
|
55
|
+
recommendations.push('Consider sequential execution for link/unlink');
|
|
56
|
+
}
|
|
57
|
+
// Check for output redirection
|
|
58
|
+
if (command.includes('>') || command.includes('>>')) {
|
|
59
|
+
warnings.push('Output redirection in parallel may interleave output');
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
valid: issues.length === 0,
|
|
63
|
+
issues,
|
|
64
|
+
warnings,
|
|
65
|
+
recommendations
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Log validation results
|
|
70
|
+
*/
|
|
71
|
+
static logValidation(result) {
|
|
72
|
+
if (!result.valid) {
|
|
73
|
+
this.logger.error('VALIDATOR_FAILED: Command validation failed for parallel execution | Error Count: ' + result.issues.length + ' | Impact: Cannot proceed safely');
|
|
74
|
+
for (const issue of result.issues) {
|
|
75
|
+
this.logger.error(`VALIDATOR_ERROR_DETAIL: Validation issue | Issue: ${issue}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (result.warnings.length > 0) {
|
|
79
|
+
this.logger.warn('VALIDATOR_WARNINGS: Command validation warnings for parallel execution | Warning Count: ' + result.warnings.length + ' | Impact: May cause issues');
|
|
80
|
+
for (const warning of result.warnings) {
|
|
81
|
+
this.logger.warn(`VALIDATOR_WARNING_DETAIL: Validation warning | Warning: ${warning}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (result.recommendations.length > 0 && this.logger.verbose) {
|
|
85
|
+
this.logger.info('VALIDATOR_RECOMMENDATIONS: Command validation recommendations | Count: ' + result.recommendations.length + ' | Purpose: Improve parallel execution');
|
|
86
|
+
for (const rec of result.recommendations) {
|
|
87
|
+
this.logger.info(`VALIDATOR_RECOMMENDATION_DETAIL: ${rec}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get recommended concurrency for a command type
|
|
93
|
+
*/
|
|
94
|
+
static getRecommendedConcurrency(builtInCommand, cpuCount = 4, command) {
|
|
95
|
+
// If command is provided, check for memory-intensive patterns
|
|
96
|
+
if (command) {
|
|
97
|
+
const memoryIntensivePatterns = [
|
|
98
|
+
{ pattern: /npm\s+test/, message: 'Test execution is memory intensive' },
|
|
99
|
+
{ pattern: /npm\s+run\s+test/, message: 'Test execution is memory intensive' },
|
|
100
|
+
{ pattern: /vitest/, message: 'Vitest execution is memory intensive' },
|
|
101
|
+
{ pattern: /coverage/, message: 'Coverage generation is memory intensive' },
|
|
102
|
+
{ pattern: /npm\s+run\s+precommit/, message: 'Precommit tasks (build+lint+test) are resource intensive' }
|
|
103
|
+
];
|
|
104
|
+
for (const { pattern } of memoryIntensivePatterns) {
|
|
105
|
+
if (pattern.test(command)) {
|
|
106
|
+
// Return lower concurrency for memory intensive tasks: 25% of CPUs, min 2, max 4
|
|
107
|
+
const recommended = Math.max(2, Math.min(4, Math.floor(cpuCount * 0.25)));
|
|
108
|
+
return Math.min(recommended, cpuCount);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
switch (builtInCommand) {
|
|
113
|
+
case 'commit':
|
|
114
|
+
// Lower concurrency for commit to reduce conflicts
|
|
115
|
+
return Math.min(2, cpuCount);
|
|
116
|
+
case 'publish':
|
|
117
|
+
// Moderate concurrency for publish (long-running)
|
|
118
|
+
return Math.max(2, Math.floor(cpuCount / 2));
|
|
119
|
+
case 'link':
|
|
120
|
+
case 'unlink':
|
|
121
|
+
// Very conservative for link operations
|
|
122
|
+
return 1; // Sequential recommended
|
|
123
|
+
default:
|
|
124
|
+
// Full concurrency for general commands
|
|
125
|
+
return cpuCount;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=CommandValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandValidator.js","sourceRoot":"","sources":["../../src/execution/CommandValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DependencyGraph } from '@grunnverk/tree-core';
|
|
2
|
+
import type { ExecutionState } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* DependencyChecker validates package readiness and provides dependency information
|
|
5
|
+
* for the task pool scheduler.
|
|
6
|
+
*/
|
|
7
|
+
export declare class DependencyChecker {
|
|
8
|
+
private graph;
|
|
9
|
+
constructor(graph: DependencyGraph);
|
|
10
|
+
/**
|
|
11
|
+
* Check if a package is ready to execute
|
|
12
|
+
* A package is ready when all its dependencies are completed and none have failed
|
|
13
|
+
*/
|
|
14
|
+
isReady(packageName: string, state: ExecutionState): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Get count of packages that depend on this one
|
|
17
|
+
* Higher count = higher priority (unlocks more packages)
|
|
18
|
+
*/
|
|
19
|
+
getDependentCount(packageName: string): number;
|
|
20
|
+
/**
|
|
21
|
+
* Get depth of package in dependency tree
|
|
22
|
+
* Depth = longest path from a root package (package with no dependencies)
|
|
23
|
+
* Lower depth = higher priority (can unlock dependent packages sooner)
|
|
24
|
+
*/
|
|
25
|
+
getDepth(packageName: string): number;
|
|
26
|
+
/**
|
|
27
|
+
* Get all dependencies for a package
|
|
28
|
+
*/
|
|
29
|
+
getDependencies(packageName: string): Set<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Get all dependents (packages that depend on this one)
|
|
32
|
+
*/
|
|
33
|
+
getDependents(packageName: string): Set<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Check if package has any dependencies
|
|
36
|
+
*/
|
|
37
|
+
hasDependencies(packageName: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Check if package has any dependents
|
|
40
|
+
*/
|
|
41
|
+
hasDependents(packageName: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Get packages that are blocked by a failed package
|
|
44
|
+
*/
|
|
45
|
+
getBlockedPackages(failedPackage: string, state: ExecutionState): Set<string>;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=DependencyChecker.d.ts.map
|