@lumpcode/core 0.1.0 → 0.2.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/README.md +18 -16
- package/dist/helpers/execAsync/main.d.ts +14 -9
- package/dist/helpers/execAsync/main.d.ts.map +1 -1
- package/dist/helpers/executeStepsForContextList/main.d.ts +4 -9
- package/dist/helpers/executeStepsForContextList/main.d.ts.map +1 -1
- package/dist/helpers/getContextStatus/main.d.ts.map +1 -1
- package/dist/index.cjs +303 -177
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +299 -175
- package/dist/index.js.map +1 -1
- package/dist/testing/processTreeTestHelpers.d.ts +3 -0
- package/dist/testing/processTreeTestHelpers.d.ts.map +1 -1
- package/dist/types/ExecuteStepsFailureReason.d.ts +1 -1
- package/dist/types/ExecuteStepsFailureReason.d.ts.map +1 -1
- package/dist/types/GitAddCommitFn.d.ts +16 -0
- package/dist/types/GitAddCommitFn.d.ts.map +1 -0
- package/dist/types/GitPushFn.d.ts +12 -0
- package/dist/types/GitPushFn.d.ts.map +1 -0
- package/dist/types/PostCommandExecFn.d.ts +2 -0
- package/dist/types/PostCommandExecFn.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/usages/runLump/defaultInjectedFns.d.ts +3 -4
- package/dist/usages/runLump/defaultInjectedFns.d.ts.map +1 -1
- package/dist/usages/runLump/index.d.ts +1 -1
- package/dist/usages/runLump/index.d.ts.map +1 -1
- package/dist/usages/runLump/main.d.ts +3 -4
- package/dist/usages/runLump/main.d.ts.map +1 -1
- package/dist/utils/commitMessageIncludesMarker/index.d.ts +2 -0
- package/dist/utils/commitMessageIncludesMarker/index.d.ts.map +1 -0
- package/dist/utils/commitMessageIncludesMarker/main.d.ts +3 -0
- package/dist/utils/commitMessageIncludesMarker/main.d.ts.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/parseGitLogHashBodyRecords/index.d.ts +2 -0
- package/dist/utils/parseGitLogHashBodyRecords/index.d.ts.map +1 -0
- package/dist/utils/parseGitLogHashBodyRecords/main.d.ts +8 -0
- package/dist/utils/parseGitLogHashBodyRecords/main.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3403,6 +3403,40 @@ function parseGitLogHashSubjectLines(stdout) {
|
|
|
3403
3403
|
});
|
|
3404
3404
|
}
|
|
3405
3405
|
|
|
3406
|
+
const GIT_LOG_HASH_BODY_FORMAT = '%x1e%H%x00%B';
|
|
3407
|
+
const RECORD_SEPARATOR = '\x1e';
|
|
3408
|
+
const HASH_MESSAGE_SEPARATOR = '\x00';
|
|
3409
|
+
/** Parses `git log --format='%x1e%H%x00%B'` stdout into commit hash and full-message pairs. */
|
|
3410
|
+
function parseGitLogHashBodyRecords(stdout) {
|
|
3411
|
+
const records = [];
|
|
3412
|
+
for (const record of stdout.split(RECORD_SEPARATOR)) {
|
|
3413
|
+
if (!record)
|
|
3414
|
+
continue;
|
|
3415
|
+
const nul = record.indexOf(HASH_MESSAGE_SEPARATOR);
|
|
3416
|
+
if (nul === -1)
|
|
3417
|
+
continue;
|
|
3418
|
+
const hash = record.slice(0, nul).trim();
|
|
3419
|
+
if (!hash)
|
|
3420
|
+
continue;
|
|
3421
|
+
records.push({
|
|
3422
|
+
hash,
|
|
3423
|
+
message: record.slice(nul + 1),
|
|
3424
|
+
});
|
|
3425
|
+
}
|
|
3426
|
+
return records;
|
|
3427
|
+
}
|
|
3428
|
+
|
|
3429
|
+
const MARKER_CONTINUE = '[A-Za-z0-9_-]';
|
|
3430
|
+
function escapeRegExp(value) {
|
|
3431
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
3432
|
+
}
|
|
3433
|
+
/** True when `marker` occurs in `message` and is not a prefix of a longer `[A-Za-z0-9_-]*` token. */
|
|
3434
|
+
function commitMessageIncludesMarker(message, marker) {
|
|
3435
|
+
if (marker.length === 0)
|
|
3436
|
+
return false;
|
|
3437
|
+
return new RegExp(`${escapeRegExp(marker)}(?!${MARKER_CONTINUE})`).test(message);
|
|
3438
|
+
}
|
|
3439
|
+
|
|
3406
3440
|
/** Node-style `code` from a caught unknown error (e.g. `ENOENT`), or undefined. */
|
|
3407
3441
|
function nodeErrnoCode(error) {
|
|
3408
3442
|
if (error && typeof error === 'object' && 'code' in error) {
|
|
@@ -3550,32 +3584,54 @@ async function killProcessTree(input) {
|
|
|
3550
3584
|
}
|
|
3551
3585
|
|
|
3552
3586
|
const execAsyncBase = promisify(exec);
|
|
3587
|
+
/** Same signal Node's `exec` `timeout` option uses by default. */
|
|
3588
|
+
const EXEC_TIMEOUT_KILL_SIGNAL = 'SIGTERM';
|
|
3589
|
+
function isExecTimeout(error, timeoutMillis) {
|
|
3590
|
+
if (timeoutMillis === undefined) {
|
|
3591
|
+
return false;
|
|
3592
|
+
}
|
|
3593
|
+
if (typeof error !== 'object' || error === null) {
|
|
3594
|
+
return false;
|
|
3595
|
+
}
|
|
3596
|
+
const execError = error;
|
|
3597
|
+
if (execError.killed !== true) {
|
|
3598
|
+
return false;
|
|
3599
|
+
}
|
|
3600
|
+
// Windows has no POSIX signals; Node force-kills and `signal` is often null.
|
|
3601
|
+
if (process.platform === 'win32') {
|
|
3602
|
+
return true;
|
|
3603
|
+
}
|
|
3604
|
+
return execError.signal === EXEC_TIMEOUT_KILL_SIGNAL;
|
|
3605
|
+
}
|
|
3553
3606
|
async function execAsync(command, options) {
|
|
3554
|
-
const
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3607
|
+
const timeoutMillis = options?.timeoutMillis;
|
|
3608
|
+
try {
|
|
3609
|
+
const result = await execAsyncBase(command, {
|
|
3610
|
+
cwd: options?.cwd,
|
|
3611
|
+
...(timeoutMillis !== undefined
|
|
3612
|
+
? { timeout: timeoutMillis, killSignal: EXEC_TIMEOUT_KILL_SIGNAL }
|
|
3613
|
+
: {}),
|
|
3614
|
+
});
|
|
3615
|
+
return success({
|
|
3616
|
+
stdout: String(result.stdout),
|
|
3617
|
+
stderr: String(result.stderr),
|
|
3618
|
+
});
|
|
3619
|
+
}
|
|
3620
|
+
catch (e) {
|
|
3621
|
+
const timedOut = isExecTimeout(e, timeoutMillis);
|
|
3622
|
+
const reason = timedOut ? 'timeout' : 'exit';
|
|
3566
3623
|
return failure({
|
|
3567
|
-
message:
|
|
3624
|
+
message: timedOut
|
|
3625
|
+
? `Command ${command} timed out after ${timeoutMillis}ms`
|
|
3626
|
+
: `Command ${command} failed with error: ${e}`,
|
|
3627
|
+
reason,
|
|
3568
3628
|
info: {
|
|
3569
3629
|
command,
|
|
3570
|
-
stdout,
|
|
3571
|
-
stderr,
|
|
3630
|
+
stdout: e,
|
|
3631
|
+
stderr: e,
|
|
3572
3632
|
},
|
|
3573
3633
|
});
|
|
3574
3634
|
}
|
|
3575
|
-
return success({
|
|
3576
|
-
stdout,
|
|
3577
|
-
stderr,
|
|
3578
|
-
});
|
|
3579
3635
|
}
|
|
3580
3636
|
|
|
3581
3637
|
function execBinary(input) {
|
|
@@ -3745,28 +3801,85 @@ async function collectStepsForContext(params) {
|
|
|
3745
3801
|
return collectedSteps;
|
|
3746
3802
|
}
|
|
3747
3803
|
|
|
3748
|
-
|
|
3749
|
-
|
|
3804
|
+
const ABORT_STEP_WALK_MESSAGE = 'Process aborted';
|
|
3805
|
+
function stepWalkAbortedFailure() {
|
|
3806
|
+
return {
|
|
3807
|
+
success: false,
|
|
3808
|
+
data: {
|
|
3809
|
+
message: ABORT_STEP_WALK_MESSAGE,
|
|
3810
|
+
reason: 'stepWalkFailed',
|
|
3811
|
+
},
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3814
|
+
function createStepWalkAbortError() {
|
|
3815
|
+
const error = new Error(ABORT_STEP_WALK_MESSAGE);
|
|
3816
|
+
error.name = 'AbortError';
|
|
3817
|
+
return error;
|
|
3818
|
+
}
|
|
3819
|
+
function isStepWalkAbortError(error) {
|
|
3820
|
+
return (typeof error === 'object'
|
|
3821
|
+
&& error !== null
|
|
3822
|
+
&& 'name' in error
|
|
3823
|
+
&& error.name === 'AbortError');
|
|
3824
|
+
}
|
|
3825
|
+
/**
|
|
3826
|
+
* Await user-hook work, but stop waiting once `signal` aborts so the walk can
|
|
3827
|
+
* unwind (locks/teardown). Does not cancel sync busy-loops on the event loop.
|
|
3828
|
+
*/
|
|
3829
|
+
async function awaitUnlessAborted(work, signal) {
|
|
3830
|
+
if (!signal) {
|
|
3831
|
+
return await work;
|
|
3832
|
+
}
|
|
3833
|
+
signal.throwIfAborted();
|
|
3834
|
+
return new Promise((resolve, reject) => {
|
|
3835
|
+
const onAbort = () => {
|
|
3836
|
+
cleanup();
|
|
3837
|
+
reject(createStepWalkAbortError());
|
|
3838
|
+
};
|
|
3839
|
+
const cleanup = () => {
|
|
3840
|
+
signal.removeEventListener('abort', onAbort);
|
|
3841
|
+
};
|
|
3842
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
3843
|
+
Promise.resolve(work).then((value) => {
|
|
3844
|
+
cleanup();
|
|
3845
|
+
resolve(value);
|
|
3846
|
+
}, (error) => {
|
|
3847
|
+
cleanup();
|
|
3848
|
+
reject(error);
|
|
3849
|
+
});
|
|
3850
|
+
});
|
|
3851
|
+
}
|
|
3852
|
+
/**
|
|
3853
|
+
* Result-aware runner for `gitAddCommitFn` / `gitPushFn`.
|
|
3854
|
+
* nullish Success → no-op; empty string / Failure / throw / exec fail → failed.
|
|
3855
|
+
*/
|
|
3856
|
+
async function runGitResultHook(input) {
|
|
3857
|
+
const { getResult, cwd, logger, execLabel } = input;
|
|
3750
3858
|
try {
|
|
3751
|
-
const
|
|
3752
|
-
if (command == null || command === '') {
|
|
3753
|
-
return 'ok';
|
|
3754
|
-
}
|
|
3755
|
-
const result = await execAsync(command, { cwd });
|
|
3756
|
-
logger.verbose(`${label} ${JSON.stringify(result)}`);
|
|
3859
|
+
const result = await getResult();
|
|
3757
3860
|
if (!result.success) {
|
|
3758
|
-
|
|
3759
|
-
|
|
3861
|
+
return { status: 'failed', detail: result.data };
|
|
3862
|
+
}
|
|
3863
|
+
const command = result.data;
|
|
3864
|
+
if (command == null) {
|
|
3865
|
+
return { status: 'ok' };
|
|
3866
|
+
}
|
|
3867
|
+
if (command === '') {
|
|
3868
|
+
return { status: 'failed', detail: 'empty command string' };
|
|
3760
3869
|
}
|
|
3761
|
-
|
|
3870
|
+
const execResult = await execAsync(command, { cwd });
|
|
3871
|
+
logger.verbose(`${execLabel} ${JSON.stringify(execResult)}`);
|
|
3872
|
+
if (!execResult.success) {
|
|
3873
|
+
return { status: 'failed', detail: execResult.data.message };
|
|
3874
|
+
}
|
|
3875
|
+
return { status: 'ok' };
|
|
3762
3876
|
}
|
|
3763
3877
|
catch (error) {
|
|
3764
3878
|
const message = error instanceof Error ? error.message : String(error);
|
|
3765
|
-
|
|
3766
|
-
return 'failed';
|
|
3879
|
+
return { status: 'failed', detail: message };
|
|
3767
3880
|
}
|
|
3768
3881
|
}
|
|
3769
|
-
async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList,
|
|
3882
|
+
async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList, gitAddCommitFn, gitPushFn, gitCommitMessageFn, projectRoot, steps, setupFn, setupWorkspaceFn, teardownFn, teardownWorkspaceFn, getKeepHistoryFilePathFn, logger: loggerInput, signal, }) {
|
|
3770
3883
|
const logger = loggerInput ?? createConsoleLogger({});
|
|
3771
3884
|
const contextNames = contextList.map(context => context.name);
|
|
3772
3885
|
logger.verbose(`contextNames ${JSON.stringify(contextNames)}`);
|
|
@@ -3792,7 +3905,9 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
|
|
|
3792
3905
|
});
|
|
3793
3906
|
logger.verbose(`setupWorkspaceCommandExec ${JSON.stringify(setupWorkspaceCommandExec)}`);
|
|
3794
3907
|
if (!setupWorkspaceCommandExec.success) {
|
|
3795
|
-
return
|
|
3908
|
+
return failure({
|
|
3909
|
+
message: `Failed to setup the workspace: ${setupWorkspaceCommandExec.data.message}`,
|
|
3910
|
+
});
|
|
3796
3911
|
}
|
|
3797
3912
|
if (afterExec) {
|
|
3798
3913
|
await afterExec({ workspacePath });
|
|
@@ -3804,6 +3919,10 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
|
|
|
3804
3919
|
let runFailure;
|
|
3805
3920
|
try {
|
|
3806
3921
|
for (let i = 0; i < contextList.length; i++) {
|
|
3922
|
+
if (signal?.aborted) {
|
|
3923
|
+
runFailure = stepWalkAbortedFailure();
|
|
3924
|
+
break;
|
|
3925
|
+
}
|
|
3807
3926
|
const context = contextList[i];
|
|
3808
3927
|
logger.info(contextList.length > 1
|
|
3809
3928
|
? `Running context "${context.name}" (${i + 1}/${contextList.length})`
|
|
@@ -3816,127 +3935,137 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
|
|
|
3816
3935
|
const contextRunState = setupResult?.contextRunState || {};
|
|
3817
3936
|
let stepWalkFailure;
|
|
3818
3937
|
async function walkAndExecuteSteps(stepsToExec, currStepIndex) {
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
if (typeof step === 'function') {
|
|
3829
|
-
subSteps =
|
|
3938
|
+
try {
|
|
3939
|
+
for (let stepIndex = 0; stepIndex < stepsToExec.length; stepIndex++) {
|
|
3940
|
+
if (stepWalkFailure) {
|
|
3941
|
+
return;
|
|
3942
|
+
}
|
|
3943
|
+
signal?.throwIfAborted();
|
|
3944
|
+
const step = stepsToExec[stepIndex];
|
|
3945
|
+
const nextCallHeadIndex = [...currStepIndex, stepIndex];
|
|
3946
|
+
const compositeStepIndex = nextCallHeadIndex.length === 1 ? nextCallHeadIndex[0] : nextCallHeadIndex;
|
|
3947
|
+
if (typeof step === 'function' || Array.isArray(step)) {
|
|
3948
|
+
const subSteps = typeof step === 'function'
|
|
3949
|
+
? await awaitUnlessAborted(step({
|
|
3950
|
+
context,
|
|
3951
|
+
stepIndex: compositeStepIndex,
|
|
3952
|
+
contextRunState,
|
|
3953
|
+
lumpVariables,
|
|
3954
|
+
}), signal)
|
|
3955
|
+
: step;
|
|
3956
|
+
await walkAndExecuteSteps(subSteps, nextCallHeadIndex);
|
|
3957
|
+
continue;
|
|
3958
|
+
}
|
|
3959
|
+
logger.verbose(`step ${JSON.stringify(step)}`);
|
|
3960
|
+
const { commandFn = () => null, stepVariables, promptFn, postCommandExecFn, continueOnError, timeoutMillis = 1000 * 60 * 30, } = step;
|
|
3961
|
+
const prompt = promptFn
|
|
3962
|
+
? await awaitUnlessAborted(promptFn({
|
|
3830
3963
|
context,
|
|
3831
3964
|
stepIndex: compositeStepIndex,
|
|
3832
3965
|
contextRunState,
|
|
3833
3966
|
lumpVariables,
|
|
3967
|
+
stepVariables,
|
|
3968
|
+
}), signal)
|
|
3969
|
+
: '';
|
|
3970
|
+
const command = await awaitUnlessAborted(commandFn({
|
|
3971
|
+
context,
|
|
3972
|
+
prompt,
|
|
3973
|
+
stepIndex: compositeStepIndex,
|
|
3974
|
+
contextRunState,
|
|
3975
|
+
lumpVariables,
|
|
3976
|
+
stepVariables,
|
|
3977
|
+
projectRoot,
|
|
3978
|
+
workspacePath,
|
|
3979
|
+
}), signal);
|
|
3980
|
+
let commandResult = '';
|
|
3981
|
+
let commandSucceeded = true;
|
|
3982
|
+
if (command != null) {
|
|
3983
|
+
const { executable, args, env } = command;
|
|
3984
|
+
logger.verbose(`command for prompt ${executable} ${args.join(' ')}`);
|
|
3985
|
+
if (env != null) {
|
|
3986
|
+
logger.verbose(`command env overrides ${JSON.stringify(env)}`);
|
|
3987
|
+
}
|
|
3988
|
+
logger.verbose(`workspacePath ${workspacePath}`);
|
|
3989
|
+
const commandExec = await execBinary({
|
|
3990
|
+
binaryPath: executable,
|
|
3991
|
+
args,
|
|
3992
|
+
timeoutMillis,
|
|
3993
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
3994
|
+
cwd: workspacePath,
|
|
3995
|
+
signal,
|
|
3996
|
+
...(env != null ? { env: { ...process.env, ...env } } : {}),
|
|
3834
3997
|
});
|
|
3998
|
+
logger.verbose(`commandExec ${JSON.stringify(commandExec)}`);
|
|
3999
|
+
if (!commandExec.success) {
|
|
4000
|
+
const aborted = commandExec.data.reason === 'aborted';
|
|
4001
|
+
if (aborted || !continueOnError) {
|
|
4002
|
+
stepWalkFailure = {
|
|
4003
|
+
success: false,
|
|
4004
|
+
data: {
|
|
4005
|
+
message: `Failed to run the command: ${commandExec.data.message}. Command: ${executable} ${args.join(' ')}`,
|
|
4006
|
+
reason: 'stepWalkFailed',
|
|
4007
|
+
},
|
|
4008
|
+
};
|
|
4009
|
+
return;
|
|
4010
|
+
}
|
|
4011
|
+
commandSucceeded = false;
|
|
4012
|
+
commandResult = (commandExec.data.stdout
|
|
4013
|
+
|| commandExec.data.stderr
|
|
4014
|
+
|| commandExec.data.message
|
|
4015
|
+
|| '').toString();
|
|
4016
|
+
logger.verbose(`commandResult ${commandResult}`);
|
|
4017
|
+
}
|
|
4018
|
+
else {
|
|
4019
|
+
commandResult = (commandExec.data.stdout || commandExec.data.stderr || '').toString();
|
|
4020
|
+
logger.verbose(`commandResult ${commandResult}`);
|
|
4021
|
+
}
|
|
4022
|
+
if (commandSucceeded) {
|
|
4023
|
+
const gitStatusAfterCommand = await execAsync(`git status`, { cwd: workspacePath });
|
|
4024
|
+
logger.verbose(`gitStatusCommand ${JSON.stringify(gitStatusAfterCommand.data)}`);
|
|
4025
|
+
}
|
|
3835
4026
|
}
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
await walkAndExecuteSteps(subSteps, nextCallHeadIndex);
|
|
3840
|
-
continue;
|
|
3841
|
-
}
|
|
3842
|
-
logger.verbose(`step ${JSON.stringify(step)}`);
|
|
3843
|
-
const { commandFn = () => null, stepVariables, promptFn, postCommandExecFn, continueOnError, timeoutMillis = 1000 * 60 * 30, } = step;
|
|
3844
|
-
const prompt = promptFn
|
|
3845
|
-
? await promptFn({
|
|
4027
|
+
const historyEntry = {
|
|
4028
|
+
commandResult,
|
|
4029
|
+
commandSucceeded,
|
|
3846
4030
|
context,
|
|
4031
|
+
prompt,
|
|
3847
4032
|
stepIndex: compositeStepIndex,
|
|
3848
4033
|
contextRunState,
|
|
3849
4034
|
lumpVariables,
|
|
3850
4035
|
stepVariables,
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
prompt,
|
|
3856
|
-
stepIndex: compositeStepIndex,
|
|
3857
|
-
contextRunState,
|
|
3858
|
-
lumpVariables,
|
|
3859
|
-
stepVariables,
|
|
3860
|
-
projectRoot,
|
|
3861
|
-
workspacePath,
|
|
3862
|
-
});
|
|
3863
|
-
let commandResult = '';
|
|
3864
|
-
let commandSucceeded = true;
|
|
3865
|
-
if (command != null) {
|
|
3866
|
-
const { executable, args, env } = command;
|
|
3867
|
-
logger.verbose(`command for prompt ${executable} ${args.join(' ')}`);
|
|
3868
|
-
if (env != null) {
|
|
3869
|
-
logger.verbose(`command env overrides ${JSON.stringify(env)}`);
|
|
3870
|
-
}
|
|
3871
|
-
logger.verbose(`workspacePath ${workspacePath}`);
|
|
3872
|
-
const commandExec = await execBinary({
|
|
3873
|
-
binaryPath: executable,
|
|
3874
|
-
args,
|
|
3875
|
-
timeoutMillis,
|
|
3876
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
3877
|
-
cwd: workspacePath,
|
|
4036
|
+
projectRoot,
|
|
4037
|
+
};
|
|
4038
|
+
const postCommandExecFnInput = {
|
|
4039
|
+
...historyEntry,
|
|
3878
4040
|
signal,
|
|
3879
|
-
|
|
3880
|
-
});
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
};
|
|
3892
|
-
return;
|
|
4041
|
+
};
|
|
4042
|
+
logger.verbose(`context is ${JSON.stringify(context)}`);
|
|
4043
|
+
const keepHistoryFilePath = getKeepHistoryFilePathFn(context) || '';
|
|
4044
|
+
logger.verbose(`keepHistoryFilePath ${keepHistoryFilePath}`);
|
|
4045
|
+
if (!!command && keepHistoryFilePath.length > 0) {
|
|
4046
|
+
const appendResult = await appendHistoryEntry({
|
|
4047
|
+
filePath: keepHistoryFilePath,
|
|
4048
|
+
entry: historyEntry,
|
|
4049
|
+
});
|
|
4050
|
+
if (!appendResult.success) {
|
|
4051
|
+
// TODO: sanitize history appending to avoid this warning for certain commands outputs
|
|
4052
|
+
logger.warn(`Failed to append history entry to ${keepHistoryFilePath}: ${appendResult.data}`);
|
|
3893
4053
|
}
|
|
3894
|
-
commandSucceeded = false;
|
|
3895
|
-
commandResult = (commandExec.data.stdout
|
|
3896
|
-
|| commandExec.data.stderr
|
|
3897
|
-
|| commandExec.data.message
|
|
3898
|
-
|| '').toString();
|
|
3899
|
-
logger.verbose(`commandResult ${commandResult}`);
|
|
3900
|
-
}
|
|
3901
|
-
else {
|
|
3902
|
-
commandResult = (commandExec.data.stdout || commandExec.data.stderr || '').toString();
|
|
3903
|
-
logger.verbose(`commandResult ${commandResult}`);
|
|
3904
|
-
}
|
|
3905
|
-
if (commandSucceeded) {
|
|
3906
|
-
const gitStatusAfterCommand = await execAsync(`git status`, { cwd: workspacePath });
|
|
3907
|
-
logger.verbose(`gitStatusCommand ${JSON.stringify(gitStatusAfterCommand.data)}`);
|
|
3908
4054
|
}
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
prompt,
|
|
3915
|
-
stepIndex: compositeStepIndex,
|
|
3916
|
-
contextRunState,
|
|
3917
|
-
lumpVariables,
|
|
3918
|
-
stepVariables,
|
|
3919
|
-
projectRoot,
|
|
3920
|
-
};
|
|
3921
|
-
logger.verbose(`context is ${JSON.stringify(context)}`);
|
|
3922
|
-
const keepHistoryFilePath = getKeepHistoryFilePathFn(context) || '';
|
|
3923
|
-
logger.verbose(`keepHistoryFilePath ${keepHistoryFilePath}`);
|
|
3924
|
-
if (!!command && keepHistoryFilePath.length > 0) {
|
|
3925
|
-
const appendResult = await appendHistoryEntry({
|
|
3926
|
-
filePath: keepHistoryFilePath,
|
|
3927
|
-
entry: postCommandExecFnInput,
|
|
3928
|
-
});
|
|
3929
|
-
if (!appendResult.success) {
|
|
3930
|
-
// TODO: sanitize history appending to avoid this warning for certain commands outputs
|
|
3931
|
-
logger.warn(`Failed to append history entry to ${keepHistoryFilePath}: ${appendResult.data}`);
|
|
4055
|
+
if (postCommandExecFn) {
|
|
4056
|
+
const returnedSteps = await awaitUnlessAborted(postCommandExecFn(postCommandExecFnInput), signal);
|
|
4057
|
+
if (returnedSteps != null && returnedSteps.length > 0) {
|
|
4058
|
+
await walkAndExecuteSteps(returnedSteps, nextCallHeadIndex);
|
|
4059
|
+
}
|
|
3932
4060
|
}
|
|
3933
4061
|
}
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
4062
|
+
}
|
|
4063
|
+
catch (error) {
|
|
4064
|
+
if (isStepWalkAbortError(error)) {
|
|
4065
|
+
stepWalkFailure = stepWalkAbortedFailure();
|
|
4066
|
+
return;
|
|
3939
4067
|
}
|
|
4068
|
+
throw error;
|
|
3940
4069
|
}
|
|
3941
4070
|
}
|
|
3942
4071
|
try {
|
|
@@ -3960,43 +4089,42 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
|
|
|
3960
4089
|
runFailure = stepWalkFailure;
|
|
3961
4090
|
break;
|
|
3962
4091
|
}
|
|
3963
|
-
const
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
4092
|
+
const commitMessage = gitCommitMessageFn({ context, lumpVariables, baseBranch });
|
|
4093
|
+
const addCommitExecLabel = `git add+commit for context ${context.name}`;
|
|
4094
|
+
const addCommitOutcome = await runGitResultHook({
|
|
4095
|
+
getResult: () => gitAddCommitFn({
|
|
4096
|
+
...injectedGitAndWorkspaceFnsInput,
|
|
4097
|
+
context,
|
|
4098
|
+
commitMessage,
|
|
4099
|
+
}),
|
|
3970
4100
|
cwd: workspacePath,
|
|
3971
4101
|
logger,
|
|
4102
|
+
execLabel: addCommitExecLabel,
|
|
3972
4103
|
});
|
|
3973
|
-
if (
|
|
4104
|
+
if (addCommitOutcome.status === 'failed') {
|
|
4105
|
+
const message = `Failed to add and commit for context ${context.name}: ${addCommitOutcome.detail}`;
|
|
4106
|
+
logger.error(message);
|
|
3974
4107
|
runFailure = {
|
|
3975
4108
|
success: false,
|
|
3976
4109
|
data: {
|
|
3977
|
-
message
|
|
4110
|
+
message,
|
|
4111
|
+
reason: 'gitAddCommitFailed',
|
|
3978
4112
|
},
|
|
3979
4113
|
};
|
|
3980
4114
|
break;
|
|
3981
4115
|
}
|
|
3982
|
-
const commitMessage = gitCommitMessageFn({ context, lumpVariables, baseBranch });
|
|
3983
|
-
await runOptionalGitCommand({
|
|
3984
|
-
label: `git commit for context ${context.name}`,
|
|
3985
|
-
getCommand: () => gitCommitCommandFn({
|
|
3986
|
-
...perContextInput,
|
|
3987
|
-
commitMessage,
|
|
3988
|
-
}),
|
|
3989
|
-
cwd: workspacePath,
|
|
3990
|
-
logger,
|
|
3991
|
-
});
|
|
3992
4116
|
}
|
|
3993
4117
|
if (!runFailure) {
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
4118
|
+
const pushExecLabel = `git push on branch ${branchName}`;
|
|
4119
|
+
const pushOutcome = await runGitResultHook({
|
|
4120
|
+
getResult: () => gitPushFn(injectedGitAndWorkspaceFnsInput),
|
|
3997
4121
|
cwd: workspacePath,
|
|
3998
4122
|
logger,
|
|
4123
|
+
execLabel: pushExecLabel,
|
|
3999
4124
|
});
|
|
4125
|
+
if (pushOutcome.status === 'failed') {
|
|
4126
|
+
logger.error(`Failed to ${pushExecLabel}: ${pushOutcome.detail}`);
|
|
4127
|
+
}
|
|
4000
4128
|
}
|
|
4001
4129
|
}
|
|
4002
4130
|
finally {
|
|
@@ -4138,13 +4266,13 @@ async function getContextStatus(params) {
|
|
|
4138
4266
|
if (!fetchResult.success)
|
|
4139
4267
|
return 'toDo';
|
|
4140
4268
|
}
|
|
4141
|
-
const logResult = await execAsync(`git log --remotes=${remoteName} -F --grep=${shellSingleQuote(commitMessage)} --format=${shellSingleQuote(
|
|
4269
|
+
const logResult = await execAsync(`git log --remotes=${remoteName} -F --grep=${shellSingleQuote(commitMessage)} --format=${shellSingleQuote(GIT_LOG_HASH_BODY_FORMAT)}`, { cwd: projectRoot });
|
|
4142
4270
|
if (!logResult.success)
|
|
4143
4271
|
return 'toDo';
|
|
4144
4272
|
logger?.verbose(`logResult ${JSON.stringify(logResult.data)}`);
|
|
4145
4273
|
const logResultOutput = logResult.data.stdout || logResult.data.stderr || '';
|
|
4146
|
-
const matchingHashes =
|
|
4147
|
-
.filter((entry) => entry.
|
|
4274
|
+
const matchingHashes = parseGitLogHashBodyRecords(logResultOutput)
|
|
4275
|
+
.filter((entry) => commitMessageIncludesMarker(entry.message, commitMessage))
|
|
4148
4276
|
.map((entry) => entry.hash);
|
|
4149
4277
|
logger?.verbose(`contextName ${contextName}`);
|
|
4150
4278
|
logger?.verbose(`remoteName ${remoteName}`);
|
|
@@ -4261,14 +4389,11 @@ const contextStatus = ['toDo', 'branchPushed', 'finished'];
|
|
|
4261
4389
|
const defaultGitCommitMessageFn = ({ context }) => {
|
|
4262
4390
|
return `LUMP:${context.name}`;
|
|
4263
4391
|
};
|
|
4264
|
-
const
|
|
4265
|
-
return `git
|
|
4266
|
-
};
|
|
4267
|
-
const defaultGitAddCommandFn = () => {
|
|
4268
|
-
return `git add .`;
|
|
4392
|
+
const defaultGitAddCommitFn = (input) => {
|
|
4393
|
+
return success(`git add . && git commit --allow-empty -m ${shellSingleQuote(input.commitMessage)}`);
|
|
4269
4394
|
};
|
|
4270
|
-
const
|
|
4271
|
-
return `git
|
|
4395
|
+
const defaultGitPushFn = (input) => {
|
|
4396
|
+
return success(`git push origin ${shellSingleQuote(input.branchName)}`);
|
|
4272
4397
|
};
|
|
4273
4398
|
const defaultSetupWorkspaceFn = async (input) => {
|
|
4274
4399
|
const { baseBranch, branchName } = input;
|
|
@@ -4306,7 +4431,7 @@ const defaultSetupWorkspaceFnWithWorktree = async (input) => {
|
|
|
4306
4431
|
};
|
|
4307
4432
|
|
|
4308
4433
|
async function runLump(input) {
|
|
4309
|
-
const { baseBranch, branchFn, lumpVariables: lumpVariablesInput, getContextListFn,
|
|
4434
|
+
const { baseBranch, branchFn, lumpVariables: lumpVariablesInput, getContextListFn, gitAddCommitFn = defaultGitAddCommitFn, gitCommitMessageFn = defaultGitCommitMessageFn, gitPushFn = defaultGitPushFn, numberOfContextsPerBranch = 1, projectRoot, steps, setupFn = () => ({ contextRunState: {} }), setupWorkspaceFn = defaultSetupWorkspaceFn, teardownFn = () => undefined, teardownWorkspaceFn = defaultTeardownWorkspaceFn, getKeepHistoryFilePathFn = () => undefined, logger: loggerInput, signal, refreshRemoteTrackingRefsFn, } = input;
|
|
4310
4435
|
const lumpVariables = (lumpVariablesInput ?? {});
|
|
4311
4436
|
const logger = loggerInput ?? createConsoleLogger({});
|
|
4312
4437
|
const contextListToDoResult = await getToDoContextList({
|
|
@@ -4341,10 +4466,9 @@ async function runLump(input) {
|
|
|
4341
4466
|
branchFn: branchFn,
|
|
4342
4467
|
lumpVariables: lumpVariables,
|
|
4343
4468
|
contextList: nextContextsForBranchList,
|
|
4344
|
-
|
|
4345
|
-
gitCommitCommandFn,
|
|
4469
|
+
gitAddCommitFn,
|
|
4346
4470
|
gitCommitMessageFn,
|
|
4347
|
-
|
|
4471
|
+
gitPushFn,
|
|
4348
4472
|
projectRoot,
|
|
4349
4473
|
steps,
|
|
4350
4474
|
setupFn,
|
|
@@ -4363,5 +4487,5 @@ async function runLump(input) {
|
|
|
4363
4487
|
});
|
|
4364
4488
|
}
|
|
4365
4489
|
|
|
4366
|
-
export { appendHistoryEntry, collectStepsForContext, contextStatus, createConsoleLogger,
|
|
4490
|
+
export { GIT_LOG_HASH_BODY_FORMAT, appendHistoryEntry, collectStepsForContext, commitMessageIncludesMarker, contextStatus, createConsoleLogger, defaultGitAddCommitFn, defaultGitCommitMessageFn, defaultGitPushFn, defaultSetupWorkspaceFn, defaultSetupWorkspaceFnWithWorktree, defaultTeardownWorkspaceFn, defaultTeardownWorkspaceFnWithWorktree, execAsync, execBinary, executeStepsForContextList, failure, formatExecFailureMessage, getCodeBasePaths, getContextStatus, getToDoContextList, historyFormatFromPath, isProcessAlive, killProcessTree, nodeErrnoCode, parseGitLogHashBodyRecords, parseGitLogHashSubjectLines, pathExists, readHistoryFile, refreshRemoteTrackingRefs, resolveSpawnExecutable, runLump, set, shellSingleQuote, success, validateContextListNames, windowsNpmCmdShimBody, writeHistoryFile };
|
|
4367
4491
|
//# sourceMappingURL=index.js.map
|