@lumpcode/core 0.0.16 → 0.1.1
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 +3 -3
- package/dist/helpers/executeStepsForContextList/main.d.ts.map +1 -1
- package/dist/helpers/getContextStatus/main.d.ts +5 -0
- package/dist/helpers/getContextStatus/main.d.ts.map +1 -1
- package/dist/helpers/getToDoContextList/main.d.ts +6 -0
- package/dist/helpers/getToDoContextList/main.d.ts.map +1 -1
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/refreshRemoteTrackingRefs/index.d.ts +2 -0
- package/dist/helpers/refreshRemoteTrackingRefs/index.d.ts.map +1 -0
- package/dist/helpers/refreshRemoteTrackingRefs/main.d.ts +11 -0
- package/dist/helpers/refreshRemoteTrackingRefs/main.d.ts.map +1 -0
- package/dist/index.cjs +289 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +286 -145
- package/dist/index.js.map +1 -1
- package/dist/types/ExecShellFn.d.ts +16 -0
- package/dist/types/ExecShellFn.d.ts.map +1 -0
- package/dist/types/GitAddCommandFn.d.ts +7 -1
- package/dist/types/GitAddCommandFn.d.ts.map +1 -1
- package/dist/types/GitCommitCommandFn.d.ts +7 -1
- package/dist/types/GitCommitCommandFn.d.ts.map +1 -1
- package/dist/types/GitPushCommandFn.d.ts +7 -1
- package/dist/types/GitPushCommandFn.d.ts.map +1 -1
- package/dist/types/PostCommandExecFn.d.ts +2 -0
- package/dist/types/PostCommandExecFn.d.ts.map +1 -1
- package/dist/usages/runLump/main.d.ts +6 -1
- 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) {
|
|
@@ -3745,6 +3779,75 @@ async function collectStepsForContext(params) {
|
|
|
3745
3779
|
return collectedSteps;
|
|
3746
3780
|
}
|
|
3747
3781
|
|
|
3782
|
+
const ABORT_STEP_WALK_MESSAGE = 'Process aborted';
|
|
3783
|
+
function stepWalkAbortedFailure() {
|
|
3784
|
+
return {
|
|
3785
|
+
success: false,
|
|
3786
|
+
data: {
|
|
3787
|
+
message: ABORT_STEP_WALK_MESSAGE,
|
|
3788
|
+
reason: 'stepWalkFailed',
|
|
3789
|
+
},
|
|
3790
|
+
};
|
|
3791
|
+
}
|
|
3792
|
+
function createStepWalkAbortError() {
|
|
3793
|
+
const error = new Error(ABORT_STEP_WALK_MESSAGE);
|
|
3794
|
+
error.name = 'AbortError';
|
|
3795
|
+
return error;
|
|
3796
|
+
}
|
|
3797
|
+
function isStepWalkAbortError(error) {
|
|
3798
|
+
return (typeof error === 'object'
|
|
3799
|
+
&& error !== null
|
|
3800
|
+
&& 'name' in error
|
|
3801
|
+
&& error.name === 'AbortError');
|
|
3802
|
+
}
|
|
3803
|
+
/**
|
|
3804
|
+
* Await user-hook work, but stop waiting once `signal` aborts so the walk can
|
|
3805
|
+
* unwind (locks/teardown). Does not cancel sync busy-loops on the event loop.
|
|
3806
|
+
*/
|
|
3807
|
+
async function awaitUnlessAborted(work, signal) {
|
|
3808
|
+
if (!signal) {
|
|
3809
|
+
return await work;
|
|
3810
|
+
}
|
|
3811
|
+
signal.throwIfAborted();
|
|
3812
|
+
return new Promise((resolve, reject) => {
|
|
3813
|
+
const onAbort = () => {
|
|
3814
|
+
cleanup();
|
|
3815
|
+
reject(createStepWalkAbortError());
|
|
3816
|
+
};
|
|
3817
|
+
const cleanup = () => {
|
|
3818
|
+
signal.removeEventListener('abort', onAbort);
|
|
3819
|
+
};
|
|
3820
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
3821
|
+
Promise.resolve(work).then((value) => {
|
|
3822
|
+
cleanup();
|
|
3823
|
+
resolve(value);
|
|
3824
|
+
}, (error) => {
|
|
3825
|
+
cleanup();
|
|
3826
|
+
reject(error);
|
|
3827
|
+
});
|
|
3828
|
+
});
|
|
3829
|
+
}
|
|
3830
|
+
async function runOptionalGitCommand(input) {
|
|
3831
|
+
const { label, getCommand, cwd, logger } = input;
|
|
3832
|
+
try {
|
|
3833
|
+
const command = await getCommand();
|
|
3834
|
+
if (command == null || command === '') {
|
|
3835
|
+
return 'ok';
|
|
3836
|
+
}
|
|
3837
|
+
const result = await execAsync(command, { cwd });
|
|
3838
|
+
logger.verbose(`${label} ${JSON.stringify(result)}`);
|
|
3839
|
+
if (!result.success) {
|
|
3840
|
+
logger.error(formatExecFailureMessage({ label, failure: result }));
|
|
3841
|
+
return 'failed';
|
|
3842
|
+
}
|
|
3843
|
+
return 'ok';
|
|
3844
|
+
}
|
|
3845
|
+
catch (error) {
|
|
3846
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3847
|
+
logger.error(`Failed to run ${label}: ${message}`);
|
|
3848
|
+
return 'failed';
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3748
3851
|
async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList, gitAddCommandFn, gitCommitCommandFn, gitPushCommandFn, gitCommitMessageFn, projectRoot, steps, setupFn, setupWorkspaceFn, teardownFn, teardownWorkspaceFn, getKeepHistoryFilePathFn, logger: loggerInput, signal, }) {
|
|
3749
3852
|
const logger = loggerInput ?? createConsoleLogger({});
|
|
3750
3853
|
const contextNames = contextList.map(context => context.name);
|
|
@@ -3783,6 +3886,10 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
|
|
|
3783
3886
|
let runFailure;
|
|
3784
3887
|
try {
|
|
3785
3888
|
for (let i = 0; i < contextList.length; i++) {
|
|
3889
|
+
if (signal?.aborted) {
|
|
3890
|
+
runFailure = stepWalkAbortedFailure();
|
|
3891
|
+
break;
|
|
3892
|
+
}
|
|
3786
3893
|
const context = contextList[i];
|
|
3787
3894
|
logger.info(contextList.length > 1
|
|
3788
3895
|
? `Running context "${context.name}" (${i + 1}/${contextList.length})`
|
|
@@ -3795,127 +3902,137 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
|
|
|
3795
3902
|
const contextRunState = setupResult?.contextRunState || {};
|
|
3796
3903
|
let stepWalkFailure;
|
|
3797
3904
|
async function walkAndExecuteSteps(stepsToExec, currStepIndex) {
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
if (typeof step === 'function') {
|
|
3808
|
-
subSteps =
|
|
3905
|
+
try {
|
|
3906
|
+
for (let stepIndex = 0; stepIndex < stepsToExec.length; stepIndex++) {
|
|
3907
|
+
if (stepWalkFailure) {
|
|
3908
|
+
return;
|
|
3909
|
+
}
|
|
3910
|
+
signal?.throwIfAborted();
|
|
3911
|
+
const step = stepsToExec[stepIndex];
|
|
3912
|
+
const nextCallHeadIndex = [...currStepIndex, stepIndex];
|
|
3913
|
+
const compositeStepIndex = nextCallHeadIndex.length === 1 ? nextCallHeadIndex[0] : nextCallHeadIndex;
|
|
3914
|
+
if (typeof step === 'function' || Array.isArray(step)) {
|
|
3915
|
+
const subSteps = typeof step === 'function'
|
|
3916
|
+
? await awaitUnlessAborted(step({
|
|
3917
|
+
context,
|
|
3918
|
+
stepIndex: compositeStepIndex,
|
|
3919
|
+
contextRunState,
|
|
3920
|
+
lumpVariables,
|
|
3921
|
+
}), signal)
|
|
3922
|
+
: step;
|
|
3923
|
+
await walkAndExecuteSteps(subSteps, nextCallHeadIndex);
|
|
3924
|
+
continue;
|
|
3925
|
+
}
|
|
3926
|
+
logger.verbose(`step ${JSON.stringify(step)}`);
|
|
3927
|
+
const { commandFn = () => null, stepVariables, promptFn, postCommandExecFn, continueOnError, timeoutMillis = 1000 * 60 * 30, } = step;
|
|
3928
|
+
const prompt = promptFn
|
|
3929
|
+
? await awaitUnlessAborted(promptFn({
|
|
3809
3930
|
context,
|
|
3810
3931
|
stepIndex: compositeStepIndex,
|
|
3811
3932
|
contextRunState,
|
|
3812
3933
|
lumpVariables,
|
|
3934
|
+
stepVariables,
|
|
3935
|
+
}), signal)
|
|
3936
|
+
: '';
|
|
3937
|
+
const command = await awaitUnlessAborted(commandFn({
|
|
3938
|
+
context,
|
|
3939
|
+
prompt,
|
|
3940
|
+
stepIndex: compositeStepIndex,
|
|
3941
|
+
contextRunState,
|
|
3942
|
+
lumpVariables,
|
|
3943
|
+
stepVariables,
|
|
3944
|
+
projectRoot,
|
|
3945
|
+
workspacePath,
|
|
3946
|
+
}), signal);
|
|
3947
|
+
let commandResult = '';
|
|
3948
|
+
let commandSucceeded = true;
|
|
3949
|
+
if (command != null) {
|
|
3950
|
+
const { executable, args, env } = command;
|
|
3951
|
+
logger.verbose(`command for prompt ${executable} ${args.join(' ')}`);
|
|
3952
|
+
if (env != null) {
|
|
3953
|
+
logger.verbose(`command env overrides ${JSON.stringify(env)}`);
|
|
3954
|
+
}
|
|
3955
|
+
logger.verbose(`workspacePath ${workspacePath}`);
|
|
3956
|
+
const commandExec = await execBinary({
|
|
3957
|
+
binaryPath: executable,
|
|
3958
|
+
args,
|
|
3959
|
+
timeoutMillis,
|
|
3960
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
3961
|
+
cwd: workspacePath,
|
|
3962
|
+
signal,
|
|
3963
|
+
...(env != null ? { env: { ...process.env, ...env } } : {}),
|
|
3813
3964
|
});
|
|
3965
|
+
logger.verbose(`commandExec ${JSON.stringify(commandExec)}`);
|
|
3966
|
+
if (!commandExec.success) {
|
|
3967
|
+
const aborted = commandExec.data.reason === 'aborted';
|
|
3968
|
+
if (aborted || !continueOnError) {
|
|
3969
|
+
stepWalkFailure = {
|
|
3970
|
+
success: false,
|
|
3971
|
+
data: {
|
|
3972
|
+
message: `Failed to run the command: ${commandExec.data.message}. Command: ${executable} ${args.join(' ')}`,
|
|
3973
|
+
reason: 'stepWalkFailed',
|
|
3974
|
+
},
|
|
3975
|
+
};
|
|
3976
|
+
return;
|
|
3977
|
+
}
|
|
3978
|
+
commandSucceeded = false;
|
|
3979
|
+
commandResult = (commandExec.data.stdout
|
|
3980
|
+
|| commandExec.data.stderr
|
|
3981
|
+
|| commandExec.data.message
|
|
3982
|
+
|| '').toString();
|
|
3983
|
+
logger.verbose(`commandResult ${commandResult}`);
|
|
3984
|
+
}
|
|
3985
|
+
else {
|
|
3986
|
+
commandResult = (commandExec.data.stdout || commandExec.data.stderr || '').toString();
|
|
3987
|
+
logger.verbose(`commandResult ${commandResult}`);
|
|
3988
|
+
}
|
|
3989
|
+
if (commandSucceeded) {
|
|
3990
|
+
const gitStatusAfterCommand = await execAsync(`git status`, { cwd: workspacePath });
|
|
3991
|
+
logger.verbose(`gitStatusCommand ${JSON.stringify(gitStatusAfterCommand.data)}`);
|
|
3992
|
+
}
|
|
3814
3993
|
}
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
await walkAndExecuteSteps(subSteps, nextCallHeadIndex);
|
|
3819
|
-
continue;
|
|
3820
|
-
}
|
|
3821
|
-
logger.verbose(`step ${JSON.stringify(step)}`);
|
|
3822
|
-
const { commandFn = () => null, stepVariables, promptFn, postCommandExecFn, continueOnError, timeoutMillis = 1000 * 60 * 30, } = step;
|
|
3823
|
-
const prompt = promptFn
|
|
3824
|
-
? await promptFn({
|
|
3994
|
+
const historyEntry = {
|
|
3995
|
+
commandResult,
|
|
3996
|
+
commandSucceeded,
|
|
3825
3997
|
context,
|
|
3998
|
+
prompt,
|
|
3826
3999
|
stepIndex: compositeStepIndex,
|
|
3827
4000
|
contextRunState,
|
|
3828
4001
|
lumpVariables,
|
|
3829
4002
|
stepVariables,
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
prompt,
|
|
3835
|
-
stepIndex: compositeStepIndex,
|
|
3836
|
-
contextRunState,
|
|
3837
|
-
lumpVariables,
|
|
3838
|
-
stepVariables,
|
|
3839
|
-
projectRoot,
|
|
3840
|
-
workspacePath,
|
|
3841
|
-
});
|
|
3842
|
-
let commandResult = '';
|
|
3843
|
-
let commandSucceeded = true;
|
|
3844
|
-
if (command != null) {
|
|
3845
|
-
const { executable, args, env } = command;
|
|
3846
|
-
logger.verbose(`command for prompt ${executable} ${args.join(' ')}`);
|
|
3847
|
-
if (env != null) {
|
|
3848
|
-
logger.verbose(`command env overrides ${JSON.stringify(env)}`);
|
|
3849
|
-
}
|
|
3850
|
-
logger.verbose(`workspacePath ${workspacePath}`);
|
|
3851
|
-
const commandExec = await execBinary({
|
|
3852
|
-
binaryPath: executable,
|
|
3853
|
-
args,
|
|
3854
|
-
timeoutMillis,
|
|
3855
|
-
stdio: ['inherit', 'pipe', 'pipe'],
|
|
3856
|
-
cwd: workspacePath,
|
|
4003
|
+
projectRoot,
|
|
4004
|
+
};
|
|
4005
|
+
const postCommandExecFnInput = {
|
|
4006
|
+
...historyEntry,
|
|
3857
4007
|
signal,
|
|
3858
|
-
|
|
3859
|
-
});
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
};
|
|
3871
|
-
return;
|
|
4008
|
+
};
|
|
4009
|
+
logger.verbose(`context is ${JSON.stringify(context)}`);
|
|
4010
|
+
const keepHistoryFilePath = getKeepHistoryFilePathFn(context) || '';
|
|
4011
|
+
logger.verbose(`keepHistoryFilePath ${keepHistoryFilePath}`);
|
|
4012
|
+
if (!!command && keepHistoryFilePath.length > 0) {
|
|
4013
|
+
const appendResult = await appendHistoryEntry({
|
|
4014
|
+
filePath: keepHistoryFilePath,
|
|
4015
|
+
entry: historyEntry,
|
|
4016
|
+
});
|
|
4017
|
+
if (!appendResult.success) {
|
|
4018
|
+
// TODO: sanitize history appending to avoid this warning for certain commands outputs
|
|
4019
|
+
logger.warn(`Failed to append history entry to ${keepHistoryFilePath}: ${appendResult.data}`);
|
|
3872
4020
|
}
|
|
3873
|
-
commandSucceeded = false;
|
|
3874
|
-
commandResult = (commandExec.data.stdout
|
|
3875
|
-
|| commandExec.data.stderr
|
|
3876
|
-
|| commandExec.data.message
|
|
3877
|
-
|| '').toString();
|
|
3878
|
-
logger.verbose(`commandResult ${commandResult}`);
|
|
3879
4021
|
}
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
const gitStatusAfterCommand = await execAsync(`git status`, { cwd: workspacePath });
|
|
3886
|
-
logger.verbose(`gitStatusCommand ${JSON.stringify(gitStatusAfterCommand.data)}`);
|
|
3887
|
-
}
|
|
3888
|
-
}
|
|
3889
|
-
const postCommandExecFnInput = {
|
|
3890
|
-
commandResult,
|
|
3891
|
-
commandSucceeded,
|
|
3892
|
-
context,
|
|
3893
|
-
prompt,
|
|
3894
|
-
stepIndex: compositeStepIndex,
|
|
3895
|
-
contextRunState,
|
|
3896
|
-
lumpVariables,
|
|
3897
|
-
stepVariables,
|
|
3898
|
-
projectRoot,
|
|
3899
|
-
};
|
|
3900
|
-
logger.verbose(`context is ${JSON.stringify(context)}`);
|
|
3901
|
-
const keepHistoryFilePath = getKeepHistoryFilePathFn(context) || '';
|
|
3902
|
-
logger.verbose(`keepHistoryFilePath ${keepHistoryFilePath}`);
|
|
3903
|
-
if (!!command && keepHistoryFilePath.length > 0) {
|
|
3904
|
-
const appendResult = await appendHistoryEntry({
|
|
3905
|
-
filePath: keepHistoryFilePath,
|
|
3906
|
-
entry: postCommandExecFnInput,
|
|
3907
|
-
});
|
|
3908
|
-
if (!appendResult.success) {
|
|
3909
|
-
// TODO: sanitize history appending to avoid this warning for certain commands outputs
|
|
3910
|
-
logger.warn(`Failed to append history entry to ${keepHistoryFilePath}: ${appendResult.data}`);
|
|
4022
|
+
if (postCommandExecFn) {
|
|
4023
|
+
const returnedSteps = await awaitUnlessAborted(postCommandExecFn(postCommandExecFnInput), signal);
|
|
4024
|
+
if (returnedSteps != null && returnedSteps.length > 0) {
|
|
4025
|
+
await walkAndExecuteSteps(returnedSteps, nextCallHeadIndex);
|
|
4026
|
+
}
|
|
3911
4027
|
}
|
|
3912
4028
|
}
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
4029
|
+
}
|
|
4030
|
+
catch (error) {
|
|
4031
|
+
if (isStepWalkAbortError(error)) {
|
|
4032
|
+
stepWalkFailure = stepWalkAbortedFailure();
|
|
4033
|
+
return;
|
|
3918
4034
|
}
|
|
4035
|
+
throw error;
|
|
3919
4036
|
}
|
|
3920
4037
|
}
|
|
3921
4038
|
try {
|
|
@@ -3943,39 +4060,39 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
|
|
|
3943
4060
|
...injectedGitAndWorkspaceFnsInput,
|
|
3944
4061
|
context,
|
|
3945
4062
|
};
|
|
3946
|
-
const
|
|
3947
|
-
|
|
3948
|
-
|
|
4063
|
+
const addOutcome = await runOptionalGitCommand({
|
|
4064
|
+
label: `git add for context ${context.name}`,
|
|
4065
|
+
getCommand: () => gitAddCommandFn(perContextInput),
|
|
4066
|
+
cwd: workspacePath,
|
|
4067
|
+
logger,
|
|
4068
|
+
});
|
|
4069
|
+
if (addOutcome === 'failed') {
|
|
3949
4070
|
runFailure = {
|
|
3950
4071
|
success: false,
|
|
3951
4072
|
data: {
|
|
3952
|
-
message: `Failed to add the changes for context ${context.name}
|
|
4073
|
+
message: `Failed to add the changes for context ${context.name}`,
|
|
3953
4074
|
},
|
|
3954
4075
|
};
|
|
3955
4076
|
break;
|
|
3956
4077
|
}
|
|
3957
4078
|
const commitMessage = gitCommitMessageFn({ context, lumpVariables, baseBranch });
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
}));
|
|
3968
|
-
}
|
|
4079
|
+
await runOptionalGitCommand({
|
|
4080
|
+
label: `git commit for context ${context.name}`,
|
|
4081
|
+
getCommand: () => gitCommitCommandFn({
|
|
4082
|
+
...perContextInput,
|
|
4083
|
+
commitMessage,
|
|
4084
|
+
}),
|
|
4085
|
+
cwd: workspacePath,
|
|
4086
|
+
logger,
|
|
4087
|
+
});
|
|
3969
4088
|
}
|
|
3970
4089
|
if (!runFailure) {
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
}));
|
|
3978
|
-
}
|
|
4090
|
+
await runOptionalGitCommand({
|
|
4091
|
+
label: `git push on branch ${branchName}`,
|
|
4092
|
+
getCommand: () => gitPushCommandFn(injectedGitAndWorkspaceFnsInput),
|
|
4093
|
+
cwd: workspacePath,
|
|
4094
|
+
logger,
|
|
4095
|
+
});
|
|
3979
4096
|
}
|
|
3980
4097
|
}
|
|
3981
4098
|
finally {
|
|
@@ -4091,25 +4208,41 @@ async function scanDirectory({ dirPath, allPaths, gitignoresMap, projectRoot, lo
|
|
|
4091
4208
|
}
|
|
4092
4209
|
}
|
|
4093
4210
|
|
|
4211
|
+
/**
|
|
4212
|
+
* Refreshes remote-tracking refs for status / planning (one network fetch).
|
|
4213
|
+
* Uses `--no-write-fetch-head` so concurrent preflight/pull paths are safer.
|
|
4214
|
+
*/
|
|
4215
|
+
const refreshRemoteTrackingRefs = async (input) => {
|
|
4216
|
+
const { projectRoot, remoteName = 'origin' } = input;
|
|
4217
|
+
const result = await execAsync(`git fetch --prune --no-write-fetch-head ${remoteName}`, { cwd: projectRoot });
|
|
4218
|
+
if (!result.success) {
|
|
4219
|
+
return failure(result.data.message);
|
|
4220
|
+
}
|
|
4221
|
+
return success(undefined);
|
|
4222
|
+
};
|
|
4223
|
+
|
|
4094
4224
|
async function getContextStatus(params) {
|
|
4095
|
-
const { contextName, gitCommitMessageFn, projectRoot, baseBranch, lumpVariables: lumpVariablesInput, contextVariables = {}, remoteName = "origin", logger, } = params;
|
|
4225
|
+
const { contextName, gitCommitMessageFn, projectRoot, baseBranch, lumpVariables: lumpVariablesInput, contextVariables = {}, remoteName = "origin", logger, skipFetch = false, } = params;
|
|
4096
4226
|
const lumpVariables = (lumpVariablesInput ?? {});
|
|
4097
4227
|
const commitMessage = gitCommitMessageFn({
|
|
4098
4228
|
context: { name: contextName, variables: contextVariables },
|
|
4099
4229
|
lumpVariables,
|
|
4100
4230
|
baseBranch,
|
|
4101
4231
|
});
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4232
|
+
if (!skipFetch) {
|
|
4233
|
+
const fetchResult = await refreshRemoteTrackingRefs({ projectRoot, remoteName });
|
|
4234
|
+
if (!fetchResult.success)
|
|
4235
|
+
return 'toDo';
|
|
4236
|
+
}
|
|
4237
|
+
const logResult = await execAsync(`git log --remotes=${remoteName} -F --grep=${shellSingleQuote(commitMessage)} --format=${shellSingleQuote(GIT_LOG_HASH_BODY_FORMAT)}`, { cwd: projectRoot });
|
|
4106
4238
|
if (!logResult.success)
|
|
4107
4239
|
return 'toDo';
|
|
4108
4240
|
logger?.verbose(`logResult ${JSON.stringify(logResult.data)}`);
|
|
4109
4241
|
const logResultOutput = logResult.data.stdout || logResult.data.stderr || '';
|
|
4110
|
-
const matchingHashes =
|
|
4111
|
-
.filter((entry) => entry.
|
|
4242
|
+
const matchingHashes = parseGitLogHashBodyRecords(logResultOutput)
|
|
4243
|
+
.filter((entry) => commitMessageIncludesMarker(entry.message, commitMessage))
|
|
4112
4244
|
.map((entry) => entry.hash);
|
|
4245
|
+
logger?.verbose(`contextName ${contextName}`);
|
|
4113
4246
|
logger?.verbose(`remoteName ${remoteName}`);
|
|
4114
4247
|
logger?.verbose(`baseBranch ${baseBranch}`);
|
|
4115
4248
|
logger?.verbose(`commitMessage ${commitMessage}`);
|
|
@@ -4164,7 +4297,7 @@ function validateContextListNames(contextList) {
|
|
|
4164
4297
|
}
|
|
4165
4298
|
|
|
4166
4299
|
async function getToDoContextList(params) {
|
|
4167
|
-
const { getContextListFn, lumpVariables, gitCommitMessageFn, projectRoot, baseBranch, logger } = params;
|
|
4300
|
+
const { getContextListFn, lumpVariables, gitCommitMessageFn, projectRoot, baseBranch, logger, refreshRemoteTrackingRefsFn = refreshRemoteTrackingRefs, } = params;
|
|
4168
4301
|
const codeBasePathsResult = await getCodeBasePaths({ cwd: projectRoot, logger });
|
|
4169
4302
|
if (!codeBasePathsResult.success) {
|
|
4170
4303
|
return failure({
|
|
@@ -4182,21 +4315,28 @@ async function getToDoContextList(params) {
|
|
|
4182
4315
|
const allCtxNames = contextList.flatMap(context => [context.name, ...(context.options?.dependsOnContexts ?? [])]);
|
|
4183
4316
|
const allCtxNamesSet = new Set(allCtxNames);
|
|
4184
4317
|
const allCtxNamesList = Array.from(allCtxNamesSet);
|
|
4185
|
-
const
|
|
4186
|
-
|
|
4187
|
-
|
|
4318
|
+
const refreshResult = await refreshRemoteTrackingRefsFn({ projectRoot });
|
|
4319
|
+
let contextStatusMap;
|
|
4320
|
+
if (!refreshResult.success) {
|
|
4321
|
+
logger?.warn(`Failed to refresh remote-tracking refs for context status; treating contexts as toDo: ${refreshResult.data}`);
|
|
4322
|
+
contextStatusMap = new Map(allCtxNamesList.map((name) => [name, 'toDo']));
|
|
4323
|
+
}
|
|
4324
|
+
else {
|
|
4325
|
+
const contextStatusList = await Promise.all(allCtxNamesList.map((contextName) => getContextStatus({
|
|
4326
|
+
contextName,
|
|
4188
4327
|
contextVariables: {}, // TODO: Remove contextVariables from getContextStatus, really not needed
|
|
4189
4328
|
gitCommitMessageFn,
|
|
4190
4329
|
lumpVariables,
|
|
4191
4330
|
projectRoot,
|
|
4192
4331
|
baseBranch,
|
|
4193
4332
|
logger,
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4333
|
+
skipFetch: true,
|
|
4334
|
+
})));
|
|
4335
|
+
contextStatusMap = new Map(allCtxNamesList.map((contextName, i) => [contextName, contextStatusList[i]]));
|
|
4336
|
+
}
|
|
4197
4337
|
const contextListToDo = contextList
|
|
4198
|
-
.filter((context
|
|
4199
|
-
const contextStatus =
|
|
4338
|
+
.filter((context) => {
|
|
4339
|
+
const contextStatus = contextStatusMap.get(context.name);
|
|
4200
4340
|
if (contextStatus && contextStatus !== 'toDo')
|
|
4201
4341
|
return false;
|
|
4202
4342
|
const deps = context.options?.dependsOnContexts;
|
|
@@ -4262,7 +4402,7 @@ const defaultSetupWorkspaceFnWithWorktree = async (input) => {
|
|
|
4262
4402
|
};
|
|
4263
4403
|
|
|
4264
4404
|
async function runLump(input) {
|
|
4265
|
-
const { baseBranch, branchFn, lumpVariables: lumpVariablesInput, getContextListFn, gitAddCommandFn = defaultGitAddCommandFn, gitCommitCommandFn = defaultGitCommitCommandFn, gitCommitMessageFn = defaultGitCommitMessageFn, gitPushCommandFn = defaultGitPushCommandFn, numberOfContextsPerBranch = 1, projectRoot, steps, setupFn = () => ({ contextRunState: {} }), setupWorkspaceFn = defaultSetupWorkspaceFn, teardownFn = () => undefined, teardownWorkspaceFn = defaultTeardownWorkspaceFn, getKeepHistoryFilePathFn = () => undefined, logger: loggerInput, signal, } = input;
|
|
4405
|
+
const { baseBranch, branchFn, lumpVariables: lumpVariablesInput, getContextListFn, gitAddCommandFn = defaultGitAddCommandFn, gitCommitCommandFn = defaultGitCommitCommandFn, gitCommitMessageFn = defaultGitCommitMessageFn, gitPushCommandFn = defaultGitPushCommandFn, numberOfContextsPerBranch = 1, projectRoot, steps, setupFn = () => ({ contextRunState: {} }), setupWorkspaceFn = defaultSetupWorkspaceFn, teardownFn = () => undefined, teardownWorkspaceFn = defaultTeardownWorkspaceFn, getKeepHistoryFilePathFn = () => undefined, logger: loggerInput, signal, refreshRemoteTrackingRefsFn, } = input;
|
|
4266
4406
|
const lumpVariables = (lumpVariablesInput ?? {});
|
|
4267
4407
|
const logger = loggerInput ?? createConsoleLogger({});
|
|
4268
4408
|
const contextListToDoResult = await getToDoContextList({
|
|
@@ -4272,6 +4412,7 @@ async function runLump(input) {
|
|
|
4272
4412
|
baseBranch,
|
|
4273
4413
|
gitCommitMessageFn,
|
|
4274
4414
|
logger,
|
|
4415
|
+
refreshRemoteTrackingRefsFn,
|
|
4275
4416
|
});
|
|
4276
4417
|
if (!contextListToDoResult.success) {
|
|
4277
4418
|
return set(contextListToDoResult, ['data', 'message'], "Error in runLump: Failed to get to do context list. Original Error: " + contextListToDoResult.data.message);
|
|
@@ -4318,5 +4459,5 @@ async function runLump(input) {
|
|
|
4318
4459
|
});
|
|
4319
4460
|
}
|
|
4320
4461
|
|
|
4321
|
-
export { appendHistoryEntry, collectStepsForContext, contextStatus, createConsoleLogger, defaultGitAddCommandFn, defaultGitCommitCommandFn, defaultGitCommitMessageFn, defaultGitPushCommandFn, defaultSetupWorkspaceFn, defaultSetupWorkspaceFnWithWorktree, defaultTeardownWorkspaceFn, defaultTeardownWorkspaceFnWithWorktree, execAsync, execBinary, executeStepsForContextList, failure, formatExecFailureMessage, getCodeBasePaths, getContextStatus, getToDoContextList, historyFormatFromPath, isProcessAlive, killProcessTree, nodeErrnoCode, parseGitLogHashSubjectLines, pathExists, readHistoryFile, resolveSpawnExecutable, runLump, set, shellSingleQuote, success, validateContextListNames, windowsNpmCmdShimBody, writeHistoryFile };
|
|
4462
|
+
export { GIT_LOG_HASH_BODY_FORMAT, appendHistoryEntry, collectStepsForContext, commitMessageIncludesMarker, contextStatus, createConsoleLogger, defaultGitAddCommandFn, defaultGitCommitCommandFn, defaultGitCommitMessageFn, defaultGitPushCommandFn, 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 };
|
|
4322
4463
|
//# sourceMappingURL=index.js.map
|