Skip to content

Commit 75ded06

Browse files
authored
Merge pull request #19 from NativeScript/tnikolov/find-git
Find the path to the git executable on different platforms.
2 parents 8800c00 + b2c4d63 commit 75ded06

File tree

6 files changed

+103
-17
lines changed

6 files changed

+103
-17
lines changed

lib/helpers.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { platform } from "os";
1+
import { HostInfo } from "./host-info";
22

33
export class Helpers {
4+
constructor(private hostInfo: HostInfo) { }
5+
46
public getPropertyName(method: Function): string {
57
if (method) {
68
let match = method.toString().match(/(?:return\s+?.*\.(.+);)|(?:=>\s*?.*\.(.+)\b)/);
@@ -17,7 +19,7 @@ export class Helpers {
1719
return value;
1820
}
1921

20-
return (platform() === "win32") ? this.cmdQuote(value) : this.bashQuote(value);
22+
return this.hostInfo.isWindows ? this.cmdQuote(value) : this.bashQuote(value);
2123
}
2224

2325
private bashQuote(s: string): string {

lib/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const childProcess = new ChildProcess();
1414
const winReg = new WinReg();
1515
const hostInfo = new HostInfo(winReg);
1616
const fileSystem = new FileSystem();
17-
const helpers = new Helpers();
17+
const helpers = new Helpers(hostInfo);
1818
const androidToolsInfo = new AndroidToolsInfo(childProcess, fileSystem, hostInfo);
1919

2020
const sysInfo: NativeScriptDoctor.ISysInfo = new SysInfo(childProcess, fileSystem, helpers, hostInfo, winReg, androidToolsInfo);

lib/sys-info.ts

+62-3
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ export class SysInfo implements NativeScriptDoctor.ISysInfo {
193193

194194
public async isAndroidSdkConfiguredCorrectly(): Promise<boolean> {
195195
return this.getValueForProperty(() => this.isAndroidSdkConfiguredCorrectlyCache, async (): Promise<boolean> => {
196-
const output = await this.childProcess.spawnFromEvent(this.androidToolsInfo.getPathToEmulatorExecutable(), ['-help'], "close", { ignoreError: true });
196+
const output = await this.childProcess.spawnFromEvent(this.androidToolsInfo.getPathToEmulatorExecutable(), ["-help"], "close", { ignoreError: true });
197197

198198
return output && output.stdout.indexOf("usage: emulator") >= 0;
199199
});
@@ -209,10 +209,14 @@ export class SysInfo implements NativeScriptDoctor.ISysInfo {
209209

210210
public getGitVersion(): Promise<string> {
211211
return this.getValueForProperty(() => this.gitVerCache, async (): Promise<string> => {
212-
const output = await this.execCommand("git --version");
212+
const gitPath = await this.getGitPath();
213+
if (!gitPath) {
214+
return null;
215+
}
216+
217+
const output = await this.execCommand(`${this.helpers.quoteString(gitPath)} --version`);
213218
const matches = SysInfo.GIT_VERSION_REGEXP.exec(output);
214219
return matches && matches[1];
215-
216220
});
217221
}
218222

@@ -340,6 +344,61 @@ export class SysInfo implements NativeScriptDoctor.ISysInfo {
340344
this.shouldCache = shouldCache;
341345
}
342346

347+
public getGitPath(): Promise<string> {
348+
return this.hostInfo.isWindows ? this.findGitWin32() : this.findGitUnix();
349+
}
350+
351+
private async findGitWin32(): Promise<string> {
352+
let result: string;
353+
const win32Paths = [process.env["ProgramFiles"], process.env["ProgramFiles(x86)"]];
354+
for (const win32Path of win32Paths) {
355+
result = this.findSystemGitWin32(win32Path);
356+
if (result) {
357+
return result;
358+
}
359+
}
360+
361+
result = this.findGitHubGitWin32();
362+
return result ? result : await this.findGitCore("where");
363+
}
364+
365+
private findSystemGitWin32(base: string): string {
366+
if (!base) {
367+
return null;
368+
}
369+
370+
return this.findSpecificGit(path.join(base, "Git", "cmd", "git.exe"));
371+
}
372+
373+
private findGitHubGitWin32(): string {
374+
const github = path.join(process.env["LOCALAPPDATA"], "GitHub");
375+
if (!this.fileSystem.exists(github)) {
376+
return null;
377+
}
378+
379+
const children = this.fileSystem.readDirectory(github);
380+
const git = children.filter(child => /^PortableGit/.test(child))[0];
381+
if (!this.fileSystem.exists(git)) {
382+
return null;
383+
}
384+
385+
return this.findSpecificGit(path.join(github, git, "cmd", "git.exe"));
386+
}
387+
388+
private findSpecificGit(gitPath: string): string {
389+
return this.fileSystem.exists(gitPath) ? gitPath : null;
390+
}
391+
392+
private async findGitUnix(): Promise<string> {
393+
return await this.findGitCore("which");
394+
}
395+
396+
private async findGitCore(command: string, options?: any): Promise<string> {
397+
const result = await this.execCommand(`${command} git`);
398+
399+
return result && result.split("\n")[0].trim();
400+
}
401+
343402
private async getValueForProperty<T>(property: Function, getValueMethod: () => Promise<T>): Promise<T> {
344403
if (this.shouldCache) {
345404
const propertyName = this.helpers.getPropertyName(property);

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nativescript-doctor",
3-
"version": "0.4.1",
3+
"version": "0.5.0",
44
"description": "Library that helps identifying if the environment can be used for development of {N} apps.",
55
"main": "lib/index.js",
66
"types": "./typings/nativescript-doctor.d.ts",

test/sys-info.ts

+29-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { ChildProcess } from "../lib/wrappers/child-process";
66

77
const JavaHomeName = "JAVA_HOME";
88
const AndroidHomeName = "ANDROID_HOME";
9-
const helpers = new Helpers();
9+
const PROGRAM_FILES = "ProgramFiles";
10+
const PROGRAM_FILES_ENV_PATH = "C:\\Program Files";
1011

1112
interface IChildProcessResultDescription {
1213
result?: any;
@@ -29,6 +30,7 @@ interface IChildProcessResults {
2930
podVersion: IChildProcessResultDescription;
3031
pod: IChildProcessResultDescription;
3132
nativeScriptCliVersion: IChildProcessResultDescription;
33+
git: IChildProcessResultDescription;
3234
}
3335

3436
interface IHostInfoMockOptions {
@@ -73,16 +75,19 @@ function createChildProcessResults(childProcessResult: IChildProcessResults): ID
7375
"xcodebuild -version": childProcessResult.xCodeVersion,
7476
"pod --version": childProcessResult.podVersion,
7577
"pod": childProcessResult.pod,
76-
'adb': childProcessResult.adbVersion,
77-
'adb version': childProcessResult.adbVersion,
78+
"adb": childProcessResult.adbVersion,
79+
"adb version": childProcessResult.adbVersion,
7880
"'adb' version": childProcessResult.adbVersion, // for Mac and Linux
79-
'android': childProcessResult.androidInstalled,
80-
'android.bat': childProcessResult.androidInstalled, // for Windows
81+
"android": childProcessResult.androidInstalled,
82+
"android.bat": childProcessResult.androidInstalled, // for Windows
8183
"mono --version": childProcessResult.monoVersion,
82-
"git --version": childProcessResult.gitVersion,
84+
"'git' --version": childProcessResult.gitVersion, // for Mac and Linux
85+
'"C:\\Program Files\\Git\\cmd\\git.exe" --version': childProcessResult.gitVersion, // for Windows
86+
'"C:\\Program Files/Git/cmd/git.exe" --version': childProcessResult.gitVersion, // When running Windows test on the Non-Windows platform
8387
"gradle -v": childProcessResult.gradleVersion,
8488
"tns --version": childProcessResult.nativeScriptCliVersion,
85-
"emulator": { shouldThrowError: false }
89+
"emulator": { shouldThrowError: false },
90+
"which git": childProcessResult.git
8691
};
8792
}
8893

@@ -128,9 +133,11 @@ function mockSysInfo(childProcessResult: IChildProcessResults, hostInfoOptions?:
128133

129134
const fileSystem: any = {
130135
exists: () => Promise.resolve((fileSystemOptions || {}).existsResult),
131-
extractZip: () => Promise.resolve()
136+
extractZip: () => Promise.resolve(),
137+
readDirectory: () => Promise.resolve([])
132138
};
133139

140+
const helpers = new Helpers(hostInfo);
134141
return new SysInfo(childProcess, fileSystem, helpers, hostInfo, winreg, androidToolsInfo);
135142
}
136143

@@ -169,6 +176,7 @@ describe("SysInfo unit tests", () => {
169176
}
170177
};
171178

179+
const helpers = new Helpers(null);
172180
sysInfo = new SysInfo(childProcess, null, helpers, null, null, androidToolsInfo);
173181
});
174182

@@ -221,7 +229,8 @@ describe("SysInfo unit tests", () => {
221229
gitVersion: { result: setStdOut("git version 1.9.5") },
222230
podVersion: { result: setStdOut("0.38.2") },
223231
pod: { result: setStdOut("success") },
224-
nativeScriptCliVersion: { result: setStdOut("2.5.0") }
232+
nativeScriptCliVersion: { result: setStdOut("2.5.0") },
233+
git: { result: setStdOut("git") }
225234
};
226235

227236
delete process.env[JavaHomeName];
@@ -253,8 +262,11 @@ describe("SysInfo unit tests", () => {
253262
});
254263

255264
it("on Windows", async () => {
265+
const originalProgramFiles = process.env[PROGRAM_FILES];
266+
process.env[PROGRAM_FILES] = PROGRAM_FILES_ENV_PATH;
256267
sysInfo = mockSysInfo(childProcessResult, { isWindows: true, isDarwin: false, dotNetVersion: "4.5.1" });
257268
let result = await sysInfo.getSysInfo();
269+
process.env[PROGRAM_FILES] = originalProgramFiles;
258270
assertCommonValues(result);
259271
assert.deepEqual(result.xcodeVer, null);
260272
assert.deepEqual(result.cocoaPodsVer, null);
@@ -287,8 +299,11 @@ describe("SysInfo unit tests", () => {
287299
});
288300

289301
it("is null when OS is not Mac", async () => {
302+
const originalProgramFiles = process.env[PROGRAM_FILES];
303+
process.env[PROGRAM_FILES] = PROGRAM_FILES_ENV_PATH;
290304
sysInfo = mockSysInfo(childProcessResult, { isWindows: true, isDarwin: false, dotNetVersion: "4.5.1" });
291305
let result = await sysInfo.getSysInfo();
306+
process.env[PROGRAM_FILES] = originalProgramFiles;
292307
assert.deepEqual(result.cocoaPodsVer, null);
293308
});
294309

@@ -324,7 +339,8 @@ describe("SysInfo unit tests", () => {
324339
gitVersion: { shouldThrowError: true },
325340
podVersion: { shouldThrowError: true },
326341
pod: { shouldThrowError: true },
327-
nativeScriptCliVersion: { shouldThrowError: true }
342+
nativeScriptCliVersion: { shouldThrowError: true },
343+
git: { shouldThrowError: false }
328344
};
329345
androidToolsInfo.validateAndroidHomeEnvVariable = (): any[] => [1];
330346
});
@@ -346,7 +362,10 @@ describe("SysInfo unit tests", () => {
346362
};
347363

348364
it("on Windows", async () => {
365+
const originalProgramFiles = process.env[PROGRAM_FILES];
366+
process.env[PROGRAM_FILES] = PROGRAM_FILES_ENV_PATH;
349367
sysInfo = mockSysInfo(childProcessResult, { isWindows: true, isDarwin: false, dotNetVersion: "4.5.1" });
368+
process.env[PROGRAM_FILES] = originalProgramFiles;
350369
await assertAllValuesAreNull();
351370
});
352371

typings/interfaces.ts

+6
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ declare module NativeScriptDoctor {
8181
*/
8282
getGitVersion(): Promise<string>;
8383

84+
/**
85+
* Returns the path to the currently installed Git.
86+
* @return {Promise<string>} Returns the path to the currently installed Git. It will return null if Git is not installed.
87+
*/
88+
getGitPath(): Promise<string>;
89+
8490
/**
8591
* Returns the currently installed Gradle version.
8692
* @return {Promise<string>} Returns the currently installed Gradle version. It will return null if Gradle is not installed.

0 commit comments

Comments
 (0)