2019-12-03 16:28:59 +01:00
import * as core from '@actions/core'
import * as fsHelper from './fs-helper'
2020-03-02 17:33:30 +01:00
import * as gitAuthHelper from './git-auth-helper'
2019-12-03 16:28:59 +01:00
import * as gitCommandManager from './git-command-manager'
2020-03-02 17:33:30 +01:00
import * as gitDirectoryHelper from './git-directory-helper'
2019-12-12 19:16:16 +01:00
import * as githubApiHelper from './github-api-helper'
2019-12-03 16:28:59 +01:00
import * as io from '@actions/io'
import * as path from 'path'
import * as refHelper from './ref-helper'
2019-12-12 19:16:16 +01:00
import * as stateHelper from './state-helper'
2020-03-25 20:12:22 +01:00
import * as urlHelper from './url-helper'
2019-12-03 16:28:59 +01:00
import { IGitCommandManager } from './git-command-manager'
2020-03-02 17:33:30 +01:00
import { IGitSourceSettings } from './git-source-settings'
2019-12-03 16:28:59 +01:00
2020-03-02 17:33:30 +01:00
export async function getSource ( settings : IGitSourceSettings ) : Promise < void > {
2019-12-12 19:16:16 +01:00
// Repository URL
2019-12-03 16:28:59 +01:00
core . info (
` Syncing repository: ${ settings . repositoryOwner } / ${ settings . repositoryName } `
)
2020-03-25 20:12:22 +01:00
const repositoryUrl = urlHelper . getFetchUrl ( settings )
2019-12-03 16:28:59 +01:00
// Remove conflicting file path
if ( fsHelper . fileExistsSync ( settings . repositoryPath ) ) {
await io . rmRF ( settings . repositoryPath )
}
// Create directory
let isExisting = true
if ( ! fsHelper . directoryExistsSync ( settings . repositoryPath ) ) {
isExisting = false
await io . mkdirP ( settings . repositoryPath )
}
// Git command manager
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Getting Git version info' )
2019-12-12 19:16:16 +01:00
const git = await getGitCommandManager ( settings )
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2019-12-03 16:28:59 +01:00
2019-12-12 19:16:16 +01:00
// Prepare existing directory, otherwise recreate
if ( isExisting ) {
2020-03-02 17:33:30 +01:00
await gitDirectoryHelper . prepareExistingDirectory (
2019-12-03 16:28:59 +01:00
git ,
settings . repositoryPath ,
repositoryUrl ,
2020-05-27 15:54:28 +02:00
settings . clean ,
settings . ref
2019-12-03 16:28:59 +01:00
)
}
2019-12-12 19:16:16 +01:00
if ( ! git ) {
// Downloading using REST API
core . info ( ` The repository will be downloaded using the GitHub REST API ` )
core . info (
` To create a local Git repository instead, add Git ${ gitCommandManager . MinimumGitVersion } or higher to the PATH `
)
2020-03-12 16:42:38 +01:00
if ( settings . submodules ) {
throw new Error (
` Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${ gitCommandManager . MinimumGitVersion } or higher to the PATH. `
)
} else if ( settings . sshKey ) {
throw new Error (
` Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${ gitCommandManager . MinimumGitVersion } or higher to the PATH. `
)
}
2019-12-12 19:16:16 +01:00
await githubApiHelper . downloadRepository (
2019-12-12 19:49:26 +01:00
settings . authToken ,
2019-12-12 19:16:16 +01:00
settings . repositoryOwner ,
settings . repositoryName ,
settings . ref ,
settings . commit ,
settings . repositoryPath
)
2020-03-05 20:21:59 +01:00
return
}
2019-12-03 16:28:59 +01:00
2020-03-05 20:21:59 +01:00
// Save state for POST action
stateHelper . setRepositoryPath ( settings . repositoryPath )
2019-12-03 16:28:59 +01:00
2020-03-05 20:21:59 +01:00
// Initialize the repository
if (
! fsHelper . directoryExistsSync ( path . join ( settings . repositoryPath , '.git' ) )
) {
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Initializing the repository' )
2020-03-05 20:21:59 +01:00
await git . init ( )
await git . remoteAdd ( 'origin' , repositoryUrl )
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2020-03-05 20:21:59 +01:00
}
2019-12-12 19:16:16 +01:00
2020-03-05 20:21:59 +01:00
// Disable automatic garbage collection
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Disabling automatic garbage collection' )
2020-03-05 20:21:59 +01:00
if ( ! ( await git . tryDisableAutomaticGarbageCollection ( ) ) ) {
core . warning (
` Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay. `
)
}
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2019-12-03 16:28:59 +01:00
2020-03-05 20:21:59 +01:00
const authHelper = gitAuthHelper . createAuthHelper ( git , settings )
try {
// Configure auth
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Setting up auth' )
2020-03-05 20:21:59 +01:00
await authHelper . configureAuth ( )
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2019-12-03 16:28:59 +01:00
2020-03-05 20:21:59 +01:00
// LFS install
if ( settings . lfs ) {
await git . lfsInstall ( )
}
// Fetch
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Fetching the repository' )
2020-05-27 15:54:28 +02:00
if ( settings . fetchDepth <= 0 ) {
// Fetch all branches and tags
let refSpec = refHelper . getRefSpecForAllHistory (
settings . ref ,
settings . commit
)
await git . fetch ( refSpec )
// When all history is fetched, the ref we're interested in may have moved to a different
// commit (push or force push). If so, fetch again with a targeted refspec.
if ( ! ( await refHelper . testRef ( git , settings . ref , settings . commit ) ) ) {
refSpec = refHelper . getRefSpec ( settings . ref , settings . commit )
await git . fetch ( refSpec )
}
} else {
const refSpec = refHelper . getRefSpec ( settings . ref , settings . commit )
await git . fetch ( refSpec , settings . fetchDepth )
}
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2020-03-05 20:21:59 +01:00
// Checkout info
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Determining the checkout info' )
2020-03-05 20:21:59 +01:00
const checkoutInfo = await refHelper . getCheckoutInfo (
git ,
settings . ref ,
settings . commit
)
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2020-03-05 20:21:59 +01:00
// LFS fetch
// Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time).
// Explicit lfs fetch will fetch lfs objects in parallel.
if ( settings . lfs ) {
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Fetching LFS objects' )
2020-03-05 20:21:59 +01:00
await git . lfsFetch ( checkoutInfo . startPoint || checkoutInfo . ref )
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2020-03-05 20:21:59 +01:00
}
2019-12-03 16:28:59 +01:00
2020-03-05 20:21:59 +01:00
// Checkout
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Checking out the ref' )
2020-03-05 20:21:59 +01:00
await git . checkout ( checkoutInfo . ref , checkoutInfo . startPoint )
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2020-03-05 20:21:59 +01:00
// Submodules
if ( settings . submodules ) {
try {
// Temporarily override global config
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Setting up auth for fetching submodules' )
2020-03-05 20:21:59 +01:00
await authHelper . configureGlobalAuth ( )
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2020-03-05 20:21:59 +01:00
// Checkout submodules
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Fetching submodules' )
2020-03-05 20:21:59 +01:00
await git . submoduleSync ( settings . nestedSubmodules )
await git . submoduleUpdate (
settings . fetchDepth ,
settings . nestedSubmodules
)
await git . submoduleForeach (
'git config --local gc.auto 0' ,
settings . nestedSubmodules
)
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2020-03-05 20:21:59 +01:00
// Persist credentials
if ( settings . persistCredentials ) {
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Persisting credentials for submodules' )
2020-03-05 20:21:59 +01:00
await authHelper . configureSubmoduleAuth ( )
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2020-03-05 20:21:59 +01:00
}
} finally {
// Remove temporary global config override
await authHelper . removeGlobalAuth ( )
2019-12-12 19:49:26 +01:00
}
}
2020-03-05 20:21:59 +01:00
// Dump some info about the checked out commit
2020-05-21 17:09:16 +02:00
const commitInfo = await git . log1 ( )
// Check for incorrect pull request merge commit
await refHelper . checkCommitInfo (
settings . authToken ,
commitInfo ,
settings . repositoryOwner ,
settings . repositoryName ,
settings . ref ,
settings . commit
)
2020-03-05 20:21:59 +01:00
} finally {
// Remove auth
if ( ! settings . persistCredentials ) {
2020-03-27 18:12:15 +01:00
core . startGroup ( 'Removing auth' )
2020-03-05 20:21:59 +01:00
await authHelper . removeAuth ( )
2020-03-27 18:12:15 +01:00
core . endGroup ( )
2020-03-05 20:21:59 +01:00
}
2019-12-12 19:16:16 +01:00
}
2019-12-03 16:28:59 +01:00
}
export async function cleanup ( repositoryPath : string ) : Promise < void > {
// Repo exists?
2020-01-27 16:21:50 +01:00
if (
! repositoryPath ||
! fsHelper . fileExistsSync ( path . join ( repositoryPath , '.git' , 'config' ) )
) {
2019-12-03 16:28:59 +01:00
return
}
2020-01-27 16:21:50 +01:00
let git : IGitCommandManager
try {
2020-03-02 17:33:30 +01:00
git = await gitCommandManager . createCommandManager ( repositoryPath , false )
2020-01-27 16:21:50 +01:00
} catch {
return
}
2020-03-02 17:33:30 +01:00
// Remove auth
const authHelper = gitAuthHelper . createAuthHelper ( git )
await authHelper . removeAuth ( )
2019-12-03 16:28:59 +01:00
}
2019-12-12 19:16:16 +01:00
async function getGitCommandManager (
2020-03-02 17:33:30 +01:00
settings : IGitSourceSettings
) : Promise < IGitCommandManager | undefined > {
2019-12-12 19:16:16 +01:00
core . info ( ` Working directory is ' ${ settings . repositoryPath } ' ` )
try {
2020-03-02 17:33:30 +01:00
return await gitCommandManager . createCommandManager (
2019-12-12 19:16:16 +01:00
settings . repositoryPath ,
settings . lfs
)
} catch ( err ) {
// Git is required for LFS
if ( settings . lfs ) {
throw err
}
// Otherwise fallback to REST API
2020-03-02 17:33:30 +01:00
return undefined
2019-12-03 16:28:59 +01:00
}
}