Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
- [Overview](./overview.md) - introduces how the repository and scripts work
- [Setup](./setup.md) - what you need to get involved with contributing to `dugite`
- API
- [`GitProcess.exec`](api/exec.md)
- [`GitProcess.spawn`](api/spawn.md)
- [`GitProcess.parseError`](api/parseError.md)
- [`exec`](api/exec.md)
- [`spawn`](api/spawn.md)
- [`parseError`](api/parseError.md)
- [Releases](./releases.md) - details about how releases are made
56 changes: 48 additions & 8 deletions docs/api/exec.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,48 @@
# `GitProcess.exec`
# `exec`

This is the easiest and safest way to work with Git. It provides a
Promise-based API that wraps the standard output, standard error and exit code
from the process, and also provides some error handling for common scenarios.

```ts
GitProcess.exec(args: string[], path: string, options?: IGitExecutionOptions):Promise<IGitResult>
exec(args: string[], path: string, options?: IGitExecutionOptions): Promise<IGitResult>
```

- `args` - an array of arguments that should be passed to `git` when executing
the process
- `path` - the directory to execute the command in
- `options` - additional parameters to pass
- `env`: a collection of key-value pairs which are assigned to the created
process
- `env`: a `Record<string, string | undefined>` of key-value pairs which are assigned to the created process
- `stdin`: a string or `Buffer` which is written to the created process
- `stdinEncoding`: if you need to set the encoding of the input
- `processCallback`: a callback to inject additional code which will be
invoked after the child process is spawned
- `encoding`: control output encoding (`BufferEncoding` or `'buffer'`)
- `signal`: an `AbortSignal` for cancellation support
- `killSignal`: custom signal to use when killing the process
- `maxBuffer`: maximum buffer size (defaults to `Infinity`)

`IGitResult` has these fields:

- `stdout` - a string representing the standard process output from invoking Git
- `stderr` - a string representing the standard error process output from invoking Git
- `stdout` - the standard process output from invoking Git (string or Buffer depending on `encoding`)
- `stderr` - the standard error process output from invoking Git (string or Buffer depending on `encoding`)
- `exitCode` - zero if the process completed without error, non-zero indicates an error

For typed results, use `IGitStringResult` (when `encoding` is a string encoding) or `IGitBufferResult` (when `encoding: 'buffer'`).

On execution failures, an `ExecError` is thrown which includes `stdout`, `stderr`, and additional properties like `code`, `signal`, and `killed`.

## Example

```ts
import { exec, IGitExecutionOptions } from 'dugite'

const path = 'C:/path/to/repo/'

const options: IGitExecutionOptions = {
// enable diagnostics
env: {
'GIT_HTTP_USER_AGENT': 'dugite/2.12.0',
'GIT_HTTP_USER_AGENT': 'dugite/3.0.0',
'GIT_TRACE': '1',
'GIT_CURL_VERBOSE': '1'
},
Expand All @@ -44,6 +53,37 @@ const options: IGitExecutionOptions = {
}
}

const result = await GitProcess.exec([ 'pull', 'origin' ], path, options)
const result = await exec([ 'pull', 'origin' ], path, options)
```

## Cancellation

Use `AbortController` to cancel long-running Git operations:

```ts
import { exec } from 'dugite'

const controller = new AbortController()

const resultPromise = exec(['clone', 'https://github.com/example/repo'], '/path/to/dir', {
signal: controller.signal,
})

// Cancel if needed
controller.abort()

try {
const result = await resultPromise
} catch (error) {
// Handle cancellation
}
```

You can also use `AbortSignal.timeout()` for automatic cancellation:

```ts
const result = await exec(['clone', 'https://github.com/example/repo'], '/path/to/dir', {
signal: AbortSignal.timeout(5000), // Auto-cancel after 5 seconds
})
```

8 changes: 5 additions & 3 deletions docs/api/parseError.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `GitProcess.parseError`
# `parseError`

Parsing error messages from Git is an essential part of detecting errors raised
by Git. `dugite` comes with a collection of known errors, and you can
Expand All @@ -8,9 +8,11 @@ error messages.
For example:

```ts
const result = await GitProcess.exec([ 'pull', 'origin', branch ], path, options)
import { exec, parseError, GitError } from 'dugite'

const result = await exec([ 'pull', 'origin', branch ], path, options)
if (result.exitCode !== 0) {
const error = GitProcess.parseError(result.stderr)
const error = parseError(result.stderr)
if (error) {
if (error === GitError.HTTPSAuthenticationFailed) {
// invalid credentials
Expand Down
15 changes: 8 additions & 7 deletions docs/api/spawn.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
# `GitProcess.spawn`
# `spawn`

This method exposes the raw child process for callers to manipulate directly.
This function exposes the raw child process for callers to manipulate directly.
Because of this, it is the responsibility of the caller to ensure that the
child process handles success and error scenarios in their application.

```ts
GitProcess.spawn(args: string[], path: string, options?: IGitSpawnExecutionOptions): ChildProcess
spawn(args: string[], path: string, options?: IGitSpawnOptions): ChildProcess
```

- `args` - an array of arguments that should be passed to `git` when executing
the process
- `path` - the directory to execute the command in
- `options` - additional parameters to pass
- `env`: a collection of key-value pairs which are assigned to the created
process
- `env`: a `Record<string, string | undefined>` of key-value pairs which are assigned to the created process

## Example

This example mimics the Promise-based API of `GitProcess.exec` with some simple handling for the exit code:
This example mimics the Promise-based API of `exec` with some simple handling for the exit code:

```ts
import { spawn } from 'dugite'

return new Promise<string>((resolve, reject) => {
const process = GitProcess.spawn([ 'status', '--porcelain=v2', '--untracked-files=all' ], directory)
const process = spawn([ 'status', '--porcelain=v2', '--untracked-files=all' ], directory)
const output = new Array<Buffer>()
process.stdout.on('data', (chunk) => {
if (chunk instanceof Buffer) {
Expand Down
2 changes: 1 addition & 1 deletion docs/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

There are some ways you can change the behaviour of `dugite` by providing
environment variables. These are grouped into two categories - when you
install `dugite` into a package, and when you spawn Git using `GitProcess`.
install `dugite` into a package, and when you spawn Git using `exec` or `spawn`.

## Installation

Expand Down
13 changes: 8 additions & 5 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

`dugite` is a wrapper on top of the Git command line interface, with some helpers to make it easy to consume in your Node applications. The important parts:

- `GitProcess` - the core of the library - `GitProcess.exec` is how you
interact with Git
- `exec` - the core function of the library - how you interact with Git
- `spawn` - exposes the raw child process for callers to manipulate directly
- `parseError` - parse error messages from Git to detect known errors
- `IGitResult` - the abstraction for a result returned by Git - contains
exit code and standard output/error text
exit code and standard output/error text (which can be `string` or `Buffer`)
- `IGitExecutionOptions` - additional overrides to change the behaviour
of `GitProcess` (see [API extensibility](./api-extensibility.md) for
more information)
of `exec`
- `IGitSpawnOptions` - additional overrides to change the behaviour
of `spawn`
- `GitError` - a collection of known error codes that `dugite` can understand
- `ExecError` - error class thrown by `exec` on execution failures, includes stdout/stderr
57 changes: 9 additions & 48 deletions docs/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,19 @@

## Update Git

The most important part of the release process is updating the embedded Git package. This can be done using this one-liner:
Run the [update-git.yml workflow](https://github.com/desktop/dugite/actions/workflows/update-git.yml) to update the embedded Git version.

```sh
yarn update-embedded-git
```
This workflow will:

This script:
- retrieve the latest `dugite-native` release from the GitHub API
- get the checksums embedded in the release
- generate the `script/embedded-git.json` payload to be used at install time
- open a pull request with the dugite-native upgrade changes.

- retrieves the latest `dugite-native` release from the GitHub API
- gets the checksums embedded in the release
- generates the `script/embedded-git.json` payload to be used at install time

### Note

If you don't want the latest dugite-native release for some reason, you can edit the release URL in `script/update-embedded-git.js` to point to a different GitHub release URL.

```js
const url = `https://api.github.com/repos/desktop/dugite-native/releases/23544533`
```
You must then approve and merge the pull request before continuing to the release process.

## Release/Publishing

Before running the commands in 'Publishing to NPM',
create a new release branch of the form `releases/x.x.x`

After running commands in 'Publishing to NPM', the release branch should be pushed. Now, you need to get it reviewed and merged.

After that, don't forget publish the release on the repo.

- Go to https://github.com/desktop/dugite/releases
- Click click `Draft a New Release`
- Fill in form
- Hit `Publish release`

## Publishing to NPM

Releases are done to NPM, and are currently limited to the core team.

```sh
# to ensure everything is up-to-date
yarn

yarn build

# if you have not run `yarn build` before, a couple of you cloning auth test will fail.
yarn test

# you might need to do a different sort of version bump here
npm version minor
Run the [publish.yml workflow](https://github.com/desktop/dugite/actions/workflows/publish.yml) to publish a new release. The workflow will take care of bumping the version number, publishing the package to NPM, and creating a GitHub release.

# this will also run the test suite and fail if any errors found
# this will also run `git push --follow-tags` at the end
# remember to `npm login`
npm publish
```
Releasing with version 'minor' is typically the way to go (it'll bump from x.y.z to x.(y+1).0), but you can also choose 'patch' (x.y.(z+1)) or 'major' ((x+1).0.0) if you need to.
2 changes: 1 addition & 1 deletion docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This is what you need to install:

- [NodeJS](https://nodejs.org) v12 or higher
- [NodeJS](https://nodejs.org) v20 or higher

Then open a shell and clone the repository to your local machine.

Expand Down