@lumpcode/cli-utils 0.1.1 → 0.3.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/dist/index.cjs +79 -22
- package/dist/index.d.ts +43 -1
- package/dist/index.js +79 -22
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -101,6 +101,8 @@ function formatJsonFileContent(input) {
|
|
|
101
101
|
return trailingNewline ? `${json}\n` : json;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
const WORKSPACE_LOCK_WAIT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
105
|
+
const WORKSPACE_LOCK_WAIT_LOG_INTERVAL_MS = 30000;
|
|
104
106
|
const WAIT_POLL_MS = 500;
|
|
105
107
|
function workspaceLocksDirPath(input) {
|
|
106
108
|
return path__namespace.join(input.globalConfigFolderPath, input.spec.locksSubdirName);
|
|
@@ -113,26 +115,53 @@ function workspaceLockFilePath(input) {
|
|
|
113
115
|
spec: input.spec,
|
|
114
116
|
}), `${hash}.lock.json`);
|
|
115
117
|
}
|
|
116
|
-
function
|
|
117
|
-
const { spec, workspacePath, holder } = input;
|
|
118
|
+
function formatHolderClause(holder, style) {
|
|
118
119
|
if (holder?.lumpName && holder.pid) {
|
|
119
|
-
return
|
|
120
|
-
`(pid ${holder.pid}, lump "${holder.lumpName}")
|
|
120
|
+
return style === 'busy'
|
|
121
|
+
? ` (pid ${holder.pid}, lump "${holder.lumpName}")`
|
|
122
|
+
: ` (held by lump "${holder.lumpName}" pid ${holder.pid})`;
|
|
121
123
|
}
|
|
122
124
|
if (holder?.pid) {
|
|
123
|
-
return (
|
|
124
|
-
|
|
125
|
+
return style === 'busy' ? ` (pid ${holder.pid})` : ` (held by pid ${holder.pid})`;
|
|
126
|
+
}
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
129
|
+
function formatBusyMessage(input) {
|
|
130
|
+
const { spec, workspacePath, holder } = input;
|
|
131
|
+
const clause = formatHolderClause(holder, 'busy');
|
|
132
|
+
if (clause) {
|
|
133
|
+
return (`${spec.workspaceLabel} "${workspacePath}" is in use by another lumpcode run` +
|
|
134
|
+
`${clause}. Wait for it to finish or stop the daemon before running again.`);
|
|
125
135
|
}
|
|
126
136
|
return (`${spec.workspaceLabel} "${workspacePath}" is in use by another lumpcode run. ` +
|
|
127
137
|
`Wait for it to finish or stop the daemon before running again.`);
|
|
128
138
|
}
|
|
129
139
|
function formatWorkspaceFileWaitMessage(input) {
|
|
130
140
|
const { spec, workspacePath, holder } = input;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
141
|
+
return (`${spec.waitLogNoun} busy at "${workspacePath}"` +
|
|
142
|
+
`${formatHolderClause(holder, 'held')}; waiting…`);
|
|
143
|
+
}
|
|
144
|
+
function formatWorkspaceFileStillWaitingMessage(input) {
|
|
145
|
+
const { spec, workspacePath, holder, elapsedMs } = input;
|
|
146
|
+
const elapsedSec = Math.max(1, Math.round(elapsedMs / 1000));
|
|
147
|
+
return (`${spec.waitLogNoun} busy at "${workspacePath}"` +
|
|
148
|
+
`${formatHolderClause(holder, 'held')}; still waiting (${elapsedSec}s)…`);
|
|
149
|
+
}
|
|
150
|
+
function formatWaitTimeoutMessage(input) {
|
|
151
|
+
const { spec, workspacePath, holder, waitTimeoutMs } = input;
|
|
152
|
+
return (`Waited ${waitTimeoutMs}ms for ${spec.waitLogNoun} "${workspacePath}"` +
|
|
153
|
+
`${formatHolderClause(holder, 'held')}; giving up.`);
|
|
154
|
+
}
|
|
155
|
+
function toBusyError(input) {
|
|
156
|
+
const { spec, workspacePath, holder, message, reason } = input;
|
|
157
|
+
return {
|
|
158
|
+
code: spec.busyCode,
|
|
159
|
+
message,
|
|
160
|
+
[spec.workspacePathField]: workspacePath,
|
|
161
|
+
...(holder?.pid !== undefined ? { holderPid: holder.pid } : {}),
|
|
162
|
+
...(holder?.lumpName !== undefined ? { holderLumpName: holder.lumpName } : {}),
|
|
163
|
+
...(reason !== undefined ? { reason } : {}),
|
|
164
|
+
};
|
|
136
165
|
}
|
|
137
166
|
async function readLockHolder(lockFilePath) {
|
|
138
167
|
const result = await readJsonFile({ filePath: lockFilePath, ifMissing: 'undefined' });
|
|
@@ -175,6 +204,8 @@ async function tryAcquireWorkspaceFileLockOnce(input) {
|
|
|
175
204
|
}
|
|
176
205
|
async function acquireWorkspaceFileLock(input) {
|
|
177
206
|
const { spec, globalConfigFolderPath, workspacePath, lumpName, mode, projectName, logger } = input;
|
|
207
|
+
const waitTimeoutMs = input.waitTimeoutMs ?? WORKSPACE_LOCK_WAIT_TIMEOUT_MS;
|
|
208
|
+
const waitLogIntervalMs = input.waitLogIntervalMs ?? WORKSPACE_LOCK_WAIT_LOG_INTERVAL_MS;
|
|
178
209
|
const normalizedWorkspacePath = path__namespace.resolve(workspacePath);
|
|
179
210
|
const locksDir = workspaceLocksDirPath({ globalConfigFolderPath, spec });
|
|
180
211
|
await fs__namespace.mkdir(locksDir, { recursive: true });
|
|
@@ -191,6 +222,8 @@ async function acquireWorkspaceFileLock(input) {
|
|
|
191
222
|
...(projectName !== undefined ? { projectName } : {}),
|
|
192
223
|
};
|
|
193
224
|
let loggedWait = false;
|
|
225
|
+
let lastWaitLogAt = 0;
|
|
226
|
+
const waitStartedAt = Date.now();
|
|
194
227
|
for (;;) {
|
|
195
228
|
const attempt = await tryAcquireWorkspaceFileLockOnce({ lockFilePath, payload, spec, logger });
|
|
196
229
|
if (attempt.status === 'acquired') {
|
|
@@ -226,29 +259,51 @@ async function acquireWorkspaceFileLock(input) {
|
|
|
226
259
|
continue;
|
|
227
260
|
}
|
|
228
261
|
if (mode === 'fail') {
|
|
229
|
-
return core.failure({
|
|
230
|
-
|
|
262
|
+
return core.failure(toBusyError({
|
|
263
|
+
spec,
|
|
264
|
+
workspacePath: normalizedWorkspacePath,
|
|
265
|
+
holder: attempt.holder,
|
|
231
266
|
message: formatBusyMessage({
|
|
232
267
|
spec,
|
|
233
268
|
workspacePath: normalizedWorkspacePath,
|
|
234
269
|
holder: attempt.holder,
|
|
235
270
|
}),
|
|
236
|
-
|
|
237
|
-
...(attempt.holder?.pid !== undefined ? { holderPid: attempt.holder.pid } : {}),
|
|
238
|
-
...(attempt.holder?.lumpName !== undefined
|
|
239
|
-
? { holderLumpName: attempt.holder.lumpName }
|
|
240
|
-
: {}),
|
|
241
|
-
});
|
|
271
|
+
}));
|
|
242
272
|
}
|
|
243
|
-
|
|
244
|
-
|
|
273
|
+
const now = Date.now();
|
|
274
|
+
const elapsedMs = now - waitStartedAt;
|
|
275
|
+
if (elapsedMs >= waitTimeoutMs) {
|
|
276
|
+
return core.failure(toBusyError({
|
|
245
277
|
spec,
|
|
246
278
|
workspacePath: normalizedWorkspacePath,
|
|
247
279
|
holder: attempt.holder,
|
|
280
|
+
reason: 'waitTimedOut',
|
|
281
|
+
message: formatWaitTimeoutMessage({
|
|
282
|
+
spec,
|
|
283
|
+
workspacePath: normalizedWorkspacePath,
|
|
284
|
+
holder: attempt.holder,
|
|
285
|
+
waitTimeoutMs,
|
|
286
|
+
}),
|
|
248
287
|
}));
|
|
288
|
+
}
|
|
289
|
+
if (!loggedWait || now - lastWaitLogAt >= waitLogIntervalMs) {
|
|
290
|
+
logger?.info(loggedWait
|
|
291
|
+
? formatWorkspaceFileStillWaitingMessage({
|
|
292
|
+
spec,
|
|
293
|
+
workspacePath: normalizedWorkspacePath,
|
|
294
|
+
holder: attempt.holder,
|
|
295
|
+
elapsedMs,
|
|
296
|
+
})
|
|
297
|
+
: formatWorkspaceFileWaitMessage({
|
|
298
|
+
spec,
|
|
299
|
+
workspacePath: normalizedWorkspacePath,
|
|
300
|
+
holder: attempt.holder,
|
|
301
|
+
}));
|
|
249
302
|
loggedWait = true;
|
|
303
|
+
lastWaitLogAt = now;
|
|
250
304
|
}
|
|
251
|
-
|
|
305
|
+
const remainingMs = waitTimeoutMs - elapsedMs;
|
|
306
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(WAIT_POLL_MS, Math.max(0, remainingMs))));
|
|
252
307
|
}
|
|
253
308
|
}
|
|
254
309
|
|
|
@@ -292,6 +347,8 @@ async function acquireGitCommonDirLock(input) {
|
|
|
292
347
|
mode: input.lockMode,
|
|
293
348
|
projectName: input.projectName,
|
|
294
349
|
logger: input.logger,
|
|
350
|
+
waitTimeoutMs: input.waitTimeoutMs,
|
|
351
|
+
waitLogIntervalMs: input.waitLogIntervalMs,
|
|
295
352
|
});
|
|
296
353
|
}
|
|
297
354
|
/** Acquire, run `fn`, always release. */
|
package/dist/index.d.ts
CHANGED
|
@@ -61,10 +61,38 @@ type LumpJsConfigStep<V extends LumpVariables = LumpVariables, SV extends StepVa
|
|
|
61
61
|
command?: CommandTag | FilePath | Step<V, SV>['commandFn'];
|
|
62
62
|
}>;
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* How Lumpcode prepares the per-lump git workspace inside the preflight repo root.
|
|
66
|
+
*
|
|
67
|
+
* - `checkout`: switch the main worktree to a fresh lump branch (default).
|
|
68
|
+
* - `worktree`: add a linked worktree under `.lumpcode/worktrees/<branch>/`.
|
|
69
|
+
*/
|
|
70
|
+
type WorkspaceStrategy = 'checkout' | 'worktree';
|
|
71
|
+
|
|
72
|
+
type PostSetupWorkspaceFnInput<V extends LumpVariables = LumpVariables> = {
|
|
73
|
+
baseBranch: string;
|
|
74
|
+
branchName: string;
|
|
75
|
+
contextList: ContextList;
|
|
76
|
+
/** Branch workspace. Already prepared on a real run. Read-only. */
|
|
77
|
+
workspacePath: string;
|
|
78
|
+
executionWorkspacePath: string;
|
|
79
|
+
workspaceStrategy: WorkspaceStrategy;
|
|
80
|
+
projectRoot: string;
|
|
81
|
+
lumpVariables: V;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
type PostSetupWorkspaceFn<V extends LumpVariables = LumpVariables> = (input: PostSetupWorkspaceFnInput<V>) => MaybePromise<{
|
|
85
|
+
command?: string;
|
|
86
|
+
} | void>;
|
|
87
|
+
|
|
88
|
+
type PostTeardownWorkspaceFn<V extends LumpVariables = LumpVariables> = (input: PostSetupWorkspaceFnInput<V>) => MaybePromise<{
|
|
89
|
+
command?: string;
|
|
90
|
+
} | void>;
|
|
91
|
+
|
|
64
92
|
type LumpJsConfigSoloStep<V extends LumpVariables = LumpVariables, SV extends StepVariables = StepVariables> = LumpJsConfigStep<V, SV> | LumpJsConfigStep<V, SV>['promptTemplate'] | LumpJsConfigStep<V, SV>['promptFn'];
|
|
65
93
|
type LumpJsConfig<V extends LumpVariables = LumpVariables, SV extends StepVariables = StepVariables> = MergeObjs<Omit<{
|
|
66
94
|
[K in keyof RunLumpInput<V, SV>]?: NonNullable<RunLumpInput<V, SV>[K]> extends Function ? (RunLumpInput<V, SV>[K] | FilePath) : RunLumpInput<V, SV>[K];
|
|
67
|
-
}, 'gitCommitMessageFn' | 'projectRoot' | 'branchFn' | 'baseBranch' | 'setupWorkspaceFn' | 'teardownWorkspaceFn' | '
|
|
95
|
+
}, 'gitCommitMessageFn' | 'projectRoot' | 'branchFn' | 'baseBranch' | 'setupWorkspaceFn' | 'teardownWorkspaceFn' | 'gitAddCommitFn' | 'gitPushFn' | 'getContextListFn' | 'refreshRemoteTrackingRefsFn'>, {
|
|
68
96
|
/**
|
|
69
97
|
* Execution integration branch. Exact string, `BaseBranchFn`, or FilePath to a module.
|
|
70
98
|
* Omit → concrete effective discovery branch. Pattern strings are rejected at resolve.
|
|
@@ -89,6 +117,20 @@ type LumpJsConfig<V extends LumpVariables = LumpVariables, SV extends StepVariab
|
|
|
89
117
|
registerCommands?: string[];
|
|
90
118
|
keepHistory?: boolean;
|
|
91
119
|
verbose?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* After generated checkout/worktree setup. Mutually exclusive with
|
|
122
|
+
* `postSetupWorkspaceCommand`.
|
|
123
|
+
*/
|
|
124
|
+
postSetupWorkspaceFn?: FilePath | PostSetupWorkspaceFn<V>;
|
|
125
|
+
/** Shell fragment in the branch workspace. Mutually exclusive with `postSetupWorkspaceFn`. */
|
|
126
|
+
postSetupWorkspaceCommand?: string;
|
|
127
|
+
/**
|
|
128
|
+
* Before generated workspace teardown. Mutually exclusive with
|
|
129
|
+
* `postTeardownWorkspaceCommand`.
|
|
130
|
+
*/
|
|
131
|
+
postTeardownWorkspaceFn?: FilePath | PostTeardownWorkspaceFn<V>;
|
|
132
|
+
/** Shell fragment in the branch workspace. Mutually exclusive with `postTeardownWorkspaceFn`. */
|
|
133
|
+
postTeardownWorkspaceCommand?: string;
|
|
92
134
|
}>;
|
|
93
135
|
|
|
94
136
|
declare function getContextStatuses(input: {
|
package/dist/index.js
CHANGED
|
@@ -78,6 +78,8 @@ function formatJsonFileContent(input) {
|
|
|
78
78
|
return trailingNewline ? `${json}\n` : json;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
const WORKSPACE_LOCK_WAIT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
82
|
+
const WORKSPACE_LOCK_WAIT_LOG_INTERVAL_MS = 30000;
|
|
81
83
|
const WAIT_POLL_MS = 500;
|
|
82
84
|
function workspaceLocksDirPath(input) {
|
|
83
85
|
return path.join(input.globalConfigFolderPath, input.spec.locksSubdirName);
|
|
@@ -90,26 +92,53 @@ function workspaceLockFilePath(input) {
|
|
|
90
92
|
spec: input.spec,
|
|
91
93
|
}), `${hash}.lock.json`);
|
|
92
94
|
}
|
|
93
|
-
function
|
|
94
|
-
const { spec, workspacePath, holder } = input;
|
|
95
|
+
function formatHolderClause(holder, style) {
|
|
95
96
|
if (holder?.lumpName && holder.pid) {
|
|
96
|
-
return
|
|
97
|
-
`(pid ${holder.pid}, lump "${holder.lumpName}")
|
|
97
|
+
return style === 'busy'
|
|
98
|
+
? ` (pid ${holder.pid}, lump "${holder.lumpName}")`
|
|
99
|
+
: ` (held by lump "${holder.lumpName}" pid ${holder.pid})`;
|
|
98
100
|
}
|
|
99
101
|
if (holder?.pid) {
|
|
100
|
-
return (
|
|
101
|
-
|
|
102
|
+
return style === 'busy' ? ` (pid ${holder.pid})` : ` (held by pid ${holder.pid})`;
|
|
103
|
+
}
|
|
104
|
+
return '';
|
|
105
|
+
}
|
|
106
|
+
function formatBusyMessage(input) {
|
|
107
|
+
const { spec, workspacePath, holder } = input;
|
|
108
|
+
const clause = formatHolderClause(holder, 'busy');
|
|
109
|
+
if (clause) {
|
|
110
|
+
return (`${spec.workspaceLabel} "${workspacePath}" is in use by another lumpcode run` +
|
|
111
|
+
`${clause}. Wait for it to finish or stop the daemon before running again.`);
|
|
102
112
|
}
|
|
103
113
|
return (`${spec.workspaceLabel} "${workspacePath}" is in use by another lumpcode run. ` +
|
|
104
114
|
`Wait for it to finish or stop the daemon before running again.`);
|
|
105
115
|
}
|
|
106
116
|
function formatWorkspaceFileWaitMessage(input) {
|
|
107
117
|
const { spec, workspacePath, holder } = input;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
118
|
+
return (`${spec.waitLogNoun} busy at "${workspacePath}"` +
|
|
119
|
+
`${formatHolderClause(holder, 'held')}; waiting…`);
|
|
120
|
+
}
|
|
121
|
+
function formatWorkspaceFileStillWaitingMessage(input) {
|
|
122
|
+
const { spec, workspacePath, holder, elapsedMs } = input;
|
|
123
|
+
const elapsedSec = Math.max(1, Math.round(elapsedMs / 1000));
|
|
124
|
+
return (`${spec.waitLogNoun} busy at "${workspacePath}"` +
|
|
125
|
+
`${formatHolderClause(holder, 'held')}; still waiting (${elapsedSec}s)…`);
|
|
126
|
+
}
|
|
127
|
+
function formatWaitTimeoutMessage(input) {
|
|
128
|
+
const { spec, workspacePath, holder, waitTimeoutMs } = input;
|
|
129
|
+
return (`Waited ${waitTimeoutMs}ms for ${spec.waitLogNoun} "${workspacePath}"` +
|
|
130
|
+
`${formatHolderClause(holder, 'held')}; giving up.`);
|
|
131
|
+
}
|
|
132
|
+
function toBusyError(input) {
|
|
133
|
+
const { spec, workspacePath, holder, message, reason } = input;
|
|
134
|
+
return {
|
|
135
|
+
code: spec.busyCode,
|
|
136
|
+
message,
|
|
137
|
+
[spec.workspacePathField]: workspacePath,
|
|
138
|
+
...(holder?.pid !== undefined ? { holderPid: holder.pid } : {}),
|
|
139
|
+
...(holder?.lumpName !== undefined ? { holderLumpName: holder.lumpName } : {}),
|
|
140
|
+
...(reason !== undefined ? { reason } : {}),
|
|
141
|
+
};
|
|
113
142
|
}
|
|
114
143
|
async function readLockHolder(lockFilePath) {
|
|
115
144
|
const result = await readJsonFile({ filePath: lockFilePath, ifMissing: 'undefined' });
|
|
@@ -152,6 +181,8 @@ async function tryAcquireWorkspaceFileLockOnce(input) {
|
|
|
152
181
|
}
|
|
153
182
|
async function acquireWorkspaceFileLock(input) {
|
|
154
183
|
const { spec, globalConfigFolderPath, workspacePath, lumpName, mode, projectName, logger } = input;
|
|
184
|
+
const waitTimeoutMs = input.waitTimeoutMs ?? WORKSPACE_LOCK_WAIT_TIMEOUT_MS;
|
|
185
|
+
const waitLogIntervalMs = input.waitLogIntervalMs ?? WORKSPACE_LOCK_WAIT_LOG_INTERVAL_MS;
|
|
155
186
|
const normalizedWorkspacePath = path.resolve(workspacePath);
|
|
156
187
|
const locksDir = workspaceLocksDirPath({ globalConfigFolderPath, spec });
|
|
157
188
|
await fs.mkdir(locksDir, { recursive: true });
|
|
@@ -168,6 +199,8 @@ async function acquireWorkspaceFileLock(input) {
|
|
|
168
199
|
...(projectName !== undefined ? { projectName } : {}),
|
|
169
200
|
};
|
|
170
201
|
let loggedWait = false;
|
|
202
|
+
let lastWaitLogAt = 0;
|
|
203
|
+
const waitStartedAt = Date.now();
|
|
171
204
|
for (;;) {
|
|
172
205
|
const attempt = await tryAcquireWorkspaceFileLockOnce({ lockFilePath, payload, spec, logger });
|
|
173
206
|
if (attempt.status === 'acquired') {
|
|
@@ -203,29 +236,51 @@ async function acquireWorkspaceFileLock(input) {
|
|
|
203
236
|
continue;
|
|
204
237
|
}
|
|
205
238
|
if (mode === 'fail') {
|
|
206
|
-
return failure({
|
|
207
|
-
|
|
239
|
+
return failure(toBusyError({
|
|
240
|
+
spec,
|
|
241
|
+
workspacePath: normalizedWorkspacePath,
|
|
242
|
+
holder: attempt.holder,
|
|
208
243
|
message: formatBusyMessage({
|
|
209
244
|
spec,
|
|
210
245
|
workspacePath: normalizedWorkspacePath,
|
|
211
246
|
holder: attempt.holder,
|
|
212
247
|
}),
|
|
213
|
-
|
|
214
|
-
...(attempt.holder?.pid !== undefined ? { holderPid: attempt.holder.pid } : {}),
|
|
215
|
-
...(attempt.holder?.lumpName !== undefined
|
|
216
|
-
? { holderLumpName: attempt.holder.lumpName }
|
|
217
|
-
: {}),
|
|
218
|
-
});
|
|
248
|
+
}));
|
|
219
249
|
}
|
|
220
|
-
|
|
221
|
-
|
|
250
|
+
const now = Date.now();
|
|
251
|
+
const elapsedMs = now - waitStartedAt;
|
|
252
|
+
if (elapsedMs >= waitTimeoutMs) {
|
|
253
|
+
return failure(toBusyError({
|
|
222
254
|
spec,
|
|
223
255
|
workspacePath: normalizedWorkspacePath,
|
|
224
256
|
holder: attempt.holder,
|
|
257
|
+
reason: 'waitTimedOut',
|
|
258
|
+
message: formatWaitTimeoutMessage({
|
|
259
|
+
spec,
|
|
260
|
+
workspacePath: normalizedWorkspacePath,
|
|
261
|
+
holder: attempt.holder,
|
|
262
|
+
waitTimeoutMs,
|
|
263
|
+
}),
|
|
225
264
|
}));
|
|
265
|
+
}
|
|
266
|
+
if (!loggedWait || now - lastWaitLogAt >= waitLogIntervalMs) {
|
|
267
|
+
logger?.info(loggedWait
|
|
268
|
+
? formatWorkspaceFileStillWaitingMessage({
|
|
269
|
+
spec,
|
|
270
|
+
workspacePath: normalizedWorkspacePath,
|
|
271
|
+
holder: attempt.holder,
|
|
272
|
+
elapsedMs,
|
|
273
|
+
})
|
|
274
|
+
: formatWorkspaceFileWaitMessage({
|
|
275
|
+
spec,
|
|
276
|
+
workspacePath: normalizedWorkspacePath,
|
|
277
|
+
holder: attempt.holder,
|
|
278
|
+
}));
|
|
226
279
|
loggedWait = true;
|
|
280
|
+
lastWaitLogAt = now;
|
|
227
281
|
}
|
|
228
|
-
|
|
282
|
+
const remainingMs = waitTimeoutMs - elapsedMs;
|
|
283
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(WAIT_POLL_MS, Math.max(0, remainingMs))));
|
|
229
284
|
}
|
|
230
285
|
}
|
|
231
286
|
|
|
@@ -269,6 +324,8 @@ async function acquireGitCommonDirLock(input) {
|
|
|
269
324
|
mode: input.lockMode,
|
|
270
325
|
projectName: input.projectName,
|
|
271
326
|
logger: input.logger,
|
|
327
|
+
waitTimeoutMs: input.waitTimeoutMs,
|
|
328
|
+
waitLogIntervalMs: input.waitLogIntervalMs,
|
|
272
329
|
});
|
|
273
330
|
}
|
|
274
331
|
/** Acquire, run `fn`, always release. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumpcode/cli-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Types, defineX helpers, and runtime utilities for Lumpcode lump configs and recipes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lumpcode",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"test": "vitest run"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@lumpcode/cli-types": "^0.
|
|
47
|
-
"@lumpcode/core": "^0.
|
|
46
|
+
"@lumpcode/cli-types": "^0.3.0",
|
|
47
|
+
"@lumpcode/core": "^0.3.0",
|
|
48
48
|
"js-yaml": "^5.0.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|