Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Breaking] Allow aborting exec, remove execTask #590

Merged
merged 23 commits into from
Oct 21, 2024
Merged

[Breaking] Allow aborting exec, remove execTask #590

merged 23 commits into from
Oct 21, 2024

Conversation

niik
Copy link
Member

@niik niik commented Oct 21, 2024

As of Node 20 execFile supports an AbortSignal as a means to cancel (i.e. kill) the process. Once the AbortSignal fires Node will take care of sending the specified killSignal (defaults to SIGTERM) to the process unless the process has already run to completion in which case the abort is a noop.

This lets us do away with our custom execTask implementation in favor of relying on Node to do the heavy lifting. In order for callers to be able to detect an abort all errors thrown from Dugite's exec will now be of type ExecError and callers will be able to use the code property to determine the cause of the error.

Example:

GitProcess.exec(
  ['clone', '--progress', 'https://github.com/desktop/dugite'],
  '/tmp/git',
  {
    signal: AbortSignal.timeout(10 * 1000),
  }
)
  .then(() => console.log('Clone completed!'))
  .catch(e =>
    e instanceof ExecError && e.code === 'ABORT_ERR'
      ? console.error('The clone timed out')
      : console.error(e)
  )

This also removes the custom error codes RepositoryDoesNotExistErrorCode and GitNotFoundErrorCode. These were an attempt to provide friendly error codes for implementors but assumed that the callers cared about the distinction between the Git executable missing and the cwd missing. Instead of synchronously stat'ing the path on ENOENT when it's possible that the caller doesn't care we'll leave it up to callers to choose whether to try to detect why the exec failed.

Note that the pathExist approach was subject to race conditions and that there's no guarantee you'd ever be able to figure out exactly why the call failed. Here's an example of mimicking RepositoryDoesNotExistErrorCode and GitNotFoundErrorCode:

const { access } = require('fs/promises')

const pathExist = path =>
  access(path).then(() => true).catch(() => false)

GitProcess.exec(['--version'], '/tmp/donutexist').catch(e =>
  e instanceof ExecError && e.code === 'ENOENT'
    ? pathExist('/tmp/donutexist').then(exists =>
        Promise.reject(new Error(exists ? 'Git not found' : 'Path not found'))
      )
    : reject(e)
)

This also removes the custom message when hitting maxBuffer limits. This message is already decent enough straight from Node.

Copy link
Member

@sergiou87 sergiou87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!! This is so much more idiomatic 🥹 👏 :shipit:

@niik niik merged commit 0cad3e4 into main Oct 21, 2024
6 checks passed
@niik niik deleted the abortable-exec branch October 21, 2024 14:10
@niik niik mentioned this pull request Oct 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants