file copyright author Vivek M. Chawla <@VivekMChawla>
 *  summary description version license import * as path from 'path' ; import { ShellString } from 'shelljs' ; import { SfdxFalconDebug } from '../../modules/sfdx-falcon-debug' ; import { SfdxFalconError } from '../../modules/sfdx-falcon-error' ; import { waitASecond } from '../../modules/sfdx-falcon-util/async' ; import { ShellExecResult } from '../../modules/sfdx-falcon-types' ; const shell = require ( 'shelljs' ) ; const repoNameRegEx = /\/(\w|-)+\.git\/*$/ ; const gitUriRegEx = /(^(git|ssh|http(s)?)|(git@[\w\.]+))(:(\/\/)?)([\w\.@\:\/\-~]+)(\.git)(\/)?$/ ; const dbgNs = 'UTILITY:git:' ; shell . config . fatal = true ; function param { string } gitRemoteUri param { string } targetDirectory param { string } repoDirectory returns { void } description public export async function gitClone ( gitRemoteUri , targetDirectory = '.' , repoDirectory = '' ) { SfdxFalconDebug . obj ( `${ dbgNs }gitClone:arguments:` , arguments , `arguments: ` ) ; if ( typeof gitRemoteUri !== 'string' ) { throw new SfdxFalconError ( `Expected string for gitRemoteUri but got '${ typeof gitRemoteUri }' instead.` , 'TypeError' , `${ dbgNs }gitClone` ) ; } if ( typeof targetDirectory !== 'string' ) { throw new SfdxFalconError ( `Expected string for targetDirectory but got '${ typeof targetDirectory }' instead.` , 'TypeError' , `${ dbgNs }gitClone` ) ; } if ( targetDirectory . trim ( ) === '' ) { throw new SfdxFalconError ( `Must provide a non-empty string for targetDirectory` , 'InvalidParameter' , `${ dbgNs }gitClone` ) ; } SfdxFalconDebug . str ( `${ dbgNs }gitClone:targetDirectory:` , targetDirectory , `targetDirectory (unresolved target directory): ` ) ; SfdxFalconDebug . str ( `${ dbgNs }gitClone:normalizedTargetDir:` , path . normalize ( targetDirectory ) , `targetDirectory (normalized target directory): ` ) ; targetDirectory = path . resolve ( path . normalize ( targetDirectory ) ) ; SfdxFalconDebug . str ( `${ dbgNs }gitClone:targetDirectory:` , targetDirectory , `targetDirectory (resolved target directory): ` ) ; SfdxFalconDebug . obj ( `${ dbgNs }gitClone:parsedTargetDir:` , path . parse ( targetDirectory ) , `PARSED targetDirectory: ` ) ; return new Promise ( ( resolve , reject ) => { try { shell . cd ( targetDirectory ) ; shell . pwd ( ) ; } catch ( cdError ) { SfdxFalconDebug . obj ( `${ dbgNs }gitClone:cdError:` , cdError , `cdError: ` ) ; try { shell . mkdir ( '-p' , targetDirectory ) ; } catch ( mkdirError ) { SfdxFalconDebug . obj ( `${ dbgNs }gitClone:mkdirError:` , mkdirError , `mkdirError: ` ) ; throw new SfdxFalconError ( `Could not create directory '${ targetDirectory }'` , 'InvalidTargetDirectory' , `${ dbgNs }gitClone` , mkdirError ) ; } try { shell . cd ( targetDirectory ) ; } catch ( cdError2 ) { SfdxFalconDebug . obj ( `${ dbgNs }gitClone:cdError2:` , cdError2 , `cdError2: ` ) ; throw new SfdxFalconError ( `Target directory '${ targetDirectory }' not found or is not accessible` , 'NoTargetDirectory' , `${ dbgNs }gitClone` , cdError2 ) ; } } SfdxFalconDebug . str ( `${ dbgNs }gitClone:` , `shell.exec('git clone ${ gitRemoteUri } ${ repoDirectory }', {silent: true})` , `Shell Command: ` ) ; shell . exec ( `git clone ${ gitRemoteUri } ${ repoDirectory }` , { silent : true } , ( code , stdout , stderr ) => { const shellExecResult = { code : code , stdout : stdout , stderr : stderr , message : '' , resolve : false } as ShellExecResult ; switch ( code ) { case 0 : shellExecResult . message = 'Repository cloned successfully' ; shellExecResult . resolve = true ; break ; case 128 : shellExecResult . message = `Destination path '${ repoDirectory }' already exists and is not an empty directory.` ; shellExecResult . resolve = false ; break ; default : shellExecResult . message = `Unknown error cloning ${ gitRemoteUri } to ${ path . join ( targetDirectory , repoDirectory ) }` ; shellExecResult . resolve = false ; } SfdxFalconDebug . obj ( `${ dbgNs }gitClone:shellExecResult:` , shellExecResult , `shellExecResult: ` ) ; if ( shellExecResult . resolve ) { resolve ( shellExecResult ) ; } else { reject ( shellExecResult ) ; } } ) ; } ) ; } function param { string } targetDirectory returns { ShellString } description public export function gitInit ( targetDirectory ) { SfdxFalconDebug . obj ( `${ dbgNs }gitInit:arguments:` , arguments , `arguments: ` ) ; if ( typeof targetDirectory !== 'string' || targetDirectory === '' ) { throw new SfdxFalconError ( `Expected non-empty string for targetDirectory but got '${ typeof targetDirectory }' instead` , 'TypeError' , `${ dbgNs }gitInit` ) ; } shell . cd ( targetDirectory ) ; return shell . exec ( `git init` , { silent : true } ) as ShellString ; } function param { string } targetDirectory returns { ShellString } description public export function gitAdd ( targetDirectory ) { SfdxFalconDebug . obj ( `${ dbgNs }gitAdd:arguments:` , arguments , `arguments: ` ) ; if ( typeof targetDirectory !== 'string' || targetDirectory === '' ) { throw new SfdxFalconError ( `Expected non-empty string for targetDirectory but got '${ typeof targetDirectory }'` , 'TypeError' , `${ dbgNs }gitAdd` ) ; } shell . config . fatal = true ; shell . cd ( targetDirectory ) ; return shell . exec ( `git add -A` , { silent : true } ) ; } function param { string } targetDirectory param { string } commitMessage returns { void } description public export function gitAddAndCommit ( targetDirectory , commitMessage ) { SfdxFalconDebug . obj ( `${ dbgNs }gitAddAndCommit:arguments:` , arguments , `arguments: ` ) ; if ( typeof targetDirectory !== 'string' || targetDirectory === '' ) { throw new SfdxFalconError ( `Expected non-empty string for targetDirectory but got '${ typeof targetDirectory }'` , 'TypeError' , `${ dbgNs }gitAddAndCommit` ) ; } if ( typeof commitMessage !== 'string' || commitMessage === '' ) { throw new SfdxFalconError ( `Expected non-empty string for commitMessage but got '${ typeof commitMessage }'` , 'TypeError' , `${ dbgNs }gitAddAndCommit` ) ; } shell . config . fatal = true ; shell . cd ( targetDirectory ) ; shell . exec ( `git add -A` , { silent : true } ) ; shell . exec ( `git commit -m "${ commitMessage }"` , { silent : true } ) ; return ; } function param { string } targetDirectory param { string } commitMessage returns { ShellString } description public export function gitCommit ( targetDirectory , commitMessage ) { SfdxFalconDebug . obj ( `${ dbgNs }gitCommit:arguments:` , arguments , `arguments: ` ) ; if ( typeof targetDirectory !== 'string' || targetDirectory === '' ) { throw new SfdxFalconError ( `Expected non-empty string for targetDirectory but got '${ typeof targetDirectory }'` , 'TypeError' , `${ dbgNs }gitCommit` ) ; } if ( typeof commitMessage !== 'string' || commitMessage === '' ) { throw new SfdxFalconError ( `Expected non-empty string for commitMessage but got '${ typeof commitMessage }'` , 'TypeError' , `${ dbgNs }gitCommit` ) ; } shell . config . fatal = true ; shell . cd ( targetDirectory ) ; return shell . exec ( `git commit -m "${ commitMessage }"` , { silent : true } ) ; } function param { string } targetDirectory param { string } gitRemoteUri returns { ShellString } description public export function gitRemoteAddOrigin ( targetDirectory , gitRemoteUri ) { SfdxFalconDebug . obj ( `${ dbgNs }gitRemoteAddOrigin:arguments:` , arguments , `arguments: ` ) ; if ( typeof targetDirectory !== 'string' || targetDirectory === '' ) { throw new SfdxFalconError ( `Expected non-empty string for targetDirectory but got '${ typeof targetDirectory }'` , 'TypeError' , `${ dbgNs }gitRemoteAddOrigin` ) ; } if ( typeof gitRemoteUri !== 'string' || gitRemoteUri === '' ) { throw new SfdxFalconError ( `Expected string for gitRemoteUri but got '${ typeof gitRemoteUri }'` , 'TypeError' , `${ dbgNs }gitRemoteAddOrigin` ) ; } shell . config . fatal = true ; shell . cd ( targetDirectory ) ; return shell . exec ( `git remote add origin ${ gitRemoteUri }` , { silent : true } ) ; } function returns { boolean } description public export function isGitInstalled ( ) { try { if ( shell . which ( 'git' ) ) { return true ; } else { return false ; } } catch ( err ) { SfdxFalconDebug . obj ( `${ dbgNs }gitRemoteAddOrigin:err:` , err , `err: ` ) ; return false ; } } function param { string } gitRemoteUri returns { string } description public export function getRepoNameFromUri ( gitRemoteUri ) { SfdxFalconDebug . obj ( `${ dbgNs }getRepoNameFromUri:arguments:` , arguments , `arguments: ` ) ; if ( typeof gitRemoteUri !== 'string' ) { throw new SfdxFalconError ( `Expected string for gitRemoteUri but got '${ typeof gitRemoteUri }'` , 'TypeError' , `${ dbgNs }getRepoNameFromUri` ) ; } let repoName = repoNameRegEx . exec ( gitRemoteUri ) [ 0 ] ; repoName = repoName . substring ( 0 , repoName . lastIndexOf ( '.' ) ) ; repoName = repoName . substr ( 1 ) ; if ( repoName === '' ) { throw new SfdxFalconError ( `Repository name could not be parsed from the Git Remote URI.` , 'UnreadableRepoName' , `${ dbgNs }getRepoNameFromUri` ) ; } SfdxFalconDebug . str ( `${ dbgNs }getRepoNameFromUri:repoName:` , repoName , `repoName: ` ) ; return repoName ; } function param { string } gitRemoteUri returns { boolean } description public export function isGitRemoteEmpty ( gitRemoteUri ) { SfdxFalconDebug . obj ( `${ dbgNs }isGitRemoteEmpty:arguments:` , arguments , `arguments: ` ) ; if ( typeof gitRemoteUri !== 'string' ) { throw new SfdxFalconError ( `Expected string for gitRemoteUri but got '${ typeof gitRemoteUri }'` , 'TypeError' , `${ dbgNs }isGitRemoteEmpty` ) ; } try { shell . exec ( `git ls-remote --exit-code -h ${ gitRemoteUri }` , { silent : true } ) ; } catch ( err ) { SfdxFalconDebug . obj ( `${ dbgNs }gitRemoteAddOrigin:err:` , err , `err: ` ) ; return false ; } return true ; } function param { string } gitRemoteUri param { number } waitSecs returns { Promise < ShellExecResult > } description public async export async function checkGitRemoteStatus ( gitRemoteUri , waitSeconds = 0 ) { SfdxFalconDebug . obj ( `${ dbgNs }checkGitRemoteStatus:arguments:` , arguments , `arguments: ` ) ; if ( typeof gitRemoteUri !== 'string' ) { throw new SfdxFalconError ( `Expected string for gitRemoteUri but got '${ typeof gitRemoteUri }'` , 'TypeError' , `${ dbgNs }checkGitRemoteStatus` ) ; } if ( isNaN ( waitSeconds ) ) { throw new SfdxFalconError ( `Expected number for waitSeconds but got '${ typeof waitSeconds }'` , 'TypeError' , `${ dbgNs }checkGitRemoteStatus` ) ; } if ( waitSeconds > 0 ) { await waitASecond ( waitSeconds ) ; } return new Promise ( ( resolve , reject ) => { shell . exec ( `git ls-remote --exit-code -h ${ gitRemoteUri }` , { silent : true } , ( code , stdout , stderr ) => { const shellExecResult = { code : code , stdout : stdout , stderr : stderr , message : '' , resolve : false } as ShellExecResult ; switch ( code ) { case 0 : shellExecResult . message = 'Remote repository found' ; shellExecResult . resolve = true ; break ; case 2 : shellExecResult . message = 'Remote repository contains no commits' ; shellExecResult . resolve = false ; break ; case 128 : shellExecResult . message = 'Remote repository not found' ; shellExecResult . resolve = false ; break ; default : shellExecResult . message = 'Unexpected Error' ; shellExecResult . resolve = false ; } SfdxFalconDebug . obj ( `${ dbgNs }checkGitRemoteStatus:shellExecResult:` , shellExecResult , `shellExecResult: ` ) ; if ( shellExecResult . resolve ) { resolve ( shellExecResult ) ; } else { reject ( shellExecResult ) ; } } ) ; } ) ; } function param { string } gitRemoteUri returns { boolean } description public export function isGitRemoteReadable ( gitRemoteUri ) { SfdxFalconDebug . obj ( `${ dbgNs }isGitRemoteReadable:arguments:` , arguments , `arguments: ` ) ; if ( typeof gitRemoteUri !== 'string' ) { throw new SfdxFalconError ( `Expected string for gitRemoteUri but got '${ typeof gitRemoteUri }'` , 'TypeError' , `${ dbgNs }isGitRemoteReadable` ) ; } try { shell . exec ( `git ls-remote -h ${ gitRemoteUri }` , { silent : true } ) ; } catch ( err ) { SfdxFalconDebug . obj ( `${ dbgNs }isGitRemoteReadable:err:` , err , `err: ` ) ; return false ; } return true ; } function param { string } gitRemoteUri param { RegExp } acceptedProtocols returns { boolean } description public export function isGitUriValid ( gitRemoteUri , acceptedProtocols ? ) { SfdxFalconDebug . obj ( `${ dbgNs }isGitRemoteReadable:arguments:` , arguments , `arguments: ` ) ; if ( typeof gitRemoteUri !== 'string' ) { throw new SfdxFalconError ( `Expected string for gitRemoteUri but got '${ typeof gitRemoteUri }'` , 'TypeError' , `${ dbgNs }isGitUriValid` ) ; } if ( gitUriRegEx . test ( gitRemoteUri ) ) { if ( acceptedProtocols ) { return ( acceptedProtocols . test ( gitRemoteUri ) ) ; } else { return true ; } } return false ; }