2023-05-25 18:22:24 +00:00
import * as core from '@actions/core'
2023-07-18 09:44:59 +00:00
import * as github from '@actions/github'
2023-05-25 18:22:24 +00:00
import { Env } from './env'
import { Inputs } from './inputs'
import {
canDiffCommits ,
2023-11-27 07:11:43 +00:00
cleanShaInput ,
2023-10-02 23:35:05 +00:00
getCurrentBranchName ,
2023-05-25 18:22:24 +00:00
getHeadSha ,
2023-05-25 23:39:26 +00:00
getParentSha ,
2023-05-25 18:22:24 +00:00
getPreviousGitTag ,
2023-06-01 16:03:09 +00:00
getRemoteBranchHeadSha ,
2023-05-25 18:22:24 +00:00
gitFetch ,
gitFetchSubmodules ,
gitLog ,
verifyCommitSha
} from './utils'
const getCurrentSHA = async ( {
inputs ,
workingDirectory
} : {
inputs : Inputs
workingDirectory : string
} ) : Promise < string > = > {
2023-11-27 07:11:43 +00:00
let currentSha = await cleanShaInput ( {
sha : inputs.sha ,
cwd : workingDirectory ,
token : inputs.token
} )
2023-05-25 18:22:24 +00:00
core . debug ( 'Getting current SHA...' )
if ( inputs . until ) {
core . debug ( ` Getting base SHA for ' ${ inputs . until } '... ` )
try {
currentSha = await gitLog ( {
cwd : workingDirectory ,
args : [
2023-05-25 23:16:16 +00:00
'--format=%H' ,
2023-05-25 18:22:24 +00:00
'-n' ,
'1' ,
'--date' ,
'local' ,
'--until' ,
inputs . until
]
} )
} catch ( error ) {
core . error (
` Invalid until date: ${ inputs . until } . ${ ( error as Error ) . message } `
)
throw error
}
} else {
if ( ! currentSha ) {
2023-06-06 12:00:56 +00:00
if (
2023-07-18 09:44:59 +00:00
github . context . payload . pull_request ? . head ? . sha &&
2023-06-06 12:00:56 +00:00
( await verifyCommitSha ( {
2023-07-18 09:44:59 +00:00
sha : github.context.payload.pull_request?.head?.sha ,
2023-06-06 12:00:56 +00:00
cwd : workingDirectory ,
showAsErrorMessage : false
} ) ) === 0
) {
2023-07-18 09:44:59 +00:00
currentSha = github . context . payload . pull_request ? . head ? . sha
2023-07-24 21:40:48 +00:00
} else if ( github . context . eventName === 'merge_group' ) {
currentSha = github . context . payload . merge_group ? . head_sha
2023-06-06 12:00:56 +00:00
} else {
currentSha = await getHeadSha ( { cwd : workingDirectory } )
}
2023-05-25 18:22:24 +00:00
}
}
await verifyCommitSha ( { sha : currentSha , cwd : workingDirectory } )
core . debug ( ` Current SHA: ${ currentSha } ` )
return currentSha
}
export interface DiffResult {
previousSha : string
currentSha : string
currentBranch : string
targetBranch : string
diff : string
2023-05-26 16:48:32 +00:00
initialCommit? : boolean
2023-05-25 18:22:24 +00:00
}
2023-07-24 21:40:48 +00:00
export const getSHAForNonPullRequestEvent = async (
2023-05-25 18:22:24 +00:00
inputs : Inputs ,
env : Env ,
workingDirectory : string ,
isShallow : boolean ,
hasSubmodule : boolean ,
2023-06-14 19:22:47 +00:00
gitFetchExtraArgs : string [ ] ,
2023-05-25 18:22:24 +00:00
isTag : boolean
) : Promise < DiffResult > = > {
2023-05-25 23:39:26 +00:00
let targetBranch = env . GITHUB_REF_NAME
2023-10-02 23:35:05 +00:00
let currentBranch = targetBranch
2023-05-25 18:22:24 +00:00
let initialCommit = false
2023-08-23 21:04:44 +00:00
if ( ! inputs . skipInitialFetch ) {
if ( isShallow ) {
core . info ( 'Repository is shallow, fetching more history...' )
if ( isTag ) {
let sourceBranch = ''
if ( github . context . payload . base_ref ) {
sourceBranch = github . context . payload . base_ref . replace (
'refs/heads/' ,
''
)
} else if ( github . context . payload . release ? . target_commitish ) {
sourceBranch = github . context . payload . release ? . target_commitish
}
2023-07-24 20:34:13 +00:00
2023-08-23 21:04:44 +00:00
await gitFetch ( {
cwd : workingDirectory ,
args : [
. . . gitFetchExtraArgs ,
'-u' ,
'--progress' ,
` --deepen= ${ inputs . fetchDepth } ` ,
'origin' ,
` +refs/heads/ ${ sourceBranch } :refs/remotes/origin/ ${ sourceBranch } `
]
} )
} else {
await gitFetch ( {
cwd : workingDirectory ,
args : [
. . . gitFetchExtraArgs ,
'-u' ,
'--progress' ,
` --deepen= ${ inputs . fetchDepth } ` ,
'origin' ,
` +refs/heads/ ${ targetBranch } :refs/remotes/origin/ ${ targetBranch } `
]
} )
2023-07-24 20:34:13 +00:00
}
2023-08-23 21:04:44 +00:00
if ( hasSubmodule ) {
await gitFetchSubmodules ( {
cwd : workingDirectory ,
args : [
. . . gitFetchExtraArgs ,
'-u' ,
'--progress' ,
` --deepen= ${ inputs . fetchDepth } `
]
} )
}
2023-05-25 18:22:24 +00:00
} else {
2024-01-13 21:53:17 +00:00
if ( hasSubmodule && inputs . fetchAdditionalSubmoduleHistory ) {
2023-08-23 21:04:44 +00:00
await gitFetchSubmodules ( {
cwd : workingDirectory ,
args : [
. . . gitFetchExtraArgs ,
'-u' ,
'--progress' ,
` --deepen= ${ inputs . fetchDepth } `
]
} )
}
2023-05-25 18:22:24 +00:00
}
}
2023-07-18 09:44:59 +00:00
const currentSha = await getCurrentSHA ( { inputs , workingDirectory } )
2023-11-27 07:11:43 +00:00
let previousSha = await cleanShaInput ( {
sha : inputs.baseSha ,
cwd : workingDirectory ,
token : inputs.token
} )
2023-05-25 18:22:24 +00:00
const diff = '..'
2023-10-02 23:35:05 +00:00
const currentBranchName = await getCurrentBranchName ( { cwd : workingDirectory } )
if (
currentBranchName &&
currentBranchName !== 'HEAD' &&
( currentBranchName !== targetBranch || currentBranchName !== currentBranch )
) {
targetBranch = currentBranchName
currentBranch = currentBranchName
}
2023-05-25 18:22:24 +00:00
2023-11-27 08:36:29 +00:00
if ( inputs . baseSha && inputs . sha && currentBranch && targetBranch ) {
2023-05-25 18:22:24 +00:00
if ( previousSha === currentSha ) {
core . error (
` Similar commit hashes detected: previous sha: ${ previousSha } is equivalent to the current sha: ${ currentSha } . `
)
core . error (
` Please verify that both commits are valid, and increase the fetch_depth to a number higher than ${ inputs . fetchDepth } . `
)
throw new Error ( 'Similar commit hashes detected.' )
}
2023-05-26 03:14:24 +00:00
core . debug ( ` Previous SHA: ${ previousSha } ` )
2023-05-25 18:22:24 +00:00
return {
previousSha ,
currentSha ,
currentBranch ,
targetBranch ,
diff
}
}
2023-11-27 07:54:44 +00:00
if ( ! previousSha || previousSha === currentSha ) {
2023-05-25 18:22:24 +00:00
core . debug ( 'Getting previous SHA...' )
if ( inputs . since ) {
core . debug ( ` Getting base SHA for ' ${ inputs . since } '... ` )
try {
2023-05-25 23:16:16 +00:00
const allCommitsFrom = await gitLog ( {
2023-05-25 18:22:24 +00:00
cwd : workingDirectory ,
2023-05-25 23:16:16 +00:00
args : [ '--format=%H' , '--date' , 'local' , '--since' , inputs . since ]
2023-05-25 18:22:24 +00:00
} )
2023-05-25 23:16:16 +00:00
if ( allCommitsFrom ) {
const allCommitsFromArray = allCommitsFrom . split ( '\n' )
previousSha = allCommitsFromArray [ allCommitsFromArray . length - 1 ]
}
2023-05-25 18:22:24 +00:00
} catch ( error ) {
core . error (
` Invalid since date: ${ inputs . since } . ${ ( error as Error ) . message } `
)
throw error
}
} else if ( isTag ) {
core . debug ( 'Getting previous SHA for tag...' )
const { sha , tag } = await getPreviousGitTag ( { cwd : workingDirectory } )
previousSha = sha
targetBranch = tag
} else {
2023-07-24 21:40:48 +00:00
if ( github . context . eventName === 'merge_group' ) {
core . debug ( 'Getting previous SHA for merge group...' )
previousSha = github . context . payload . merge_group ? . base_sha
} else {
core . debug ( 'Getting previous SHA for last remote commit...' )
if (
github . context . payload . forced === 'false' ||
! github . context . payload . forced
) {
previousSha = github . context . payload . before
}
2023-05-25 18:22:24 +00:00
}
if (
! previousSha ||
previousSha === '0000000000000000000000000000000000000000'
) {
2023-05-25 23:39:26 +00:00
previousSha = await getParentSha ( {
cwd : workingDirectory
} )
2023-05-26 03:14:24 +00:00
} else if (
( await verifyCommitSha ( {
sha : previousSha ,
cwd : workingDirectory ,
showAsErrorMessage : false
} ) ) !== 0
) {
core . warning (
` Previous commit ${ previousSha } is not valid. Using parent commit. `
)
previousSha = await getParentSha ( {
cwd : workingDirectory
} )
2023-05-25 18:22:24 +00:00
}
2023-05-26 16:48:32 +00:00
if ( ! previousSha || previousSha === currentSha ) {
previousSha = await getParentSha ( {
cwd : workingDirectory
} )
if ( ! previousSha ) {
2023-05-25 18:22:24 +00:00
core . warning ( 'Initial commit detected no previous commit found.' )
initialCommit = true
previousSha = currentSha
}
}
}
}
await verifyCommitSha ( { sha : previousSha , cwd : workingDirectory } )
core . debug ( ` Previous SHA: ${ previousSha } ` )
core . debug ( ` Target branch: ${ targetBranch } ` )
core . debug ( ` Current branch: ${ currentBranch } ` )
if ( ! initialCommit && previousSha === currentSha ) {
core . error (
` Similar commit hashes detected: previous sha: ${ previousSha } is equivalent to the current sha: ${ currentSha } . `
)
core . error (
` Please verify that both commits are valid, and increase the fetch_depth to a number higher than ${ inputs . fetchDepth } . `
)
throw new Error ( 'Similar commit hashes detected.' )
}
return {
previousSha ,
currentSha ,
currentBranch ,
targetBranch ,
2023-05-26 16:48:32 +00:00
diff ,
initialCommit
2023-05-25 18:22:24 +00:00
}
}
export const getSHAForPullRequestEvent = async (
inputs : Inputs ,
env : Env ,
workingDirectory : string ,
isShallow : boolean ,
hasSubmodule : boolean ,
2023-06-14 19:22:47 +00:00
gitFetchExtraArgs : string [ ]
2023-05-25 18:22:24 +00:00
) : Promise < DiffResult > = > {
2023-07-18 09:44:59 +00:00
let targetBranch = github . context . payload . pull_request ? . base ? . ref
const currentBranch = github . context . payload . pull_request ? . head ? . ref
2023-05-25 18:22:24 +00:00
if ( inputs . sinceLastRemoteCommit ) {
targetBranch = currentBranch
}
2023-08-23 21:04:44 +00:00
if ( ! inputs . skipInitialFetch ) {
2023-05-25 18:22:24 +00:00
core . info ( 'Repository is shallow, fetching more history...' )
2023-08-23 21:04:44 +00:00
if ( isShallow ) {
let prFetchExitCode = await gitFetch ( {
2023-05-25 18:22:24 +00:00
cwd : workingDirectory ,
args : [
2023-06-14 19:22:47 +00:00
. . . gitFetchExtraArgs ,
2023-05-25 18:22:24 +00:00
'-u' ,
'--progress' ,
'origin' ,
2023-08-23 21:04:44 +00:00
` pull/ ${ github . context . payload . pull_request ? . number } /head: ${ currentBranch } `
2023-05-25 18:22:24 +00:00
]
} )
2023-08-23 21:04:44 +00:00
if ( prFetchExitCode !== 0 ) {
prFetchExitCode = await gitFetch ( {
cwd : workingDirectory ,
args : [
. . . gitFetchExtraArgs ,
'-u' ,
'--progress' ,
` --deepen= ${ inputs . fetchDepth } ` ,
'origin' ,
` +refs/heads/ ${ currentBranch } *:refs/remotes/origin/ ${ currentBranch } * `
]
} )
}
2023-06-01 16:03:09 +00:00
2023-08-23 21:04:44 +00:00
if ( prFetchExitCode !== 0 ) {
throw new Error (
'Failed to fetch pull request branch. Please ensure "persist-credentials" is set to "true" when checking out the repository. See: https://github.com/actions/checkout#usage'
)
}
2023-05-25 18:22:24 +00:00
2023-08-23 21:04:44 +00:00
if ( ! inputs . sinceLastRemoteCommit ) {
core . debug ( 'Fetching target branch...' )
await gitFetch ( {
cwd : workingDirectory ,
args : [
. . . gitFetchExtraArgs ,
'-u' ,
'--progress' ,
` --deepen= ${ inputs . fetchDepth } ` ,
'origin' ,
` +refs/heads/ ${ targetBranch } :refs/remotes/origin/ ${ targetBranch } `
]
} )
if ( hasSubmodule ) {
await gitFetchSubmodules ( {
cwd : workingDirectory ,
args : [
. . . gitFetchExtraArgs ,
'-u' ,
'--progress' ,
` --deepen= ${ inputs . fetchDepth } `
]
} )
}
}
} else {
2024-01-13 21:53:17 +00:00
if ( hasSubmodule && inputs . fetchAdditionalSubmoduleHistory ) {
2023-05-25 18:22:24 +00:00
await gitFetchSubmodules ( {
cwd : workingDirectory ,
args : [
2023-06-14 19:22:47 +00:00
. . . gitFetchExtraArgs ,
2023-05-25 18:22:24 +00:00
'-u' ,
'--progress' ,
` --deepen= ${ inputs . fetchDepth } `
]
} )
}
}
2023-05-30 21:27:56 +00:00
core . info ( 'Completed fetching more history.' )
2023-05-25 18:22:24 +00:00
}
2023-07-18 09:44:59 +00:00
const currentSha = await getCurrentSHA ( { inputs , workingDirectory } )
2023-11-27 07:11:43 +00:00
let previousSha = await cleanShaInput ( {
sha : inputs.baseSha ,
cwd : workingDirectory ,
token : inputs.token
} )
2023-05-25 18:22:24 +00:00
let diff = '...'
2023-11-27 08:36:29 +00:00
if ( inputs . baseSha && inputs . sha && currentBranch && targetBranch ) {
2023-05-25 18:22:24 +00:00
if ( previousSha === currentSha ) {
core . error (
` Similar commit hashes detected: previous sha: ${ previousSha } is equivalent to the current sha: ${ currentSha } . `
)
core . error (
` Please verify that both commits are valid, and increase the fetch_depth to a number higher than ${ inputs . fetchDepth } . `
)
throw new Error ( 'Similar commit hashes detected.' )
}
2023-05-26 03:14:24 +00:00
core . debug ( ` Previous SHA: ${ previousSha } ` )
2023-05-25 18:22:24 +00:00
return {
previousSha ,
currentSha ,
currentBranch ,
targetBranch ,
diff
}
}
if (
2023-07-18 09:44:59 +00:00
! github . context . payload . pull_request ? . base ? . ref ||
github . context . payload . head ? . repo ? . fork === 'true'
2023-05-25 18:22:24 +00:00
) {
diff = '..'
}
2023-11-27 07:54:44 +00:00
if ( ! previousSha || previousSha === currentSha ) {
2023-05-25 18:22:24 +00:00
if ( inputs . sinceLastRemoteCommit ) {
2023-07-18 09:44:59 +00:00
previousSha = github . context . payload . before
2023-05-25 18:22:24 +00:00
2023-06-02 03:16:31 +00:00
if (
2023-06-14 18:45:32 +00:00
! previousSha ||
( previousSha &&
2023-09-09 01:18:37 +00:00
( await verifyCommitSha ( {
sha : previousSha ,
cwd : workingDirectory ,
showAsErrorMessage : false
} ) ) !== 0 )
2023-06-02 03:16:31 +00:00
) {
2023-09-09 01:18:37 +00:00
core . info (
` Unable to locate the previous commit in the local history for ${ github . context . eventName } ( ${ github . context . payload . action } ) event. Falling back to the previous commit in the local history. `
)
previousSha = await getParentSha ( {
cwd : workingDirectory
} )
2023-09-01 20:07:33 +00:00
if (
github . context . payload . action &&
2023-09-09 01:18:37 +00:00
github . context . payload . action === 'synchronize' &&
previousSha &&
2023-09-09 01:39:42 +00:00
( ! previousSha ||
( previousSha &&
( await verifyCommitSha ( {
sha : previousSha ,
cwd : workingDirectory ,
showAsErrorMessage : false
} ) ) !== 0 ) )
2023-09-01 20:07:33 +00:00
) {
2023-09-09 01:18:37 +00:00
throw new Error (
'Unable to locate the previous commit in the local history. Please ensure to checkout pull request HEAD commit instead of the merge commit. See: https://github.com/actions/checkout/blob/main/README.md#checkout-pull-request-head-commit-instead-of-merge-commit'
2023-09-01 20:07:33 +00:00
)
}
2023-06-16 06:17:13 +00:00
2023-09-09 01:39:42 +00:00
if (
! previousSha ||
( previousSha &&
( await verifyCommitSha ( {
sha : previousSha ,
cwd : workingDirectory ,
showAsErrorMessage : false
} ) ) !== 0 )
) {
2023-09-09 01:18:37 +00:00
throw new Error (
'Unable to locate the previous commit in the local history. Please ensure to checkout pull request HEAD commit instead of the merge commit. See: https://github.com/actions/checkout/blob/main/README.md#checkout-pull-request-head-commit-instead-of-merge-commit'
2023-06-16 06:17:13 +00:00
)
}
2023-05-25 18:22:24 +00:00
}
} else {
2023-09-09 03:02:46 +00:00
if ( github . context . payload . action === 'closed' ) {
2023-07-18 09:44:59 +00:00
previousSha = github . context . payload . pull_request ? . base ? . sha
2023-09-09 03:02:46 +00:00
} else {
previousSha = await getRemoteBranchHeadSha ( {
cwd : workingDirectory ,
branch : targetBranch
} )
if ( ! previousSha ) {
previousSha = github . context . payload . pull_request ? . base ? . sha
}
2023-05-25 18:22:24 +00:00
}
if ( isShallow ) {
if (
2023-06-16 06:17:13 +00:00
! ( await canDiffCommits ( {
2023-05-25 18:22:24 +00:00
cwd : workingDirectory ,
sha1 : previousSha ,
sha2 : currentSha ,
diff
2023-06-16 06:17:13 +00:00
} ) )
2023-05-25 18:22:24 +00:00
) {
2023-06-26 05:10:22 +00:00
core . info (
2023-05-25 18:22:24 +00:00
'Merge base is not in the local history, fetching remote target branch...'
)
for ( let i = 1 ; i <= 10 ; i ++ ) {
await gitFetch ( {
cwd : workingDirectory ,
args : [
2023-06-14 19:22:47 +00:00
. . . gitFetchExtraArgs ,
2023-05-25 18:22:24 +00:00
'-u' ,
'--progress' ,
` --deepen= ${ inputs . fetchDepth } ` ,
'origin' ,
` +refs/heads/ ${ targetBranch } :refs/remotes/origin/ ${ targetBranch } `
]
} )
if (
await canDiffCommits ( {
cwd : workingDirectory ,
sha1 : previousSha ,
sha2 : currentSha ,
diff
} )
) {
break
}
2023-06-26 05:10:22 +00:00
core . info (
2023-05-25 18:22:24 +00:00
'Merge base is not in the local history, fetching remote target branch again...'
)
2023-06-26 05:10:22 +00:00
core . info ( ` Attempt ${ i } /10 ` )
2023-05-25 18:22:24 +00:00
}
}
}
}
if ( ! previousSha || previousSha === currentSha ) {
2023-07-18 09:44:59 +00:00
previousSha = github . context . payload . pull_request ? . base ? . sha
2023-05-25 18:22:24 +00:00
}
}
if (
! ( await canDiffCommits ( {
cwd : workingDirectory ,
sha1 : previousSha ,
sha2 : currentSha ,
diff
} ) )
) {
diff = '..'
}
await verifyCommitSha ( { sha : previousSha , cwd : workingDirectory } )
core . debug ( ` Previous SHA: ${ previousSha } ` )
if (
! ( await canDiffCommits ( {
cwd : workingDirectory ,
sha1 : previousSha ,
sha2 : currentSha ,
diff
} ) )
) {
2023-09-20 19:14:07 +00:00
core . warning (
'If this pull request is from a forked repository, please set the checkout action `repository` input to the same repository as the pull request.'
)
core . warning (
'This can be done by setting actions/checkout `repository` to ${{ github.event.pull_request.head.repo.full_name }}'
)
2023-05-25 18:22:24 +00:00
throw new Error (
` Unable to determine a difference between ${ previousSha } ${ diff } ${ currentSha } `
)
}
if ( previousSha === currentSha ) {
core . error (
` Similar commit hashes detected: previous sha: ${ previousSha } is equivalent to the current sha: ${ currentSha } . `
)
2023-06-08 11:20:11 +00:00
// This occurs if a PR is created from a forked repository and the event is pull_request_target.
// - name: Checkout to branch
// uses: actions/checkout@v3
// Without setting the repository to use the same repository as the pull request will cause the previousSha
// to be the same as the currentSha since the currentSha cannot be found in the local history.
// The solution is to use:
// - name: Checkout to branch
// uses: actions/checkout@v3
// with:
// repository: ${{ github.event.pull_request.head.repo.full_name }}
2023-07-18 09:44:59 +00:00
if ( github . context . eventName === 'pull_request_target' ) {
2023-06-08 11:20:11 +00:00
core . warning (
'If this pull request is from a forked repository, please set the checkout action `repository` input to the same repository as the pull request.'
)
core . warning (
'This can be done by setting actions/checkout `repository` to ${{ github.event.pull_request.head.repo.full_name }}'
)
} else {
core . error (
` Please verify that both commits are valid, and increase the fetch_depth to a number higher than ${ inputs . fetchDepth } . `
)
}
2023-05-25 18:22:24 +00:00
throw new Error ( 'Similar commit hashes detected.' )
}
return {
previousSha ,
currentSha ,
currentBranch ,
targetBranch ,
diff
}
}