@lumpcode/core 0.1.1 → 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/dist/index.js CHANGED
@@ -3584,32 +3584,54 @@ async function killProcessTree(input) {
3584
3584
  }
3585
3585
 
3586
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
+ }
3587
3606
  async function execAsync(command, options) {
3588
- const { stdout, stderr, hasErrored } = await execAsyncBase(command, { cwd: options?.cwd })
3589
- .then(result => ({
3590
- stdout: result.stdout,
3591
- stderr: result.stderr,
3592
- hasErrored: false,
3593
- }))
3594
- .catch(e => ({
3595
- stderr: e,
3596
- stdout: e,
3597
- hasErrored: true,
3598
- }));
3599
- if (hasErrored) {
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';
3600
3623
  return failure({
3601
- message: `Command ${command} failed with error: ${stderr}`,
3624
+ message: timedOut
3625
+ ? `Command ${command} timed out after ${timeoutMillis}ms`
3626
+ : `Command ${command} failed with error: ${e}`,
3627
+ reason,
3602
3628
  info: {
3603
3629
  command,
3604
- stdout,
3605
- stderr,
3630
+ stdout: e,
3631
+ stderr: e,
3606
3632
  },
3607
3633
  });
3608
3634
  }
3609
- return success({
3610
- stdout,
3611
- stderr,
3612
- });
3613
3635
  }
3614
3636
 
3615
3637
  function execBinary(input) {
@@ -3827,28 +3849,37 @@ async function awaitUnlessAborted(work, signal) {
3827
3849
  });
3828
3850
  });
3829
3851
  }
3830
- async function runOptionalGitCommand(input) {
3831
- const { label, getCommand, cwd, logger } = input;
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;
3832
3858
  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)}`);
3859
+ const result = await getResult();
3839
3860
  if (!result.success) {
3840
- logger.error(formatExecFailureMessage({ label, failure: result }));
3841
- return 'failed';
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' };
3869
+ }
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 };
3842
3874
  }
3843
- return 'ok';
3875
+ return { status: 'ok' };
3844
3876
  }
3845
3877
  catch (error) {
3846
3878
  const message = error instanceof Error ? error.message : String(error);
3847
- logger.error(`Failed to run ${label}: ${message}`);
3848
- return 'failed';
3879
+ return { status: 'failed', detail: message };
3849
3880
  }
3850
3881
  }
3851
- async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList, gitAddCommandFn, gitCommitCommandFn, gitPushCommandFn, gitCommitMessageFn, projectRoot, steps, setupFn, setupWorkspaceFn, teardownFn, teardownWorkspaceFn, getKeepHistoryFilePathFn, logger: loggerInput, signal, }) {
3882
+ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables, contextList, gitAddCommitFn, gitPushFn, gitCommitMessageFn, projectRoot, steps, setupFn, setupWorkspaceFn, teardownFn, teardownWorkspaceFn, getKeepHistoryFilePathFn, logger: loggerInput, signal, }) {
3852
3883
  const logger = loggerInput ?? createConsoleLogger({});
3853
3884
  const contextNames = contextList.map(context => context.name);
3854
3885
  logger.verbose(`contextNames ${JSON.stringify(contextNames)}`);
@@ -3874,7 +3905,9 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
3874
3905
  });
3875
3906
  logger.verbose(`setupWorkspaceCommandExec ${JSON.stringify(setupWorkspaceCommandExec)}`);
3876
3907
  if (!setupWorkspaceCommandExec.success) {
3877
- return set(setupWorkspaceCommandExec, ['data', 'message'], `Failed to setup the workspace: ${setupWorkspaceCommandExec.data.message}`);
3908
+ return failure({
3909
+ message: `Failed to setup the workspace: ${setupWorkspaceCommandExec.data.message}`,
3910
+ });
3878
3911
  }
3879
3912
  if (afterExec) {
3880
3913
  await afterExec({ workspacePath });
@@ -4056,43 +4089,42 @@ async function executeStepsForContextList({ baseBranch, branchFn, lumpVariables,
4056
4089
  runFailure = stepWalkFailure;
4057
4090
  break;
4058
4091
  }
4059
- const perContextInput = {
4060
- ...injectedGitAndWorkspaceFnsInput,
4061
- context,
4062
- };
4063
- const addOutcome = await runOptionalGitCommand({
4064
- label: `git add for context ${context.name}`,
4065
- getCommand: () => gitAddCommandFn(perContextInput),
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
+ }),
4066
4100
  cwd: workspacePath,
4067
4101
  logger,
4102
+ execLabel: addCommitExecLabel,
4068
4103
  });
4069
- if (addOutcome === 'failed') {
4104
+ if (addCommitOutcome.status === 'failed') {
4105
+ const message = `Failed to add and commit for context ${context.name}: ${addCommitOutcome.detail}`;
4106
+ logger.error(message);
4070
4107
  runFailure = {
4071
4108
  success: false,
4072
4109
  data: {
4073
- message: `Failed to add the changes for context ${context.name}`,
4110
+ message,
4111
+ reason: 'gitAddCommitFailed',
4074
4112
  },
4075
4113
  };
4076
4114
  break;
4077
4115
  }
4078
- const commitMessage = gitCommitMessageFn({ context, lumpVariables, baseBranch });
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
- });
4088
4116
  }
4089
4117
  if (!runFailure) {
4090
- await runOptionalGitCommand({
4091
- label: `git push on branch ${branchName}`,
4092
- getCommand: () => gitPushCommandFn(injectedGitAndWorkspaceFnsInput),
4118
+ const pushExecLabel = `git push on branch ${branchName}`;
4119
+ const pushOutcome = await runGitResultHook({
4120
+ getResult: () => gitPushFn(injectedGitAndWorkspaceFnsInput),
4093
4121
  cwd: workspacePath,
4094
4122
  logger,
4123
+ execLabel: pushExecLabel,
4095
4124
  });
4125
+ if (pushOutcome.status === 'failed') {
4126
+ logger.error(`Failed to ${pushExecLabel}: ${pushOutcome.detail}`);
4127
+ }
4096
4128
  }
4097
4129
  }
4098
4130
  finally {
@@ -4357,14 +4389,11 @@ const contextStatus = ['toDo', 'branchPushed', 'finished'];
4357
4389
  const defaultGitCommitMessageFn = ({ context }) => {
4358
4390
  return `LUMP:${context.name}`;
4359
4391
  };
4360
- const defaultGitPushCommandFn = (input) => {
4361
- return `git push origin ${shellSingleQuote(input.branchName)}`;
4362
- };
4363
- const defaultGitAddCommandFn = () => {
4364
- return `git add .`;
4392
+ const defaultGitAddCommitFn = (input) => {
4393
+ return success(`git add . && git commit --allow-empty -m ${shellSingleQuote(input.commitMessage)}`);
4365
4394
  };
4366
- const defaultGitCommitCommandFn = (input) => {
4367
- return `git commit --allow-empty -m ${shellSingleQuote(input.commitMessage)}`;
4395
+ const defaultGitPushFn = (input) => {
4396
+ return success(`git push origin ${shellSingleQuote(input.branchName)}`);
4368
4397
  };
4369
4398
  const defaultSetupWorkspaceFn = async (input) => {
4370
4399
  const { baseBranch, branchName } = input;
@@ -4402,7 +4431,7 @@ const defaultSetupWorkspaceFnWithWorktree = async (input) => {
4402
4431
  };
4403
4432
 
4404
4433
  async function runLump(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;
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;
4406
4435
  const lumpVariables = (lumpVariablesInput ?? {});
4407
4436
  const logger = loggerInput ?? createConsoleLogger({});
4408
4437
  const contextListToDoResult = await getToDoContextList({
@@ -4437,10 +4466,9 @@ async function runLump(input) {
4437
4466
  branchFn: branchFn,
4438
4467
  lumpVariables: lumpVariables,
4439
4468
  contextList: nextContextsForBranchList,
4440
- gitAddCommandFn,
4441
- gitCommitCommandFn,
4469
+ gitAddCommitFn,
4442
4470
  gitCommitMessageFn,
4443
- gitPushCommandFn,
4471
+ gitPushFn,
4444
4472
  projectRoot,
4445
4473
  steps,
4446
4474
  setupFn,
@@ -4459,5 +4487,5 @@ async function runLump(input) {
4459
4487
  });
4460
4488
  }
4461
4489
 
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 };
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 };
4463
4491
  //# sourceMappingURL=index.js.map