2019-12-03 18:28:59 +03:00
import * as core from '@actions/core'
import * as fsHelper from './fs-helper'
2020-03-02 19:33:30 +03:00
import * as gitAuthHelper from './git-auth-helper'
2019-12-03 18:28:59 +03:00
import * as gitCommandManager from './git-command-manager'
2020-03-02 19:33:30 +03:00
import * as gitDirectoryHelper from './git-directory-helper'
2019-12-12 21:16:16 +03:00
import * as githubApiHelper from './github-api-helper'
2019-12-03 18:28:59 +03:00
import * as io from '@actions/io'
import * as path from 'path'
import * as refHelper from './ref-helper'
2019-12-12 21:16:16 +03:00
import * as stateHelper from './state-helper'
2020-03-25 22:12:22 +03:00
import * as urlHelper from './url-helper'
2024-03-14 17:40:14 +03:00
import {
MinimumGitSparseCheckoutVersion ,
IGitCommandManager
} from './git-command-manager'
2020-03-02 19:33:30 +03:00
import { IGitSourceSettings } from './git-source-settings'
2019-12-03 18:28:59 +03:00
2020-03-02 19:33:30 +03:00
export async function getSource ( settings : IGitSourceSettings ) : Promise < void > {
2019-12-12 21:16:16 +03:00
// Repository URL
2019-12-03 18:28:59 +03:00
core . info (
` Syncing repository: ${ settings . repositoryOwner } / ${ settings . repositoryName } `
)
2020-03-25 22:12:22 +03:00
const repositoryUrl = urlHelper . getFetchUrl ( settings )
2019-12-03 18:28:59 +03: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 20:12:15 +03:00
core . startGroup ( 'Getting Git version info' )
2019-12-12 21:16:16 +03:00
const git = await getGitCommandManager ( settings )
2020-03-27 20:12:15 +03:00
core . endGroup ( )
2019-12-03 18:28:59 +03:00
2022-04-14 21:13:20 +03:00
let authHelper : gitAuthHelper.IGitAuthHelper | null = null
try {
if ( git ) {
authHelper = gitAuthHelper . createAuthHelper ( git , settings )
2022-04-21 04:37:43 +03:00
if ( settings . setSafeDirectory ) {
// Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail
// Otherwise all git commands we run in a container fail
await authHelper . configureTempGlobalConfig ( )
core . info (
` Adding repository directory to the temporary git global config as a safe directory `
)
await git
. config ( 'safe.directory' , settings . repositoryPath , true , true )
. catch ( error = > {
core . info (
` Failed to initialize safe directory with error: ${ error } `
)
} )
stateHelper . setSafeDirectory ( )
}
2022-04-14 21:13:20 +03:00
}
2019-12-03 18:28:59 +03:00
2022-04-14 21:13:20 +03:00
// Prepare existing directory, otherwise recreate
if ( isExisting ) {
await gitDirectoryHelper . prepareExistingDirectory (
git ,
settings . repositoryPath ,
repositoryUrl ,
settings . clean ,
settings . ref
2020-03-12 18:42:38 +03:00
)
2022-04-14 21:13:20 +03: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 18:42:38 +03:00
)
2022-04-14 21:13:20 +03: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. `
)
}
await githubApiHelper . downloadRepository (
settings . authToken ,
settings . repositoryOwner ,
settings . repositoryName ,
settings . ref ,
settings . commit ,
2022-09-26 19:34:52 +03:00
settings . repositoryPath ,
settings . githubServerUrl
2022-04-14 21:13:20 +03:00
)
return
2020-03-12 18:42:38 +03:00
}
2022-04-14 21:13:20 +03:00
// Save state for POST action
stateHelper . setRepositoryPath ( settings . repositoryPath )
2019-12-03 18:28:59 +03:00
2022-04-14 21:13:20 +03:00
// Initialize the repository
if (
! fsHelper . directoryExistsSync ( path . join ( settings . repositoryPath , '.git' ) )
) {
core . startGroup ( 'Initializing the repository' )
await git . init ( )
await git . remoteAdd ( 'origin' , repositoryUrl )
core . endGroup ( )
}
2019-12-03 18:28:59 +03:00
2022-04-14 21:13:20 +03:00
// Disable automatic garbage collection
core . startGroup ( 'Disabling automatic garbage collection' )
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 20:12:15 +03:00
core . endGroup ( )
2019-12-12 21:16:16 +03:00
2022-04-14 21:13:20 +03:00
// If we didn't initialize it above, do it now
if ( ! authHelper ) {
authHelper = gitAuthHelper . createAuthHelper ( git , settings )
}
2020-03-05 22:21:59 +03:00
// Configure auth
2020-03-27 20:12:15 +03:00
core . startGroup ( 'Setting up auth' )
2020-03-05 22:21:59 +03:00
await authHelper . configureAuth ( )
2020-03-27 20:12:15 +03:00
core . endGroup ( )
2019-12-03 18:28:59 +03:00
2020-06-18 17:20:33 +03:00
// Determine the default branch
if ( ! settings . ref && ! settings . commit ) {
core . startGroup ( 'Determining the default branch' )
if ( settings . sshKey ) {
settings . ref = await git . getDefaultBranch ( repositoryUrl )
} else {
settings . ref = await githubApiHelper . getDefaultBranch (
settings . authToken ,
settings . repositoryOwner ,
2022-09-26 19:34:52 +03:00
settings . repositoryName ,
settings . githubServerUrl
2020-06-18 17:20:33 +03:00
)
}
core . endGroup ( )
}
2020-03-05 22:21:59 +03:00
// LFS install
if ( settings . lfs ) {
await git . lfsInstall ( )
}
// Fetch
2020-03-27 20:12:15 +03:00
core . startGroup ( 'Fetching the repository' )
2023-08-16 23:34:54 +03:00
const fetchOptions : {
filter? : string
fetchDepth? : number
fetchTags? : boolean
2023-09-01 21:19:18 +03:00
showProgress? : boolean
2023-08-16 23:34:54 +03:00
} = { }
2023-09-22 20:30:36 +03:00
if ( settings . filter ) {
fetchOptions . filter = settings . filter
} else if ( settings . sparseCheckout ) {
fetchOptions . filter = 'blob:none'
}
2020-05-27 16:54:28 +03:00
if ( settings . fetchDepth <= 0 ) {
// Fetch all branches and tags
let refSpec = refHelper . getRefSpecForAllHistory (
settings . ref ,
settings . commit
)
2023-06-09 16:08:21 +03:00
await git . fetch ( refSpec , fetchOptions )
2020-05-27 16:54:28 +03:00
// 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 )
2023-06-09 16:08:21 +03:00
await git . fetch ( refSpec , fetchOptions )
2020-05-27 16:54:28 +03:00
}
} else {
2023-06-09 16:08:21 +03:00
fetchOptions . fetchDepth = settings . fetchDepth
2023-08-16 23:34:54 +03:00
fetchOptions . fetchTags = settings . fetchTags
2020-05-27 16:54:28 +03:00
const refSpec = refHelper . getRefSpec ( settings . ref , settings . commit )
2023-06-09 16:08:21 +03:00
await git . fetch ( refSpec , fetchOptions )
2020-05-27 16:54:28 +03:00
}
2020-03-27 20:12:15 +03:00
core . endGroup ( )
2020-03-05 22:21:59 +03:00
// Checkout info
2020-03-27 20:12:15 +03:00
core . startGroup ( 'Determining the checkout info' )
2020-03-05 22:21:59 +03:00
const checkoutInfo = await refHelper . getCheckoutInfo (
git ,
settings . ref ,
settings . commit
)
2020-03-27 20:12:15 +03:00
core . endGroup ( )
2020-03-05 22:21:59 +03: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.
2023-06-09 16:08:21 +03:00
// For sparse checkouts, let `checkout` fetch the needed objects lazily.
if ( settings . lfs && ! settings . sparseCheckout ) {
2020-03-27 20:12:15 +03:00
core . startGroup ( 'Fetching LFS objects' )
2020-03-05 22:21:59 +03:00
await git . lfsFetch ( checkoutInfo . startPoint || checkoutInfo . ref )
2020-03-27 20:12:15 +03:00
core . endGroup ( )
2020-03-05 22:21:59 +03:00
}
2019-12-03 18:28:59 +03:00
2023-06-09 16:08:21 +03:00
// Sparse checkout
2024-02-21 15:56:19 +03:00
if ( ! settings . sparseCheckout ) {
2024-03-14 17:40:14 +03:00
let gitVersion = await git . version ( )
// no need to disable sparse-checkout if the installed git runtime doesn't even support it.
if ( gitVersion . checkMinimum ( MinimumGitSparseCheckoutVersion ) ) {
await git . disableSparseCheckout ( )
}
2024-02-21 15:56:19 +03:00
} else {
2023-06-09 16:08:21 +03:00
core . startGroup ( 'Setting up sparse checkout' )
if ( settings . sparseCheckoutConeMode ) {
await git . sparseCheckout ( settings . sparseCheckout )
} else {
await git . sparseCheckoutNonConeMode ( settings . sparseCheckout )
}
core . endGroup ( )
}
2020-03-05 22:21:59 +03:00
// Checkout
2020-03-27 20:12:15 +03:00
core . startGroup ( 'Checking out the ref' )
2020-03-05 22:21:59 +03:00
await git . checkout ( checkoutInfo . ref , checkoutInfo . startPoint )
2020-03-27 20:12:15 +03:00
core . endGroup ( )
2020-03-05 22:21:59 +03:00
// Submodules
if ( settings . submodules ) {
2022-04-14 21:13:20 +03:00
// Temporarily override global config
core . startGroup ( 'Setting up auth for fetching submodules' )
await authHelper . configureGlobalAuth ( )
core . endGroup ( )
2020-03-05 22:21:59 +03:00
2022-04-14 21:13:20 +03:00
// Checkout submodules
core . startGroup ( 'Fetching submodules' )
await git . submoduleSync ( settings . nestedSubmodules )
await git . submoduleUpdate ( settings . fetchDepth , settings . nestedSubmodules )
await git . submoduleForeach (
'git config --local gc.auto 0' ,
settings . nestedSubmodules
)
core . endGroup ( )
2020-03-05 22:21:59 +03:00
2022-04-14 21:13:20 +03:00
// Persist credentials
if ( settings . persistCredentials ) {
core . startGroup ( 'Persisting credentials for submodules' )
await authHelper . configureSubmoduleAuth ( )
core . endGroup ( )
2019-12-12 21:49:26 +03:00
}
}
2020-03-05 22:21:59 +03:00
2020-09-23 16:41:47 +03:00
// Get commit information
2020-05-21 18:09:16 +03:00
const commitInfo = await git . log1 ( )
2020-09-23 16:41:47 +03:00
// Log commit sha
await git . log1 ( "--format='%H'" )
2020-05-21 18:09:16 +03:00
// Check for incorrect pull request merge commit
await refHelper . checkCommitInfo (
settings . authToken ,
commitInfo ,
settings . repositoryOwner ,
settings . repositoryName ,
settings . ref ,
2022-09-26 19:34:52 +03:00
settings . commit ,
settings . githubServerUrl
2020-05-21 18:09:16 +03:00
)
2020-03-05 22:21:59 +03:00
} finally {
// Remove auth
2022-04-14 21:13:20 +03:00
if ( authHelper ) {
if ( ! settings . persistCredentials ) {
core . startGroup ( 'Removing auth' )
await authHelper . removeAuth ( )
core . endGroup ( )
}
authHelper . removeGlobalConfig ( )
2020-03-05 22:21:59 +03:00
}
2019-12-12 21:16:16 +03:00
}
2019-12-03 18:28:59 +03:00
}
export async function cleanup ( repositoryPath : string ) : Promise < void > {
// Repo exists?
2020-01-27 18:21:50 +03:00
if (
! repositoryPath ||
! fsHelper . fileExistsSync ( path . join ( repositoryPath , '.git' , 'config' ) )
) {
2019-12-03 18:28:59 +03:00
return
}
2020-01-27 18:21:50 +03:00
let git : IGitCommandManager
try {
2023-06-09 16:08:21 +03:00
git = await gitCommandManager . createCommandManager (
repositoryPath ,
false ,
false
)
2020-01-27 18:21:50 +03:00
} catch {
return
}
2020-03-02 19:33:30 +03:00
// Remove auth
const authHelper = gitAuthHelper . createAuthHelper ( git )
2022-04-14 21:13:20 +03:00
try {
2022-04-21 04:37:43 +03:00
if ( stateHelper . PostSetSafeDirectory ) {
// Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail
// Otherwise all git commands we run in a container fail
await authHelper . configureTempGlobalConfig ( )
core . info (
` Adding repository directory to the temporary git global config as a safe directory `
)
await git
. config ( 'safe.directory' , repositoryPath , true , true )
. catch ( error = > {
core . info ( ` Failed to initialize safe directory with error: ${ error } ` )
} )
}
2022-04-14 21:13:20 +03:00
await authHelper . removeAuth ( )
} finally {
await authHelper . removeGlobalConfig ( )
}
2019-12-03 18:28:59 +03:00
}
2019-12-12 21:16:16 +03:00
async function getGitCommandManager (
2020-03-02 19:33:30 +03:00
settings : IGitSourceSettings
) : Promise < IGitCommandManager | undefined > {
2019-12-12 21:16:16 +03:00
core . info ( ` Working directory is ' ${ settings . repositoryPath } ' ` )
try {
2020-03-02 19:33:30 +03:00
return await gitCommandManager . createCommandManager (
2019-12-12 21:16:16 +03:00
settings . repositoryPath ,
2023-06-09 16:08:21 +03:00
settings . lfs ,
settings . sparseCheckout != null
2019-12-12 21:16:16 +03:00
)
} catch ( err ) {
// Git is required for LFS
if ( settings . lfs ) {
throw err
}
// Otherwise fallback to REST API
2020-03-02 19:33:30 +03:00
return undefined
2019-12-03 18:28:59 +03:00
}
}