Skip to content

Commit 4259b26

Browse files
authored
Create interface for retrieving git version information (#850)
* Create interface for retrieving git version information
1 parent 19029fc commit 4259b26

File tree

10 files changed

+170
-11
lines changed

10 files changed

+170
-11
lines changed

.changeset/silly-actors-peel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'simple-git': minor
3+
---
4+
5+
Add `.version` to return git version information, including whether the git binary is installed.

examples/git-version.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## Check if git is installed
2+
3+
To check if `git` (or the `customBinary` of your choosing) is accessible, use the
4+
`git.version()` api:
5+
6+
```typescript
7+
import { simpleGit } from 'simple-git';
8+
9+
const {installed} = await simpleGit().version();
10+
if (!installed) {
11+
throw new Error(`Exit: "git" not available.`);
12+
}
13+
14+
// ... continue using git commands here
15+
```
16+
17+
## Check for a specific version of git
18+
19+
Using the `git.version()` interface, you can query for the current `git` version
20+
information split by `major`, `minor` and `patch`:
21+
22+
```typescript
23+
import { simpleGit } from 'simple-git';
24+
import { lt } from 'semver';
25+
26+
const versionResult = await simpleGit().version();
27+
if (lt(String(versionResult), '2.1.0')) {
28+
throw new Error(`Exit: "git" must be at least version 2.1.0.`);
29+
}
30+
31+
// ... continue using git commands here compatible with 2.1.0 or higher
32+
```

simple-git/readme.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,9 +387,13 @@ For type details of the response for each of the tasks, please see the [TypeScri
387387

388388
## git stash
389389

390-
- `.stash([ options ])` Stash the working directory, optional first argument can be an array of string arguments or [options](#how-to-specify-options) object to pass to the [git stash](https://git-scm.com/docs/git-stash) command.
390+
- `.stash([ options ])` Stash the working directory, optional first argument can be an array of string arguments or [options](#how-to-specify-options) object to pass to the [git stash](https://git-scm.com/docs/git-stash) command.
391391

392-
- `.stashList([ options ])` Retrieves the stash list, optional first argument can be an object in the same format as used in [git log](#git-log).
392+
- `.stashList([ options ])` Retrieves the stash list, optional first argument can be an object in the same format as used in [git log](#git-log).
393+
394+
## git version [examples](https://github.com/steveukx/git-js/blob/main/examples/git-version.md)
395+
396+
- `.version()` retrieve the major, minor and patch for the currently installed `git`. Use the `.installed` property of the result to determine whether `git` is accessible on the path.
393397

394398
## changing the working directory [examples](https://github.com/steveukx/git-js/blob/main/examples/git-change-working-directory.md)
395399

simple-git/src/lib/simple-git-api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { mergeTask } from './tasks/merge';
1111
import { pushTask } from './tasks/push';
1212
import { statusTask } from './tasks/status';
1313
import { configurationErrorTask, straightThroughStringTask } from './tasks/task';
14+
import version from './tasks/version';
1415
import { outputHandler, SimpleGitExecutor, SimpleGitTask, SimpleGitTaskCallback } from './types';
1516
import {
1617
asArray,
@@ -136,4 +137,4 @@ export class SimpleGitApi implements SimpleGitBase {
136137
}
137138
}
138139

139-
Object.assign(SimpleGitApi.prototype, commit(), config(), grep(), log());
140+
Object.assign(SimpleGitApi.prototype, commit(), config(), grep(), log(), version());

simple-git/src/lib/tasks/config.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { ConfigGetResult, ConfigListSummary, SimpleGit } from '../../../typings';
1+
import type { ConfigGetResult, ConfigListSummary, SimpleGit } from '../../../typings';
22
import { configGetParser, configListParser } from '../responses/ConfigList';
3-
import { SimpleGitApi } from '../simple-git-api';
4-
import { StringTask } from '../types';
3+
import type { SimpleGitApi } from '../simple-git-api';
4+
import type { StringTask } from '../types';
55
import { trailingFunctionArgument } from '../utils';
66

77
export enum GitConfigScope {

simple-git/src/lib/tasks/version.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import type { SimpleGitApi } from '../simple-git-api';
2+
import type { SimpleGit } from '../../../typings';
3+
import { asNumber, ExitCodes } from '../utils';
4+
5+
export interface VersionResult {
6+
major: number;
7+
minor: number;
8+
patch: number;
9+
agent: string;
10+
installed: boolean;
11+
}
12+
13+
const NOT_INSTALLED = 'installed=false';
14+
15+
function versionResponse(
16+
major = 0,
17+
minor = 0,
18+
patch = 0,
19+
agent = '',
20+
installed = true
21+
): VersionResult {
22+
return Object.defineProperty(
23+
{
24+
major,
25+
minor,
26+
patch,
27+
agent,
28+
installed,
29+
},
30+
'toString',
31+
{
32+
value() {
33+
return `${major}.${minor}.${patch}`;
34+
},
35+
configurable: false,
36+
enumerable: false,
37+
}
38+
);
39+
}
40+
41+
function notInstalledResponse() {
42+
return versionResponse(0, 0, 0, '', false);
43+
}
44+
45+
export default function (): Pick<SimpleGit, 'version'> {
46+
return {
47+
version(this: SimpleGitApi) {
48+
return this._runTask({
49+
commands: ['--version'],
50+
format: 'utf-8',
51+
parser(stdOut) {
52+
if (stdOut === NOT_INSTALLED) {
53+
return notInstalledResponse();
54+
}
55+
56+
const version = /version (\d+)\.(\d+)\.(\d+)(?:\s*\((.+)\))?/.exec(stdOut);
57+
58+
if (!version) {
59+
return versionResponse(0, 0, 0, stdOut);
60+
}
61+
62+
return versionResponse(
63+
asNumber(version[1]),
64+
asNumber(version[2]),
65+
asNumber(version[3]),
66+
version[4] || ''
67+
);
68+
},
69+
onError(result, error, done, fail) {
70+
if (result.exitCode === ExitCodes.NOT_FOUND) {
71+
return done(Buffer.from(NOT_INSTALLED));
72+
}
73+
74+
fail(error);
75+
},
76+
});
77+
},
78+
};
79+
}

simple-git/src/lib/utils/exit-codes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
export enum ExitCodes {
66
SUCCESS,
77
ERROR,
8+
NOT_FOUND = -2,
89
UNCLEAN = 128,
910
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { createTestContext, newSimpleGit, SimpleGitTestContext } from '@simple-git/test-utils';
2+
3+
describe('version', () => {
4+
let context: SimpleGitTestContext;
5+
6+
beforeEach(async () => (context = await createTestContext()));
7+
8+
it('gets the current version', async () => {
9+
const git = newSimpleGit(context.root);
10+
expect(await git.version()).toEqual({
11+
major: 2,
12+
minor: expect.any(Number),
13+
patch: expect.any(Number),
14+
agent: expect.any(String),
15+
installed: true,
16+
});
17+
});
18+
19+
it('gets the current version when the binary is not installed', async () => {
20+
const git = newSimpleGit(context.root).customBinary('bad');
21+
expect(await git.version()).toEqual({
22+
major: 0,
23+
minor: 0,
24+
patch: 0,
25+
agent: '',
26+
installed: false,
27+
});
28+
});
29+
});

simple-git/typings/simple-git.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,4 +995,11 @@ export interface SimpleGit extends SimpleGitBase {
995995
* Updates repository server info
996996
*/
997997
updateServerInfo(callback?: types.SimpleGitTaskCallback<string>): Response<string>;
998+
999+
/**
1000+
* Retrieves `git` version information, including whether `git` is installed on the `PATH`
1001+
*/
1002+
version(
1003+
callback?: types.SimpleGitTaskCallback<types.VersionResult>
1004+
): Response<types.VersionResult>;
9981005
}

simple-git/typings/types.d.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
export { RemoteWithoutRefs, RemoteWithRefs } from '../src/lib/responses/GetRemoteSummary';
2-
export { LogOptions, DefaultLogFields } from '../src/lib/tasks/log';
1+
export type { RemoteWithoutRefs, RemoteWithRefs } from '../src/lib/responses/GetRemoteSummary';
2+
export type { LogOptions, DefaultLogFields } from '../src/lib/tasks/log';
33

4-
export {
4+
export type {
55
outputHandler,
66
Options,
77
TaskOptions,
@@ -10,10 +10,11 @@ export {
1010
SimpleGitTaskCallback,
1111
} from '../src/lib/types';
1212

13-
export { ApplyOptions } from '../src/lib/tasks/apply-patch';
13+
export type { ApplyOptions } from '../src/lib/tasks/apply-patch';
1414
export { CheckRepoActions } from '../src/lib/tasks/check-is-repo';
1515
export { CleanOptions, CleanMode } from '../src/lib/tasks/clean';
16-
export { CloneOptions } from '../src/lib/tasks/clone';
16+
export type { CloneOptions } from '../src/lib/tasks/clone';
1717
export { GitConfigScope } from '../src/lib/tasks/config';
1818
export { GitGrepQuery, grepQueryBuilder } from '../src/lib/tasks/grep';
1919
export { ResetOptions, ResetMode } from '../src/lib/tasks/reset';
20+
export type { VersionResult } from '../src/lib/tasks/version';

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy