@eldrforge/kodrdriv 1.2.133 → 1.2.135
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/.cursor/rules/no-local-dependencies.md +6 -0
- package/AI-GUIDE.md +4 -1
- package/LICENSE +1 -1
- package/dist/application.js +34 -42
- package/dist/application.js.map +1 -1
- package/dist/arguments.js +3 -3
- package/dist/arguments.js.map +1 -1
- package/dist/constants.js +5 -7
- package/dist/constants.js.map +1 -1
- package/dist/logging.js +4 -32
- package/dist/logging.js.map +1 -1
- package/dist/types.js +283 -0
- package/dist/types.js.map +1 -0
- package/guide/ai-system.md +3 -0
- package/guide/architecture.md +3 -0
- package/guide/commands.md +3 -0
- package/guide/configuration.md +3 -0
- package/guide/debugging.md +4 -1
- package/guide/development.md +3 -0
- package/guide/index.md +4 -1
- package/guide/integration.md +3 -0
- package/guide/monorepo.md +3 -0
- package/guide/quickstart.md +3 -0
- package/guide/testing.md +3 -0
- package/guide/tree-operations.md +3 -0
- package/guide/usage.md +3 -0
- package/package.json +15 -10
- package/DUPLICATION-CLEANUP.md +0 -104
- package/agentic-reflection-commit-2025-12-27T22-56-06-143Z.md +0 -50
- package/agentic-reflection-commit-2025-12-27T23-01-57-294Z.md +0 -50
- package/agentic-reflection-commit-2025-12-27T23-11-57-811Z.md +0 -50
- package/agentic-reflection-commit-2025-12-27T23-12-50-645Z.md +0 -50
- package/agentic-reflection-commit-2025-12-27T23-13-59-347Z.md +0 -52
- package/agentic-reflection-commit-2025-12-27T23-14-36-001Z.md +0 -50
- package/agentic-reflection-commit-2025-12-27T23-18-59-832Z.md +0 -50
- package/agentic-reflection-commit-2025-12-27T23-25-20-667Z.md +0 -62
- package/dist/commands/audio-commit.js +0 -152
- package/dist/commands/audio-commit.js.map +0 -1
- package/dist/commands/audio-review.js +0 -274
- package/dist/commands/audio-review.js.map +0 -1
- package/dist/commands/clean.js +0 -49
- package/dist/commands/clean.js.map +0 -1
- package/dist/commands/commit.js +0 -680
- package/dist/commands/commit.js.map +0 -1
- package/dist/commands/development.js +0 -467
- package/dist/commands/development.js.map +0 -1
- package/dist/commands/link.js +0 -646
- package/dist/commands/link.js.map +0 -1
- package/dist/commands/precommit.js +0 -99
- package/dist/commands/precommit.js.map +0 -1
- package/dist/commands/publish.js +0 -1432
- package/dist/commands/publish.js.map +0 -1
- package/dist/commands/release.js +0 -376
- package/dist/commands/release.js.map +0 -1
- package/dist/commands/review.js +0 -733
- package/dist/commands/review.js.map +0 -1
- package/dist/commands/select-audio.js +0 -46
- package/dist/commands/select-audio.js.map +0 -1
- package/dist/commands/tree.js +0 -2363
- package/dist/commands/tree.js.map +0 -1
- package/dist/commands/unlink.js +0 -537
- package/dist/commands/unlink.js.map +0 -1
- package/dist/commands/updates.js +0 -211
- package/dist/commands/updates.js.map +0 -1
- package/dist/commands/versions.js +0 -221
- package/dist/commands/versions.js.map +0 -1
- package/dist/content/diff.js +0 -346
- package/dist/content/diff.js.map +0 -1
- package/dist/content/files.js +0 -190
- package/dist/content/files.js.map +0 -1
- package/dist/content/log.js +0 -72
- package/dist/content/log.js.map +0 -1
- package/dist/util/aiAdapter.js +0 -28
- package/dist/util/aiAdapter.js.map +0 -1
- package/dist/util/fileLock.js +0 -241
- package/dist/util/fileLock.js.map +0 -1
- package/dist/util/general.js +0 -379
- package/dist/util/general.js.map +0 -1
- package/dist/util/gitMutex.js +0 -161
- package/dist/util/gitMutex.js.map +0 -1
- package/dist/util/interactive.js +0 -32
- package/dist/util/interactive.js.map +0 -1
- package/dist/util/loggerAdapter.js +0 -41
- package/dist/util/loggerAdapter.js.map +0 -1
- package/dist/util/performance.js +0 -134
- package/dist/util/performance.js.map +0 -1
- package/dist/util/precommitOptimizations.js +0 -310
- package/dist/util/precommitOptimizations.js.map +0 -1
- package/dist/util/stopContext.js +0 -146
- package/dist/util/stopContext.js.map +0 -1
- package/dist/util/storageAdapter.js +0 -31
- package/dist/util/storageAdapter.js.map +0 -1
- package/dist/util/validation.js +0 -45
- package/dist/util/validation.js.map +0 -1
- package/dist/utils/branchState.js +0 -700
- package/dist/utils/branchState.js.map +0 -1
package/dist/util/fileLock.js
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import * as os from 'os';
|
|
4
|
-
import { getLogger } from '../logging.js';
|
|
5
|
-
|
|
6
|
-
// eslint-disable-next-line no-restricted-imports
|
|
7
|
-
function _define_property(obj, key, value) {
|
|
8
|
-
if (key in obj) {
|
|
9
|
-
Object.defineProperty(obj, key, {
|
|
10
|
-
value: value,
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
writable: true
|
|
14
|
-
});
|
|
15
|
-
} else {
|
|
16
|
-
obj[key] = value;
|
|
17
|
-
}
|
|
18
|
-
return obj;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* File-based lock for cross-process synchronization
|
|
22
|
-
* Uses atomic file operations to coordinate across multiple Node processes
|
|
23
|
-
*/ class FileLock {
|
|
24
|
-
/**
|
|
25
|
-
* Acquire the file lock with exponential backoff retry
|
|
26
|
-
*/ async lock() {
|
|
27
|
-
let attempts = 0;
|
|
28
|
-
let currentDelay = this.retryDelay;
|
|
29
|
-
while(attempts < this.maxRetries){
|
|
30
|
-
try {
|
|
31
|
-
// Try to create lock file atomically with 'wx' flag (fails if exists)
|
|
32
|
-
const lockData = {
|
|
33
|
-
pid: process.pid,
|
|
34
|
-
timestamp: Date.now(),
|
|
35
|
-
hostname: os.hostname()
|
|
36
|
-
};
|
|
37
|
-
// Check if lock file exists and is stale
|
|
38
|
-
if (fs.existsSync(this.lockPath)) {
|
|
39
|
-
const lockContent = fs.readFileSync(this.lockPath, 'utf-8');
|
|
40
|
-
try {
|
|
41
|
-
const existingLock = JSON.parse(lockContent);
|
|
42
|
-
const lockAge = Date.now() - existingLock.timestamp;
|
|
43
|
-
// If lock is stale, try to remove it
|
|
44
|
-
if (lockAge > this.lockTimeout) {
|
|
45
|
-
this.logger.debug(`Removing stale lock file (age: ${lockAge}ms, pid: ${existingLock.pid})`);
|
|
46
|
-
try {
|
|
47
|
-
fs.unlinkSync(this.lockPath);
|
|
48
|
-
} catch {
|
|
49
|
-
// Lock might have been removed by another process, continue
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
} catch {
|
|
53
|
-
// Invalid lock file, try to remove it
|
|
54
|
-
try {
|
|
55
|
-
fs.unlinkSync(this.lockPath);
|
|
56
|
-
} catch {
|
|
57
|
-
// Ignore errors
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
// Try to acquire lock
|
|
62
|
-
fs.writeFileSync(this.lockPath, JSON.stringify(lockData, null, 2), {
|
|
63
|
-
flag: 'wx'
|
|
64
|
-
});
|
|
65
|
-
this.lockAcquired = true;
|
|
66
|
-
if (attempts > 0) {
|
|
67
|
-
this.logger.debug(`Acquired file lock after ${attempts} attempts: ${this.lockPath}`);
|
|
68
|
-
}
|
|
69
|
-
return;
|
|
70
|
-
} catch (error) {
|
|
71
|
-
if (error.code === 'EEXIST') {
|
|
72
|
-
// Lock file exists, retry with backoff
|
|
73
|
-
attempts++;
|
|
74
|
-
if (attempts === 1 || attempts % 10 === 0) {
|
|
75
|
-
this.logger.verbose(`Waiting for file lock (attempt ${attempts}/${this.maxRetries}): ${this.lockPath}`);
|
|
76
|
-
}
|
|
77
|
-
await new Promise((resolve)=>setTimeout(resolve, currentDelay));
|
|
78
|
-
// Exponential backoff
|
|
79
|
-
currentDelay = Math.min(currentDelay * 1.5, this.maxRetryDelay);
|
|
80
|
-
} else {
|
|
81
|
-
// Unexpected error
|
|
82
|
-
throw new Error(`Failed to acquire file lock ${this.lockPath}: ${error.message}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
throw new Error(`Failed to acquire file lock after ${this.maxRetries} attempts: ${this.lockPath}`);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Release the file lock
|
|
90
|
-
*/ unlock() {
|
|
91
|
-
if (!this.lockAcquired) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
if (fs.existsSync(this.lockPath)) {
|
|
96
|
-
fs.unlinkSync(this.lockPath);
|
|
97
|
-
}
|
|
98
|
-
this.lockAcquired = false;
|
|
99
|
-
this.logger.silly(`Released file lock: ${this.lockPath}`);
|
|
100
|
-
} catch (error) {
|
|
101
|
-
// Lock file might have been removed by another process or stale lock cleanup
|
|
102
|
-
this.logger.debug(`Error releasing file lock ${this.lockPath}: ${error.message}`);
|
|
103
|
-
this.lockAcquired = false;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Check if this instance currently holds the lock
|
|
108
|
-
*/ isLocked() {
|
|
109
|
-
return this.lockAcquired;
|
|
110
|
-
}
|
|
111
|
-
constructor(lockPath){
|
|
112
|
-
_define_property(this, "lockPath", void 0);
|
|
113
|
-
_define_property(this, "lockAcquired", false);
|
|
114
|
-
_define_property(this, "maxRetries", 100); // Maximum number of lock attempts
|
|
115
|
-
_define_property(this, "retryDelay", 100); // Initial retry delay in ms
|
|
116
|
-
_define_property(this, "maxRetryDelay", 2000); // Maximum retry delay in ms
|
|
117
|
-
_define_property(this, "lockTimeout", 30000); // Consider lock stale after 30 seconds
|
|
118
|
-
_define_property(this, "logger", getLogger());
|
|
119
|
-
this.lockPath = lockPath;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Manages file-based locks for git repositories (cross-process safe)
|
|
124
|
-
*/ class RepositoryFileLockManager {
|
|
125
|
-
/**
|
|
126
|
-
* Get or create a file lock for a specific git repository
|
|
127
|
-
* @param repoPath Path to the git repository root
|
|
128
|
-
* @returns FileLock for this repository
|
|
129
|
-
*/ getRepositoryLock(repoPath) {
|
|
130
|
-
const normalizedPath = path.resolve(repoPath);
|
|
131
|
-
if (!this.locks.has(normalizedPath)) {
|
|
132
|
-
// Resolve the actual .git directory (handles both regular repos and submodules)
|
|
133
|
-
const gitDirPath = this.resolveGitDirectory(normalizedPath);
|
|
134
|
-
const lockPath = path.join(gitDirPath, 'kodrdriv.lock');
|
|
135
|
-
this.logger.debug(`Creating file lock for repository: ${normalizedPath} at ${lockPath}`);
|
|
136
|
-
this.locks.set(normalizedPath, new FileLock(lockPath));
|
|
137
|
-
// Register cleanup handler on first lock creation
|
|
138
|
-
if (!this.cleanupRegistered) {
|
|
139
|
-
this.registerCleanupHandlers();
|
|
140
|
-
this.cleanupRegistered = true;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return this.locks.get(normalizedPath);
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Resolve the actual .git directory path, handling both regular repos and submodules
|
|
147
|
-
* @param repoPath Path to the repository root
|
|
148
|
-
* @returns Path to the actual .git directory
|
|
149
|
-
*/ resolveGitDirectory(repoPath) {
|
|
150
|
-
const gitPath = path.join(repoPath, '.git');
|
|
151
|
-
try {
|
|
152
|
-
const stat = fs.statSync(gitPath);
|
|
153
|
-
if (stat.isDirectory()) {
|
|
154
|
-
// Regular git repository
|
|
155
|
-
return gitPath;
|
|
156
|
-
} else if (stat.isFile()) {
|
|
157
|
-
// Git submodule - .git is a file with format: gitdir: <path>
|
|
158
|
-
const gitFileContent = fs.readFileSync(gitPath, 'utf-8').trim();
|
|
159
|
-
const match = gitFileContent.match(/^gitdir:\s*(.+)$/);
|
|
160
|
-
if (match && match[1]) {
|
|
161
|
-
// Resolve the gitdir path (it's relative to the repo path)
|
|
162
|
-
const gitDirPath = path.resolve(repoPath, match[1]);
|
|
163
|
-
this.logger.debug(`Resolved submodule gitdir: ${gitDirPath}`);
|
|
164
|
-
// Ensure the git directory exists
|
|
165
|
-
if (!fs.existsSync(gitDirPath)) {
|
|
166
|
-
throw new Error(`Submodule git directory does not exist: ${gitDirPath}`);
|
|
167
|
-
}
|
|
168
|
-
return gitDirPath;
|
|
169
|
-
}
|
|
170
|
-
throw new Error(`Invalid .git file format in ${gitPath}: ${gitFileContent}`);
|
|
171
|
-
}
|
|
172
|
-
} catch (error) {
|
|
173
|
-
// Check if error is from statSync (file doesn't exist)
|
|
174
|
-
if (error.code === 'ENOENT') {
|
|
175
|
-
throw new Error(`No .git directory or file found in ${repoPath}`);
|
|
176
|
-
}
|
|
177
|
-
throw new Error(`Failed to resolve git directory for ${repoPath}: ${error.message}`);
|
|
178
|
-
}
|
|
179
|
-
throw new Error(`No .git directory or file found in ${repoPath}`);
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Register cleanup handlers to release locks on process exit
|
|
183
|
-
*/ registerCleanupHandlers() {
|
|
184
|
-
const cleanup = ()=>{
|
|
185
|
-
this.destroy();
|
|
186
|
-
};
|
|
187
|
-
// Handle various exit scenarios
|
|
188
|
-
process.on('exit', cleanup);
|
|
189
|
-
process.on('SIGINT', ()=>{
|
|
190
|
-
cleanup();
|
|
191
|
-
process.exit(130); // Standard exit code for SIGINT
|
|
192
|
-
});
|
|
193
|
-
process.on('SIGTERM', ()=>{
|
|
194
|
-
cleanup();
|
|
195
|
-
process.exit(143); // Standard exit code for SIGTERM
|
|
196
|
-
});
|
|
197
|
-
process.on('uncaughtException', (error)=>{
|
|
198
|
-
this.logger.error('FILELOCK_UNCAUGHT_EXCEPTION: Uncaught exception detected, cleaning up locks | Error: ' + error + ' | Action: Release all locks');
|
|
199
|
-
cleanup();
|
|
200
|
-
process.exit(1);
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Execute a git operation with repository-level file locking
|
|
205
|
-
* @param repoPath Path to the git repository root
|
|
206
|
-
* @param operation The async operation to execute under lock
|
|
207
|
-
* @param operationName Optional name for logging
|
|
208
|
-
* @returns Result of the operation
|
|
209
|
-
*/ async withGitLock(repoPath, operation, operationName) {
|
|
210
|
-
const lock = this.getRepositoryLock(repoPath);
|
|
211
|
-
const startWait = Date.now();
|
|
212
|
-
this.logger.silly(`Acquiring file lock for ${repoPath}${operationName ? ` for: ${operationName}` : ''}`);
|
|
213
|
-
await lock.lock();
|
|
214
|
-
const waitTime = Date.now() - startWait;
|
|
215
|
-
if (waitTime > 100) {
|
|
216
|
-
this.logger.debug(`Acquired file lock for ${repoPath} after ${waitTime}ms${operationName ? ` for: ${operationName}` : ''}`);
|
|
217
|
-
}
|
|
218
|
-
try {
|
|
219
|
-
return await operation();
|
|
220
|
-
} finally{
|
|
221
|
-
lock.unlock();
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Clean up all locks
|
|
226
|
-
*/ destroy() {
|
|
227
|
-
this.logger.debug(`Cleaning up ${this.locks.size} file lock(s)`);
|
|
228
|
-
for (const lock of this.locks.values()){
|
|
229
|
-
lock.unlock();
|
|
230
|
-
}
|
|
231
|
-
this.locks.clear();
|
|
232
|
-
}
|
|
233
|
-
constructor(){
|
|
234
|
-
_define_property(this, "locks", new Map());
|
|
235
|
-
_define_property(this, "logger", getLogger());
|
|
236
|
-
_define_property(this, "cleanupRegistered", false);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
export { FileLock, RepositoryFileLockManager };
|
|
241
|
-
//# sourceMappingURL=fileLock.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fileLock.js","sources":["../../src/util/fileLock.ts"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { getLogger } from '../logging';\n\n/**\n * File-based lock for cross-process synchronization\n * Uses atomic file operations to coordinate across multiple Node processes\n */\nexport class FileLock {\n private lockPath: string;\n private lockAcquired = false;\n private maxRetries = 100; // Maximum number of lock attempts\n private retryDelay = 100; // Initial retry delay in ms\n private maxRetryDelay = 2000; // Maximum retry delay in ms\n private lockTimeout = 30000; // Consider lock stale after 30 seconds\n private logger = getLogger();\n\n constructor(lockPath: string) {\n this.lockPath = lockPath;\n }\n\n /**\n * Acquire the file lock with exponential backoff retry\n */\n async lock(): Promise<void> {\n let attempts = 0;\n let currentDelay = this.retryDelay;\n\n while (attempts < this.maxRetries) {\n try {\n // Try to create lock file atomically with 'wx' flag (fails if exists)\n const lockData = {\n pid: process.pid,\n timestamp: Date.now(),\n hostname: os.hostname()\n };\n\n // Check if lock file exists and is stale\n if (fs.existsSync(this.lockPath)) {\n const lockContent = fs.readFileSync(this.lockPath, 'utf-8');\n try {\n const existingLock = JSON.parse(lockContent);\n const lockAge = Date.now() - existingLock.timestamp;\n\n // If lock is stale, try to remove it\n if (lockAge > this.lockTimeout) {\n this.logger.debug(`Removing stale lock file (age: ${lockAge}ms, pid: ${existingLock.pid})`);\n try {\n fs.unlinkSync(this.lockPath);\n } catch {\n // Lock might have been removed by another process, continue\n }\n }\n } catch {\n // Invalid lock file, try to remove it\n try {\n fs.unlinkSync(this.lockPath);\n } catch {\n // Ignore errors\n }\n }\n }\n\n // Try to acquire lock\n fs.writeFileSync(this.lockPath, JSON.stringify(lockData, null, 2), { flag: 'wx' });\n this.lockAcquired = true;\n\n if (attempts > 0) {\n this.logger.debug(`Acquired file lock after ${attempts} attempts: ${this.lockPath}`);\n }\n\n return;\n } catch (error: any) {\n if (error.code === 'EEXIST') {\n // Lock file exists, retry with backoff\n attempts++;\n\n if (attempts === 1 || attempts % 10 === 0) {\n this.logger.verbose(`Waiting for file lock (attempt ${attempts}/${this.maxRetries}): ${this.lockPath}`);\n }\n\n await new Promise(resolve => setTimeout(resolve, currentDelay));\n\n // Exponential backoff\n currentDelay = Math.min(currentDelay * 1.5, this.maxRetryDelay);\n } else {\n // Unexpected error\n throw new Error(`Failed to acquire file lock ${this.lockPath}: ${error.message}`);\n }\n }\n }\n\n throw new Error(`Failed to acquire file lock after ${this.maxRetries} attempts: ${this.lockPath}`);\n }\n\n /**\n * Release the file lock\n */\n unlock(): void {\n if (!this.lockAcquired) {\n return;\n }\n\n try {\n if (fs.existsSync(this.lockPath)) {\n fs.unlinkSync(this.lockPath);\n }\n this.lockAcquired = false;\n this.logger.silly(`Released file lock: ${this.lockPath}`);\n } catch (error: any) {\n // Lock file might have been removed by another process or stale lock cleanup\n this.logger.debug(`Error releasing file lock ${this.lockPath}: ${error.message}`);\n this.lockAcquired = false;\n }\n }\n\n /**\n * Check if this instance currently holds the lock\n */\n isLocked(): boolean {\n return this.lockAcquired;\n }\n}\n\n/**\n * Manages file-based locks for git repositories (cross-process safe)\n */\nexport class RepositoryFileLockManager {\n private locks: Map<string, FileLock> = new Map();\n private logger = getLogger();\n private cleanupRegistered = false;\n\n /**\n * Get or create a file lock for a specific git repository\n * @param repoPath Path to the git repository root\n * @returns FileLock for this repository\n */\n getRepositoryLock(repoPath: string): FileLock {\n const normalizedPath = path.resolve(repoPath);\n\n if (!this.locks.has(normalizedPath)) {\n // Resolve the actual .git directory (handles both regular repos and submodules)\n const gitDirPath = this.resolveGitDirectory(normalizedPath);\n const lockPath = path.join(gitDirPath, 'kodrdriv.lock');\n this.logger.debug(`Creating file lock for repository: ${normalizedPath} at ${lockPath}`);\n this.locks.set(normalizedPath, new FileLock(lockPath));\n\n // Register cleanup handler on first lock creation\n if (!this.cleanupRegistered) {\n this.registerCleanupHandlers();\n this.cleanupRegistered = true;\n }\n }\n\n return this.locks.get(normalizedPath)!;\n }\n\n /**\n * Resolve the actual .git directory path, handling both regular repos and submodules\n * @param repoPath Path to the repository root\n * @returns Path to the actual .git directory\n */\n private resolveGitDirectory(repoPath: string): string {\n const gitPath = path.join(repoPath, '.git');\n\n try {\n const stat = fs.statSync(gitPath);\n\n if (stat.isDirectory()) {\n // Regular git repository\n return gitPath;\n } else if (stat.isFile()) {\n // Git submodule - .git is a file with format: gitdir: <path>\n const gitFileContent = fs.readFileSync(gitPath, 'utf-8').trim();\n const match = gitFileContent.match(/^gitdir:\\s*(.+)$/);\n\n if (match && match[1]) {\n // Resolve the gitdir path (it's relative to the repo path)\n const gitDirPath = path.resolve(repoPath, match[1]);\n this.logger.debug(`Resolved submodule gitdir: ${gitDirPath}`);\n\n // Ensure the git directory exists\n if (!fs.existsSync(gitDirPath)) {\n throw new Error(`Submodule git directory does not exist: ${gitDirPath}`);\n }\n\n return gitDirPath;\n }\n\n throw new Error(`Invalid .git file format in ${gitPath}: ${gitFileContent}`);\n }\n } catch (error: any) {\n // Check if error is from statSync (file doesn't exist)\n if (error.code === 'ENOENT') {\n throw new Error(`No .git directory or file found in ${repoPath}`);\n }\n throw new Error(`Failed to resolve git directory for ${repoPath}: ${error.message}`);\n }\n\n throw new Error(`No .git directory or file found in ${repoPath}`);\n }\n\n /**\n * Register cleanup handlers to release locks on process exit\n */\n private registerCleanupHandlers(): void {\n const cleanup = () => {\n this.destroy();\n };\n\n // Handle various exit scenarios\n process.on('exit', cleanup);\n process.on('SIGINT', () => {\n cleanup();\n process.exit(130); // Standard exit code for SIGINT\n });\n process.on('SIGTERM', () => {\n cleanup();\n process.exit(143); // Standard exit code for SIGTERM\n });\n process.on('uncaughtException', (error) => {\n this.logger.error('FILELOCK_UNCAUGHT_EXCEPTION: Uncaught exception detected, cleaning up locks | Error: ' + error + ' | Action: Release all locks');\n cleanup();\n process.exit(1);\n });\n }\n\n /**\n * Execute a git operation with repository-level file locking\n * @param repoPath Path to the git repository root\n * @param operation The async operation to execute under lock\n * @param operationName Optional name for logging\n * @returns Result of the operation\n */\n async withGitLock<T>(\n repoPath: string,\n operation: () => Promise<T>,\n operationName?: string\n ): Promise<T> {\n const lock = this.getRepositoryLock(repoPath);\n const startWait = Date.now();\n\n this.logger.silly(\n `Acquiring file lock for ${repoPath}${operationName ? ` for: ${operationName}` : ''}`\n );\n\n await lock.lock();\n\n const waitTime = Date.now() - startWait;\n if (waitTime > 100) {\n this.logger.debug(\n `Acquired file lock for ${repoPath} after ${waitTime}ms${operationName ? ` for: ${operationName}` : ''}`\n );\n }\n\n try {\n return await operation();\n } finally {\n lock.unlock();\n }\n }\n\n /**\n * Clean up all locks\n */\n destroy(): void {\n this.logger.debug(`Cleaning up ${this.locks.size} file lock(s)`);\n for (const lock of this.locks.values()) {\n lock.unlock();\n }\n this.locks.clear();\n }\n}\n"],"names":["FileLock","lock","attempts","currentDelay","retryDelay","maxRetries","lockData","pid","process","timestamp","Date","now","hostname","os","fs","existsSync","lockPath","lockContent","readFileSync","existingLock","JSON","parse","lockAge","lockTimeout","logger","debug","unlinkSync","writeFileSync","stringify","flag","lockAcquired","error","code","verbose","Promise","resolve","setTimeout","Math","min","maxRetryDelay","Error","message","unlock","silly","isLocked","getLogger","RepositoryFileLockManager","getRepositoryLock","repoPath","normalizedPath","path","locks","has","gitDirPath","resolveGitDirectory","join","set","cleanupRegistered","registerCleanupHandlers","get","gitPath","stat","statSync","isDirectory","isFile","gitFileContent","trim","match","cleanup","destroy","on","exit","withGitLock","operation","operationName","startWait","waitTime","size","values","clear","Map"],"mappings":";;;;;AAAA;;;;;;;;;;;;;;AAMA;;;AAGC,IACM,MAAMA,QAAAA,CAAAA;AAaT;;AAEC,QACD,MAAMC,IAAAA,GAAsB;AACxB,QAAA,IAAIC,QAAAA,GAAW,CAAA;QACf,IAAIC,YAAAA,GAAe,IAAI,CAACC,UAAU;AAElC,QAAA,MAAOF,QAAAA,GAAW,IAAI,CAACG,UAAU,CAAE;YAC/B,IAAI;;AAEA,gBAAA,MAAMC,QAAAA,GAAW;AACbC,oBAAAA,GAAAA,EAAKC,QAAQD,GAAG;AAChBE,oBAAAA,SAAAA,EAAWC,KAAKC,GAAG,EAAA;AACnBC,oBAAAA,QAAAA,EAAUC,GAAGD,QAAQ;AACzB,iBAAA;;AAGA,gBAAA,IAAIE,GAAGC,UAAU,CAAC,IAAI,CAACC,QAAQ,CAAA,EAAG;AAC9B,oBAAA,MAAMC,cAAcH,EAAAA,CAAGI,YAAY,CAAC,IAAI,CAACF,QAAQ,EAAE,OAAA,CAAA;oBACnD,IAAI;wBACA,MAAMG,YAAAA,GAAeC,IAAAA,CAAKC,KAAK,CAACJ,WAAAA,CAAAA;AAChC,wBAAA,MAAMK,OAAAA,GAAUZ,IAAAA,CAAKC,GAAG,EAAA,GAAKQ,aAAaV,SAAS;;AAGnD,wBAAA,IAAIa,OAAAA,GAAU,IAAI,CAACC,WAAW,EAAE;AAC5B,4BAAA,IAAI,CAACC,MAAM,CAACC,KAAK,CAAC,CAAC,+BAA+B,EAAEH,OAAAA,CAAQ,SAAS,EAAEH,YAAAA,CAAaZ,GAAG,CAAC,CAAC,CAAC,CAAA;4BAC1F,IAAI;AACAO,gCAAAA,EAAAA,CAAGY,UAAU,CAAC,IAAI,CAACV,QAAQ,CAAA;AAC/B,4BAAA,CAAA,CAAE,OAAM;;AAER,4BAAA;AACJ,wBAAA;AACJ,oBAAA,CAAA,CAAE,OAAM;;wBAEJ,IAAI;AACAF,4BAAAA,EAAAA,CAAGY,UAAU,CAAC,IAAI,CAACV,QAAQ,CAAA;AAC/B,wBAAA,CAAA,CAAE,OAAM;;AAER,wBAAA;AACJ,oBAAA;AACJ,gBAAA;;gBAGAF,EAAAA,CAAGa,aAAa,CAAC,IAAI,CAACX,QAAQ,EAAEI,IAAAA,CAAKQ,SAAS,CAACtB,QAAAA,EAAU,IAAA,EAAM,CAAA,CAAA,EAAI;oBAAEuB,IAAAA,EAAM;AAAK,iBAAA,CAAA;gBAChF,IAAI,CAACC,YAAY,GAAG,IAAA;AAEpB,gBAAA,IAAI5B,WAAW,CAAA,EAAG;AACd,oBAAA,IAAI,CAACsB,MAAM,CAACC,KAAK,CAAC,CAAC,yBAAyB,EAAEvB,QAAAA,CAAS,WAAW,EAAE,IAAI,CAACc,QAAQ,CAAA,CAAE,CAAA;AACvF,gBAAA;AAEA,gBAAA;AACJ,YAAA,CAAA,CAAE,OAAOe,KAAAA,EAAY;gBACjB,IAAIA,KAAAA,CAAMC,IAAI,KAAK,QAAA,EAAU;;AAEzB9B,oBAAAA,QAAAA,EAAAA;AAEA,oBAAA,IAAIA,QAAAA,KAAa,CAAA,IAAKA,QAAAA,GAAW,EAAA,KAAO,CAAA,EAAG;wBACvC,IAAI,CAACsB,MAAM,CAACS,OAAO,CAAC,CAAC,+BAA+B,EAAE/B,QAAAA,CAAS,CAAC,EAAE,IAAI,CAACG,UAAU,CAAC,GAAG,EAAE,IAAI,CAACW,QAAQ,CAAA,CAAE,CAAA;AAC1G,oBAAA;AAEA,oBAAA,MAAM,IAAIkB,OAAAA,CAAQC,CAAAA,OAAAA,GAAWC,WAAWD,OAAAA,EAAShC,YAAAA,CAAAA,CAAAA;;AAGjDA,oBAAAA,YAAAA,GAAekC,KAAKC,GAAG,CAACnC,eAAe,GAAA,EAAK,IAAI,CAACoC,aAAa,CAAA;gBAClE,CAAA,MAAO;;AAEH,oBAAA,MAAM,IAAIC,KAAAA,CAAM,CAAC,4BAA4B,EAAE,IAAI,CAACxB,QAAQ,CAAC,EAAE,EAAEe,KAAAA,CAAMU,OAAO,CAAA,CAAE,CAAA;AACpF,gBAAA;AACJ,YAAA;AACJ,QAAA;AAEA,QAAA,MAAM,IAAID,KAAAA,CAAM,CAAC,kCAAkC,EAAE,IAAI,CAACnC,UAAU,CAAC,WAAW,EAAE,IAAI,CAACW,QAAQ,CAAA,CAAE,CAAA;AACrG,IAAA;AAEA;;AAEC,QACD0B,MAAAA,GAAe;AACX,QAAA,IAAI,CAAC,IAAI,CAACZ,YAAY,EAAE;AACpB,YAAA;AACJ,QAAA;QAEA,IAAI;AACA,YAAA,IAAIhB,GAAGC,UAAU,CAAC,IAAI,CAACC,QAAQ,CAAA,EAAG;AAC9BF,gBAAAA,EAAAA,CAAGY,UAAU,CAAC,IAAI,CAACV,QAAQ,CAAA;AAC/B,YAAA;YACA,IAAI,CAACc,YAAY,GAAG,KAAA;YACpB,IAAI,CAACN,MAAM,CAACmB,KAAK,CAAC,CAAC,oBAAoB,EAAE,IAAI,CAAC3B,QAAQ,CAAA,CAAE,CAAA;AAC5D,QAAA,CAAA,CAAE,OAAOe,KAAAA,EAAY;;AAEjB,YAAA,IAAI,CAACP,MAAM,CAACC,KAAK,CAAC,CAAC,0BAA0B,EAAE,IAAI,CAACT,QAAQ,CAAC,EAAE,EAAEe,KAAAA,CAAMU,OAAO,CAAA,CAAE,CAAA;YAChF,IAAI,CAACX,YAAY,GAAG,KAAA;AACxB,QAAA;AACJ,IAAA;AAEA;;AAEC,QACDc,QAAAA,GAAoB;QAChB,OAAO,IAAI,CAACd,YAAY;AAC5B,IAAA;AAxGA,IAAA,WAAA,CAAYd,QAAgB,CAAE;AAR9B,QAAA,gBAAA,CAAA,IAAA,EAAQA,YAAR,MAAA,CAAA;AACA,QAAA,gBAAA,CAAA,IAAA,EAAQc,cAAAA,EAAe,KAAA,CAAA;QACvB,gBAAA,CAAA,IAAA,EAAQzB,YAAAA,EAAa;QACrB,gBAAA,CAAA,IAAA,EAAQD,YAAAA,EAAa;QACrB,gBAAA,CAAA,IAAA,EAAQmC,eAAAA,EAAgB;QACxB,gBAAA,CAAA,IAAA,EAAQhB,aAAAA,EAAc;AACtB,QAAA,gBAAA,CAAA,IAAA,EAAQC,QAAAA,EAASqB,SAAAA,EAAAA,CAAAA;QAGb,IAAI,CAAC7B,QAAQ,GAAGA,QAAAA;AACpB,IAAA;AAuGJ;AAEA;;AAEC,IACM,MAAM8B,yBAAAA,CAAAA;AAKT;;;;QAKAC,iBAAAA,CAAkBC,QAAgB,EAAY;QAC1C,MAAMC,cAAAA,GAAiBC,IAAAA,CAAKf,OAAO,CAACa,QAAAA,CAAAA;AAEpC,QAAA,IAAI,CAAC,IAAI,CAACG,KAAK,CAACC,GAAG,CAACH,cAAAA,CAAAA,EAAiB;;AAEjC,YAAA,MAAMI,UAAAA,GAAa,IAAI,CAACC,mBAAmB,CAACL,cAAAA,CAAAA;AAC5C,YAAA,MAAMjC,QAAAA,GAAWkC,IAAAA,CAAKK,IAAI,CAACF,UAAAA,EAAY,eAAA,CAAA;YACvC,IAAI,CAAC7B,MAAM,CAACC,KAAK,CAAC,CAAC,mCAAmC,EAAEwB,cAAAA,CAAe,IAAI,EAAEjC,QAAAA,CAAAA,CAAU,CAAA;AACvF,YAAA,IAAI,CAACmC,KAAK,CAACK,GAAG,CAACP,cAAAA,EAAgB,IAAIjD,QAAAA,CAASgB,QAAAA,CAAAA,CAAAA;;AAG5C,YAAA,IAAI,CAAC,IAAI,CAACyC,iBAAiB,EAAE;AACzB,gBAAA,IAAI,CAACC,uBAAuB,EAAA;gBAC5B,IAAI,CAACD,iBAAiB,GAAG,IAAA;AAC7B,YAAA;AACJ,QAAA;AAEA,QAAA,OAAO,IAAI,CAACN,KAAK,CAACQ,GAAG,CAACV,cAAAA,CAAAA;AAC1B,IAAA;AAEA;;;;QAKQK,mBAAAA,CAAoBN,QAAgB,EAAU;AAClD,QAAA,MAAMY,OAAAA,GAAUV,IAAAA,CAAKK,IAAI,CAACP,QAAAA,EAAU,MAAA,CAAA;QAEpC,IAAI;YACA,MAAMa,IAAAA,GAAO/C,EAAAA,CAAGgD,QAAQ,CAACF,OAAAA,CAAAA;YAEzB,IAAIC,IAAAA,CAAKE,WAAW,EAAA,EAAI;;gBAEpB,OAAOH,OAAAA;YACX,CAAA,MAAO,IAAIC,IAAAA,CAAKG,MAAM,EAAA,EAAI;;AAEtB,gBAAA,MAAMC,iBAAiBnD,EAAAA,CAAGI,YAAY,CAAC0C,OAAAA,EAAS,SAASM,IAAI,EAAA;gBAC7D,MAAMC,KAAAA,GAAQF,cAAAA,CAAeE,KAAK,CAAC,kBAAA,CAAA;AAEnC,gBAAA,IAAIA,KAAAA,IAASA,KAAK,CAAC,CAAA,CAAE,EAAE;;AAEnB,oBAAA,MAAMd,aAAaH,IAAAA,CAAKf,OAAO,CAACa,QAAAA,EAAUmB,KAAK,CAAC,CAAA,CAAE,CAAA;oBAClD,IAAI,CAAC3C,MAAM,CAACC,KAAK,CAAC,CAAC,2BAA2B,EAAE4B,UAAAA,CAAAA,CAAY,CAAA;;AAG5D,oBAAA,IAAI,CAACvC,EAAAA,CAAGC,UAAU,CAACsC,UAAAA,CAAAA,EAAa;AAC5B,wBAAA,MAAM,IAAIb,KAAAA,CAAM,CAAC,wCAAwC,EAAEa,UAAAA,CAAAA,CAAY,CAAA;AAC3E,oBAAA;oBAEA,OAAOA,UAAAA;AACX,gBAAA;gBAEA,MAAM,IAAIb,MAAM,CAAC,4BAA4B,EAAEoB,OAAAA,CAAQ,EAAE,EAAEK,cAAAA,CAAAA,CAAgB,CAAA;AAC/E,YAAA;AACJ,QAAA,CAAA,CAAE,OAAOlC,KAAAA,EAAY;;YAEjB,IAAIA,KAAAA,CAAMC,IAAI,KAAK,QAAA,EAAU;AACzB,gBAAA,MAAM,IAAIQ,KAAAA,CAAM,CAAC,mCAAmC,EAAEQ,QAAAA,CAAAA,CAAU,CAAA;AACpE,YAAA;YACA,MAAM,IAAIR,KAAAA,CAAM,CAAC,oCAAoC,EAAEQ,SAAS,EAAE,EAAEjB,KAAAA,CAAMU,OAAO,CAAA,CAAE,CAAA;AACvF,QAAA;AAEA,QAAA,MAAM,IAAID,KAAAA,CAAM,CAAC,mCAAmC,EAAEQ,QAAAA,CAAAA,CAAU,CAAA;AACpE,IAAA;AAEA;;AAEC,QACD,uBAAQU,GAAgC;AACpC,QAAA,MAAMU,OAAAA,GAAU,IAAA;AACZ,YAAA,IAAI,CAACC,OAAO,EAAA;AAChB,QAAA,CAAA;;QAGA7D,OAAAA,CAAQ8D,EAAE,CAAC,MAAA,EAAQF,OAAAA,CAAAA;QACnB5D,OAAAA,CAAQ8D,EAAE,CAAC,QAAA,EAAU,IAAA;AACjBF,YAAAA,OAAAA,EAAAA;YACA5D,OAAAA,CAAQ+D,IAAI,CAAC,GAAA,CAAA,CAAA;AACjB,QAAA,CAAA,CAAA;QACA/D,OAAAA,CAAQ8D,EAAE,CAAC,SAAA,EAAW,IAAA;AAClBF,YAAAA,OAAAA,EAAAA;YACA5D,OAAAA,CAAQ+D,IAAI,CAAC,GAAA,CAAA,CAAA;AACjB,QAAA,CAAA,CAAA;QACA/D,OAAAA,CAAQ8D,EAAE,CAAC,mBAAA,EAAqB,CAACvC,KAAAA,GAAAA;AAC7B,YAAA,IAAI,CAACP,MAAM,CAACO,KAAK,CAAC,0FAA0FA,KAAAA,GAAQ,8BAAA,CAAA;AACpHqC,YAAAA,OAAAA,EAAAA;AACA5D,YAAAA,OAAAA,CAAQ+D,IAAI,CAAC,CAAA,CAAA;AACjB,QAAA,CAAA,CAAA;AACJ,IAAA;AAEA;;;;;;AAMC,QACD,MAAMC,WAAAA,CACFxB,QAAgB,EAChByB,SAA2B,EAC3BC,aAAsB,EACZ;AACV,QAAA,MAAMzE,IAAAA,GAAO,IAAI,CAAC8C,iBAAiB,CAACC,QAAAA,CAAAA;QACpC,MAAM2B,SAAAA,GAAYjE,KAAKC,GAAG,EAAA;AAE1B,QAAA,IAAI,CAACa,MAAM,CAACmB,KAAK,CACb,CAAC,wBAAwB,EAAEK,QAAAA,CAAAA,EAAW0B,gBAAgB,CAAC,MAAM,EAAEA,aAAAA,CAAAA,CAAe,GAAG,EAAA,CAAA,CAAI,CAAA;AAGzF,QAAA,MAAMzE,KAAKA,IAAI,EAAA;QAEf,MAAM2E,QAAAA,GAAWlE,IAAAA,CAAKC,GAAG,EAAA,GAAKgE,SAAAA;AAC9B,QAAA,IAAIC,WAAW,GAAA,EAAK;YAChB,IAAI,CAACpD,MAAM,CAACC,KAAK,CACb,CAAC,uBAAuB,EAAEuB,QAAAA,CAAS,OAAO,EAAE4B,QAAAA,CAAS,EAAE,EAAEF,aAAAA,GAAgB,CAAC,MAAM,EAAEA,aAAAA,CAAAA,CAAe,GAAG,EAAA,CAAA,CAAI,CAAA;AAEhH,QAAA;QAEA,IAAI;AACA,YAAA,OAAO,MAAMD,SAAAA,EAAAA;QACjB,CAAA,QAAU;AACNxE,YAAAA,IAAAA,CAAKyC,MAAM,EAAA;AACf,QAAA;AACJ,IAAA;AAEA;;AAEC,QACD2B,OAAAA,GAAgB;AACZ,QAAA,IAAI,CAAC7C,MAAM,CAACC,KAAK,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC0B,KAAK,CAAC0B,IAAI,CAAC,aAAa,CAAC,CAAA;AAC/D,QAAA,KAAK,MAAM5E,IAAAA,IAAQ,IAAI,CAACkD,KAAK,CAAC2B,MAAM,EAAA,CAAI;AACpC7E,YAAAA,IAAAA,CAAKyC,MAAM,EAAA;AACf,QAAA;QACA,IAAI,CAACS,KAAK,CAAC4B,KAAK,EAAA;AACpB,IAAA;;AA/IA,QAAA,gBAAA,CAAA,IAAA,EAAQ5B,SAA+B,IAAI6B,GAAAA,EAAAA,CAAAA;AAC3C,QAAA,gBAAA,CAAA,IAAA,EAAQxD,QAAAA,EAASqB,SAAAA,EAAAA,CAAAA;AACjB,QAAA,gBAAA,CAAA,IAAA,EAAQY,mBAAAA,EAAoB,KAAA,CAAA;;AA8IhC;;;;"}
|
package/dist/util/general.js
DELETED
|
@@ -1,379 +0,0 @@
|
|
|
1
|
-
import path__default from 'path';
|
|
2
|
-
import { incrementPatchVersion, validateVersionString, convertToReleaseVersion, incrementPrereleaseVersion } from '@eldrforge/shared';
|
|
3
|
-
export { calculateTargetVersion, convertToReleaseVersion, deepMerge, incrementMajorVersion, incrementMinorVersion, incrementPatchVersion, incrementPrereleaseVersion, stringifyJSON, validateVersionString } from '@eldrforge/shared';
|
|
4
|
-
import { getLogger } from '../logging.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Get version from a specific branch's package.json
|
|
8
|
-
*/ const getVersionFromBranch = async (branchName)=>{
|
|
9
|
-
const { runSecure, validateGitRef, safeJsonParse, validatePackageJson } = await import('@eldrforge/git-tools');
|
|
10
|
-
try {
|
|
11
|
-
// Validate branch name to prevent injection
|
|
12
|
-
if (!validateGitRef(branchName)) {
|
|
13
|
-
throw new Error(`Invalid branch name: ${branchName}`);
|
|
14
|
-
}
|
|
15
|
-
// Cast to any to avoid type mismatch with node_modules version
|
|
16
|
-
const { stdout } = await runSecure('git', [
|
|
17
|
-
'show',
|
|
18
|
-
`${branchName}:package.json`
|
|
19
|
-
], {
|
|
20
|
-
suppressErrorLogging: true
|
|
21
|
-
});
|
|
22
|
-
const packageJson = safeJsonParse(stdout, 'package.json');
|
|
23
|
-
const validated = validatePackageJson(packageJson, 'package.json');
|
|
24
|
-
return validated.version;
|
|
25
|
-
} catch {
|
|
26
|
-
// Return null if we can't get the version (branch may not exist or no package.json)
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Calculate target version based on branch configuration
|
|
32
|
-
* SEMANTICS: The version config specifies what version should be ON the target branch
|
|
33
|
-
*/ const calculateBranchDependentVersion = async (currentVersion, currentBranch, branchesConfig, targetBranch)=>{
|
|
34
|
-
const { getLogger } = await import('../logging.js');
|
|
35
|
-
const logger = getLogger();
|
|
36
|
-
// Look up the source branch to find the target branch
|
|
37
|
-
if (!branchesConfig || !branchesConfig[currentBranch]) {
|
|
38
|
-
// Use default configuration from constants
|
|
39
|
-
const { KODRDRIV_DEFAULTS } = await import('../constants.js');
|
|
40
|
-
const defaultConfig = KODRDRIV_DEFAULTS.branches;
|
|
41
|
-
if (defaultConfig && defaultConfig[currentBranch]) {
|
|
42
|
-
const sourceConfig = defaultConfig[currentBranch];
|
|
43
|
-
const finalTargetBranch = sourceConfig.targetBranch || targetBranch || 'main';
|
|
44
|
-
// Look at target branch's version config to determine what version it should have
|
|
45
|
-
const targetConfig = defaultConfig[finalTargetBranch];
|
|
46
|
-
logger.info(`VERSION_BRANCH_DEFAULT: Using default branch configuration | Source Branch: ${currentBranch} | Target Branch: ${finalTargetBranch} | Source: default config`);
|
|
47
|
-
if (!(targetConfig === null || targetConfig === void 0 ? void 0 : targetConfig.version)) {
|
|
48
|
-
const defaultVersion = incrementPatchVersion(currentVersion);
|
|
49
|
-
logger.debug(`No version config for target branch '${finalTargetBranch}', using default increment`);
|
|
50
|
-
return {
|
|
51
|
-
version: defaultVersion,
|
|
52
|
-
targetBranch: finalTargetBranch
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
return calculateVersionFromTargetConfig(currentVersion, finalTargetBranch, targetConfig.version, logger);
|
|
56
|
-
}
|
|
57
|
-
// No config at all, use traditional defaults
|
|
58
|
-
const defaultTargetBranch = targetBranch || 'main';
|
|
59
|
-
const defaultVersion = incrementPatchVersion(currentVersion);
|
|
60
|
-
logger.debug(`No branch-specific config found for '${currentBranch}', using defaults`);
|
|
61
|
-
return {
|
|
62
|
-
version: defaultVersion,
|
|
63
|
-
targetBranch: defaultTargetBranch
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
const sourceConfig = branchesConfig[currentBranch];
|
|
67
|
-
const finalTargetBranch = sourceConfig.targetBranch || targetBranch || 'main';
|
|
68
|
-
// Look at target branch's version config to determine what version it should have
|
|
69
|
-
const targetConfig = branchesConfig[finalTargetBranch];
|
|
70
|
-
logger.info(`VERSION_BRANCH_DEPENDENT: Using branch-dependent targeting | Source Branch: ${currentBranch} | Target Branch: ${finalTargetBranch} | Source: branch config`);
|
|
71
|
-
if (!(targetConfig === null || targetConfig === void 0 ? void 0 : targetConfig.version)) {
|
|
72
|
-
// No version config for target, use default increment
|
|
73
|
-
const defaultVersion = incrementPatchVersion(currentVersion);
|
|
74
|
-
logger.debug(`No version config for target branch '${finalTargetBranch}', using default increment`);
|
|
75
|
-
return {
|
|
76
|
-
version: defaultVersion,
|
|
77
|
-
targetBranch: finalTargetBranch
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
return calculateVersionFromTargetConfig(currentVersion, finalTargetBranch, targetConfig.version, logger);
|
|
81
|
-
};
|
|
82
|
-
/**
|
|
83
|
-
* Calculate version based on target branch configuration
|
|
84
|
-
*/ const calculateVersionFromTargetConfig = async (currentVersion, targetBranch, versionConfig, logger)=>{
|
|
85
|
-
if (versionConfig.type === 'release') {
|
|
86
|
-
// Convert to release version (remove prerelease tags)
|
|
87
|
-
const releaseVersion = convertToReleaseVersion(currentVersion);
|
|
88
|
-
logger.info(`VERSION_RELEASE_CONVERSION: Converting prerelease to release version | Current: ${currentVersion} | Release: ${releaseVersion} | Action: Remove prerelease tag`);
|
|
89
|
-
return {
|
|
90
|
-
version: releaseVersion,
|
|
91
|
-
targetBranch
|
|
92
|
-
};
|
|
93
|
-
} else if (versionConfig.type === 'prerelease') {
|
|
94
|
-
if (!versionConfig.tag) {
|
|
95
|
-
throw new Error(`Prerelease version type requires a tag in branch configuration`);
|
|
96
|
-
}
|
|
97
|
-
const tag = versionConfig.tag;
|
|
98
|
-
if (versionConfig.increment) {
|
|
99
|
-
// Check if there's already a version with this tag in the target branch
|
|
100
|
-
const targetBranchVersion = await getVersionFromBranch(targetBranch);
|
|
101
|
-
if (targetBranchVersion) {
|
|
102
|
-
// Use the target branch version as the base and increment
|
|
103
|
-
const newVersion = incrementPrereleaseVersion(targetBranchVersion, tag);
|
|
104
|
-
logger.info(`VERSION_PRERELEASE_INCREMENT: Incrementing prerelease version | Current: ${targetBranchVersion} | New: ${newVersion} | Action: Increment prerelease number`);
|
|
105
|
-
return {
|
|
106
|
-
version: newVersion,
|
|
107
|
-
targetBranch
|
|
108
|
-
};
|
|
109
|
-
} else {
|
|
110
|
-
// No version in target branch, use current version as base
|
|
111
|
-
const newVersion = incrementPrereleaseVersion(currentVersion, tag);
|
|
112
|
-
logger.info(`VERSION_PRERELEASE_CREATE: Creating new prerelease version | Current: ${currentVersion} | New: ${newVersion} | Action: Add prerelease tag`);
|
|
113
|
-
return {
|
|
114
|
-
version: newVersion,
|
|
115
|
-
targetBranch
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
// Just add/change the prerelease tag without incrementing
|
|
120
|
-
const baseVersion = convertToReleaseVersion(currentVersion);
|
|
121
|
-
const newVersion = `${baseVersion}-${tag}.0`;
|
|
122
|
-
logger.info(`VERSION_PRERELEASE_TAG: Setting prerelease tag | Current: ${currentVersion} | New: ${newVersion} | Tag: ${versionConfig.tag}`);
|
|
123
|
-
return {
|
|
124
|
-
version: newVersion,
|
|
125
|
-
targetBranch
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
throw new Error(`Invalid version type: ${versionConfig.type}`);
|
|
130
|
-
};
|
|
131
|
-
/**
|
|
132
|
-
* Find the development branch from branches configuration
|
|
133
|
-
* Returns the branch marked with developmentBranch: true
|
|
134
|
-
*/ const findDevelopmentBranch = (branchesConfig)=>{
|
|
135
|
-
if (!branchesConfig || typeof branchesConfig !== 'object') {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
for (const [branchName, branchConfig] of Object.entries(branchesConfig)){
|
|
139
|
-
if (branchConfig && typeof branchConfig === 'object' && branchConfig.developmentBranch === true) {
|
|
140
|
-
return branchName;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return null;
|
|
144
|
-
};
|
|
145
|
-
const checkIfTagExists = async (tagName)=>{
|
|
146
|
-
const { runSecure, validateGitRef } = await import('@eldrforge/git-tools');
|
|
147
|
-
try {
|
|
148
|
-
// Validate tag name to prevent injection
|
|
149
|
-
if (!validateGitRef(tagName)) {
|
|
150
|
-
throw new Error(`Invalid tag name: ${tagName}`);
|
|
151
|
-
}
|
|
152
|
-
const { stdout } = await runSecure('git', [
|
|
153
|
-
'tag',
|
|
154
|
-
'-l',
|
|
155
|
-
tagName
|
|
156
|
-
]);
|
|
157
|
-
return stdout.trim() === tagName;
|
|
158
|
-
} catch {
|
|
159
|
-
// If git command fails, assume tag doesn't exist
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
const confirmVersionInteractively = async (currentVersion, proposedVersion, targetVersionInput)=>{
|
|
164
|
-
const { getUserChoice, getUserTextInput, requireTTY } = await import('./interactive.js');
|
|
165
|
-
const { getLogger } = await import('../logging.js');
|
|
166
|
-
requireTTY('Interactive version confirmation requires a terminal.');
|
|
167
|
-
const logger = getLogger();
|
|
168
|
-
logger.info(`\nVERSION_CONFIRMATION: Version confirmation required | Current: ${currentVersion} | Proposed: ${proposedVersion}`);
|
|
169
|
-
logger.info(`VERSION_CURRENT: Current package version | Version: ${currentVersion}`);
|
|
170
|
-
logger.info(`VERSION_PROPOSED: Proposed new version | Version: ${proposedVersion}`);
|
|
171
|
-
if (targetVersionInput) {
|
|
172
|
-
logger.info(`VERSION_TARGET_INPUT: Target version provided | Input: ${targetVersionInput}`);
|
|
173
|
-
}
|
|
174
|
-
const choices = [
|
|
175
|
-
{
|
|
176
|
-
key: 'c',
|
|
177
|
-
label: `Confirm ${proposedVersion}`
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
key: 'e',
|
|
181
|
-
label: 'Enter custom version'
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
key: 'a',
|
|
185
|
-
label: 'Abort publish'
|
|
186
|
-
}
|
|
187
|
-
];
|
|
188
|
-
const choice = await getUserChoice('\n🤔 Confirm the version for this release:', choices);
|
|
189
|
-
switch(choice){
|
|
190
|
-
case 'c':
|
|
191
|
-
return proposedVersion;
|
|
192
|
-
case 'e':
|
|
193
|
-
{
|
|
194
|
-
const customVersion = await getUserTextInput('\n📝 Enter the version number (e.g., "4.30.0"):');
|
|
195
|
-
if (!validateVersionString(customVersion)) {
|
|
196
|
-
throw new Error(`Invalid version format: ${customVersion}. Expected format: "x.y.z"`);
|
|
197
|
-
}
|
|
198
|
-
const cleanCustomVersion = customVersion.startsWith('v') ? customVersion.slice(1) : customVersion;
|
|
199
|
-
logger.info(`VERSION_CUSTOM_SELECTED: Using custom version from user input | Version: ${cleanCustomVersion} | Source: interactive input`);
|
|
200
|
-
return cleanCustomVersion;
|
|
201
|
-
}
|
|
202
|
-
case 'a':
|
|
203
|
-
throw new Error('Release aborted by user');
|
|
204
|
-
default:
|
|
205
|
-
throw new Error(`Unexpected choice: ${choice}`);
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
const getOutputPath = (outputDirectory, filename)=>{
|
|
209
|
-
return path__default.join(outputDirectory, filename);
|
|
210
|
-
};
|
|
211
|
-
const getTimestampedFilename = (baseName, extension = '.json')=>{
|
|
212
|
-
const now = new Date();
|
|
213
|
-
// Format as YYMMdd-HHmm (e.g., 250701-1030)
|
|
214
|
-
const yy = now.getFullYear().toString().slice(-2);
|
|
215
|
-
const mm = (now.getMonth() + 1).toString().padStart(2, '0');
|
|
216
|
-
const dd = now.getDate().toString().padStart(2, '0');
|
|
217
|
-
const hh = now.getHours().toString().padStart(2, '0');
|
|
218
|
-
const min = now.getMinutes().toString().padStart(2, '0');
|
|
219
|
-
const timestamp = `${yy}${mm}${dd}-${hh}${min}`;
|
|
220
|
-
return `${timestamp}-${baseName}${extension}`;
|
|
221
|
-
};
|
|
222
|
-
const getTimestampedRequestFilename = (baseName)=>{
|
|
223
|
-
return getTimestampedFilename(baseName, '.request.json');
|
|
224
|
-
};
|
|
225
|
-
const getTimestampedResponseFilename = (baseName)=>{
|
|
226
|
-
return getTimestampedFilename(baseName, '.response.json');
|
|
227
|
-
};
|
|
228
|
-
const getTimestampedCommitFilename = ()=>{
|
|
229
|
-
return getTimestampedFilename('commit-message', '.md');
|
|
230
|
-
};
|
|
231
|
-
const getTimestampedReleaseNotesFilename = ()=>{
|
|
232
|
-
return getTimestampedFilename('release-notes', '.md');
|
|
233
|
-
};
|
|
234
|
-
const getTimestampedAudioFilename = ()=>{
|
|
235
|
-
return getTimestampedFilename('audio-recording', '.wav');
|
|
236
|
-
};
|
|
237
|
-
const getTimestampedReviewFilename = ()=>{
|
|
238
|
-
return getTimestampedFilename('review-analysis', '.md');
|
|
239
|
-
};
|
|
240
|
-
const getTimestampedReviewNotesFilename = ()=>{
|
|
241
|
-
return getTimestampedFilename('review-notes', '.md');
|
|
242
|
-
};
|
|
243
|
-
// archiveAudio function moved to @eldrforge/audio-tools
|
|
244
|
-
/**
|
|
245
|
-
* Query npm registry for published version of a package
|
|
246
|
-
* Returns null if package is not published or on error
|
|
247
|
-
*/ const getNpmPublishedVersion = async (packageName)=>{
|
|
248
|
-
const logger = getLogger();
|
|
249
|
-
try {
|
|
250
|
-
const { runSecure } = await import('@eldrforge/git-tools');
|
|
251
|
-
// Use npm view to get the latest published version
|
|
252
|
-
// --json flag ensures parseable output
|
|
253
|
-
const { stdout } = await runSecure('npm', [
|
|
254
|
-
'view',
|
|
255
|
-
packageName,
|
|
256
|
-
'version',
|
|
257
|
-
'--json'
|
|
258
|
-
]);
|
|
259
|
-
if (!stdout || stdout.trim() === '') {
|
|
260
|
-
logger.verbose(`Package ${packageName} not found on npm registry`);
|
|
261
|
-
return null;
|
|
262
|
-
}
|
|
263
|
-
// npm view returns just the version string for a single version
|
|
264
|
-
const version = stdout.trim().replace(/^["']|["']$/g, ''); // Remove quotes if present
|
|
265
|
-
logger.verbose(`Found ${packageName}@${version} on npm registry`);
|
|
266
|
-
return version;
|
|
267
|
-
} catch (error) {
|
|
268
|
-
// Package not found or network error
|
|
269
|
-
logger.verbose(`Could not query npm for ${packageName}: ${error.message}`);
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
/**
|
|
274
|
-
* Get detailed info about a tag including the version it points to
|
|
275
|
-
*/ const getTagInfo = async (tagName)=>{
|
|
276
|
-
try {
|
|
277
|
-
const { runSecure, validateGitRef } = await import('@eldrforge/git-tools');
|
|
278
|
-
if (!validateGitRef(tagName)) {
|
|
279
|
-
throw new Error(`Invalid tag name: ${tagName}`);
|
|
280
|
-
}
|
|
281
|
-
// Check if tag exists
|
|
282
|
-
const { stdout: tagList } = await runSecure('git', [
|
|
283
|
-
'tag',
|
|
284
|
-
'-l',
|
|
285
|
-
tagName
|
|
286
|
-
]);
|
|
287
|
-
if (tagList.trim() !== tagName) {
|
|
288
|
-
return {
|
|
289
|
-
exists: false
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
// Get the commit the tag points to
|
|
293
|
-
const { stdout: commit } = await runSecure('git', [
|
|
294
|
-
'rev-list',
|
|
295
|
-
'-n',
|
|
296
|
-
'1',
|
|
297
|
-
tagName
|
|
298
|
-
]);
|
|
299
|
-
// Extract version from tag name (assumes format like v1.2.3 or working/v1.2.3)
|
|
300
|
-
const versionMatch = tagName.match(/v?(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?)/);
|
|
301
|
-
const version = versionMatch ? versionMatch[1] : undefined;
|
|
302
|
-
return {
|
|
303
|
-
exists: true,
|
|
304
|
-
commit: commit.trim(),
|
|
305
|
-
version
|
|
306
|
-
};
|
|
307
|
-
} catch {
|
|
308
|
-
return null;
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
/**
|
|
312
|
-
* Check if a version is a development/prerelease version (has prerelease tag)
|
|
313
|
-
*/ const isDevelopmentVersion = (version)=>{
|
|
314
|
-
// Development versions have prerelease tags: 1.2.3-dev.0, 1.2.3-alpha.1, etc.
|
|
315
|
-
return version.includes('-');
|
|
316
|
-
};
|
|
317
|
-
/**
|
|
318
|
-
* Get expected version pattern for a branch
|
|
319
|
-
*/ const getExpectedVersionPattern = (branchName)=>{
|
|
320
|
-
// Development/working branches should have prerelease versions
|
|
321
|
-
const devBranchPatterns = /^(working|development|dev|feature\/|wip\/)/i;
|
|
322
|
-
if (devBranchPatterns.test(branchName)) {
|
|
323
|
-
return {
|
|
324
|
-
pattern: /^\d+\.\d+\.\d+-[a-zA-Z0-9.-]+$/,
|
|
325
|
-
description: 'X.Y.Z-<tag> (e.g., 1.2.3-dev.0)',
|
|
326
|
-
isDevelopment: true
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
// Main/master/production branches should have release versions
|
|
330
|
-
const releaseBranchPatterns = /^(main|master|production|release\/)/i;
|
|
331
|
-
if (releaseBranchPatterns.test(branchName)) {
|
|
332
|
-
return {
|
|
333
|
-
pattern: /^\d+\.\d+\.\d+$/,
|
|
334
|
-
description: 'X.Y.Z (e.g., 1.2.3)',
|
|
335
|
-
isDevelopment: false
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
// For other branches, allow both but prefer release versions
|
|
339
|
-
return {
|
|
340
|
-
pattern: /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/,
|
|
341
|
-
description: 'X.Y.Z or X.Y.Z-<tag>',
|
|
342
|
-
isDevelopment: false
|
|
343
|
-
};
|
|
344
|
-
};
|
|
345
|
-
/**
|
|
346
|
-
* Validate version against branch expectations
|
|
347
|
-
*/ const validateVersionForBranch = (version, branchName)=>{
|
|
348
|
-
const expected = getExpectedVersionPattern(branchName);
|
|
349
|
-
if (!expected.pattern.test(version)) {
|
|
350
|
-
return {
|
|
351
|
-
valid: false,
|
|
352
|
-
issue: `Invalid version format for branch '${branchName}'`,
|
|
353
|
-
fix: `Version should match ${expected.description}`
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
const isDevVersion = isDevelopmentVersion(version);
|
|
357
|
-
// Development branches should have development versions
|
|
358
|
-
if (expected.isDevelopment && !isDevVersion) {
|
|
359
|
-
return {
|
|
360
|
-
valid: false,
|
|
361
|
-
issue: `Release version on development branch '${branchName}'`,
|
|
362
|
-
fix: 'Run kodrdriv development to update to development version'
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
// Release branches should NOT have development versions
|
|
366
|
-
if (!expected.isDevelopment && branchName.match(/^(main|master|production|release\/)/) && isDevVersion) {
|
|
367
|
-
return {
|
|
368
|
-
valid: false,
|
|
369
|
-
issue: `Development version on release branch '${branchName}'`,
|
|
370
|
-
fix: 'Do not commit development versions to release branches'
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
return {
|
|
374
|
-
valid: true
|
|
375
|
-
};
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
export { calculateBranchDependentVersion, checkIfTagExists, confirmVersionInteractively, findDevelopmentBranch, getExpectedVersionPattern, getNpmPublishedVersion, getOutputPath, getTagInfo, getTimestampedAudioFilename, getTimestampedCommitFilename, getTimestampedFilename, getTimestampedReleaseNotesFilename, getTimestampedRequestFilename, getTimestampedResponseFilename, getTimestampedReviewFilename, getTimestampedReviewNotesFilename, getVersionFromBranch, isDevelopmentVersion, validateVersionForBranch };
|
|
379
|
-
//# sourceMappingURL=general.js.map
|