Skip to content

Commit

Permalink
Merge pull request #85 from desktop/use-alternate-git-environment
Browse files Browse the repository at this point in the history
support for executing an external Git environment
  • Loading branch information
shiftkey authored Apr 19, 2017
2 parents 0706647 + 0d02be3 commit ba963dd
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 80 deletions.
96 changes: 96 additions & 0 deletions lib/git-environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import * as path from 'path'

/**
* Find the path to the embedded Git environment
*/
function resolveGitDir(): string {
if (process.env.LOCAL_GIT_DIRECTORY) {
return path.resolve(process.env.LOCAL_GIT_DIRECTORY)
} else {
return path.join(__dirname, '..', '..', 'git')
}
}

/**
* Find the path to the embedded Git binary
*/
function resolveGitBinary(): string {
const gitDir = resolveGitDir()
if (process.platform === 'darwin' || process.platform === 'linux') {
return path.join(gitDir, 'bin', 'git')
} else if (process.platform === 'win32') {
return path.join(gitDir, 'cmd', 'git.exe')
}

throw new Error('Git not supported on platform: ' + process.platform)
}

/**
* Find the path to the embedded git exec path.
*/
function resolveGitExecPath(): string {
const gitDir = resolveGitDir()
if (process.platform === 'darwin' || process.platform === 'linux') {
return path.join(gitDir, 'libexec', 'git-core')
} else if (process.platform === 'win32') {
return path.join(gitDir, 'mingw64', 'libexec', 'git-core')
}

throw new Error('Git not supported on platform: ' + process.platform)
}

/**
* Setup the process environment before invoking Git.
*
* This method resolves the Git executable and creates the array of key-value
* pairs which should be used as environment variables.
*
* @param additional options to include with the process
*/
export function setupEnvironment(environmentVariables: Object): { env: Object, gitLocation: string } {
const gitLocation = resolveGitBinary()

let envPath: string = process.env.PATH || ''
const gitDir = resolveGitDir()

if (process.platform === 'win32') {
envPath = `${gitDir}\\mingw64\\bin;${envPath}`
}

const env = Object.assign({}, process.env, {
GIT_EXEC_PATH: resolveGitExecPath(),
PATH: envPath,
}, environmentVariables)

if (process.platform === 'win32') {
// while reading the environment variable is case-insensitive
// you can create a hash with multiple values, which means the
// wrong value might be used when spawning the child process
//
// this ensures we only ever supply one value for PATH
if (env.Path) {
delete env.Path
}
}

if (process.platform === 'darwin' || process.platform === 'linux') {
// templates are used to populate your .git folder
// when a repository is initialized locally
const templateDir = `${gitDir}/share/git-core/templates`
env.GIT_TEMPLATE_DIR = templateDir
}

if (process.platform === 'linux') {
// when building Git for Linux and then running it from
// an arbitrary location, you should set PREFIX for the
// process to ensure that it knows how to resolve things
env.PREFIX = gitDir

// bypass whatever certificates might be set and use
// the bundle included in the distibution
const sslCABundle = `${gitDir}/ssl/cacert.pem`
env.GIT_SSL_CAINFO = sslCABundle
}

return { env, gitLocation }
}
85 changes: 7 additions & 78 deletions lib/git-process.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import * as path from 'path'

import * as fs from 'fs'

import { execFile, ExecOptionsWithStringEncoding } from 'child_process'
import { GitError, GitErrorRegexes, RepositoryDoesNotExistErrorCode, GitNotFoundErrorCode } from './errors'
import { ChildProcess } from 'child_process'

import { setupEnvironment } from './git-environment'

/** The result of shelling out to git. */
export interface IGitResult {
/** The standard output from git. */
Expand Down Expand Up @@ -70,42 +72,6 @@ interface ErrorWithCode extends Error {
}

export class GitProcess {
/**
* Find the path to the embedded Git environment
*/
private static resolveGitDir(): string {
if (process.env.TEST_WITH_LOCAL_GIT) {
return path.join(__dirname, '..', 'git')
} else {
return path.join(__dirname, '..', '..', 'git')
}
}

/**
* Find the path to the embedded Git binary
*/
private static resolveGitBinary(): string {
const gitDir = GitProcess.resolveGitDir()
if (process.platform === 'darwin' || process.platform === 'linux') {
return path.join(gitDir, 'bin', 'git')
} else if (process.platform === 'win32') {
return path.join(gitDir, 'cmd', 'git.exe')
}

throw new Error('Git not supported on platform: ' + process.platform)
}

/** Find the path to the embedded git exec path. */
private static resolveGitExecPath(): string {
const gitDir = GitProcess.resolveGitDir()
if (process.platform === 'darwin' || process.platform === 'linux') {
return path.join(gitDir, 'libexec', 'git-core')
} else if (process.platform === 'win32') {
return path.join(gitDir, 'mingw64', 'libexec', 'git-core')
}

throw new Error('Git not supported on platform: ' + process.platform)
}

private static pathExists(path: string): Boolean {
try {
Expand All @@ -128,49 +94,12 @@ export class GitProcess {
*/
public static exec(args: string[], path: string, options?: IGitExecutionOptions): Promise<IGitResult> {
return new Promise<IGitResult>(function(resolve, reject) {
const gitLocation = GitProcess.resolveGitBinary()

let envPath: string = process.env.PATH || ''
const gitDir = GitProcess.resolveGitDir()

if (process.platform === 'win32') {
envPath = `${gitDir}\\mingw64\\bin;${envPath}`
}

const env = Object.assign({}, process.env, {
GIT_EXEC_PATH: GitProcess.resolveGitExecPath(),
PATH: envPath,
}, options ? options.env : { })

if (process.platform === 'win32') {
// while reading the environment variable is case-insensitive
// you can create a hash with multiple values, which means the
// wrong value might be used when spawning the child process
//
// this ensures we only ever supply one value for PATH
if (env.Path) {
delete env.Path
}
}

if (process.platform === 'darwin' || process.platform === 'linux') {
// templates are used to populate your .git folder
// when a repository is initialized locally
const templateDir = `${gitDir}/share/git-core/templates`
env.GIT_TEMPLATE_DIR = templateDir
let customEnv = { }
if (options && options.env) {
customEnv = options.env
}

if (process.platform === 'linux') {
// when building Git for Linux and then running it from
// an arbitrary location, you should set PREFIX for the
// process to ensure that it knows how to resolve things
env.PREFIX = gitDir

// bypass whatever certificates might be set and use
// the bundle included in the distibution
const sslCABundle = `${gitDir}/ssl/cacert.pem`
env.GIT_SSL_CAINFO = sslCABundle
}
const { env, gitLocation } = setupEnvironment(customEnv)

// Explicitly annotate opts since typescript is unable to infer the correct
// signature for execFile when options is passed as an opaque hash. The type
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"build": "npm run clean && npm run lint && tsc -p ./tsconfig.json && tsc -p ./examples/tsconfig.json",
"prepublish": "npm run build && npm run test",
"test": "npm run test:fast && npm run test:slow",
"test:fast": "cross-env TEST_WITH_LOCAL_GIT=1 mocha --require ts-node/register test/fast/*.ts test/auth/*.ts",
"test:slow": "cross-env TEST_WITH_LOCAL_GIT=1 mocha -t 10000ms --require ts-node/register test/slow/*.ts test/auth/*.ts",
"test:fast": "cross-env LOCAL_GIT_DIRECTORY=./git/ mocha --require ts-node/register test/fast/*.ts test/auth/*.ts",
"test:slow": "cross-env LOCAL_GIT_DIRECTORY=./git/ mocha -t 10000ms --require ts-node/register test/slow/*.ts test/auth/*.ts",
"postinstall": "node ./script/download-git.js"
},
"engines": {
Expand Down

0 comments on commit ba963dd

Please sign in to comment.