1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-11-06 09:09:19 +02:00

feat(codeql) merge commit git reference (#3877)

Sets git reference and gitRemoteCommitId.
Jenkins has 2 strategies - 'Merging the pull request with the current target branch revision' and 'The current pull request revision'. When 'Merging the pull request with the current target branch revision' is run, Jenkins creates a local merge commit and runs a job for that particular merge commitId. This commitId is then used for codeql to upload sarif, on upload it throws an error as the merge commit does not exist in github. To resolve this we have introduces a new variable 'gitRemoteCommitId' in commonPipelineEnvironment which gives the remote merge commit id.
This commit is contained in:
sumeet patil
2022-07-12 10:25:17 +02:00
committed by GitHub
parent 4c4f8e3e97
commit 9c4446ae0a
9 changed files with 216 additions and 18 deletions

View File

@@ -107,6 +107,10 @@ func uploadResults(config *codeqlExecuteScanOptions, utils codeqlExecuteScanUtil
return errors.New("failed running upload-results as github token was not specified")
}
if config.CommitID == "NA" {
return errors.New("failed running upload-results as gitCommitId is not available")
}
var repoInfo RepoInfo
err := getGitRepoInfo(config.Repository, &repoInfo)
if err != nil {

View File

@@ -303,7 +303,7 @@ func codeqlExecuteScanMetadata() config.StepData {
ResourceRef: []config.ResourceReference{
{
Name: "commonPipelineEnvironment",
Param: "git/commitId",
Param: "git/remoteCommitId",
},
},
Scope: []string{},

View File

@@ -32,6 +32,11 @@ func TestRunCodeqlExecuteScan(t *testing.T) {
assert.Error(t, runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()))
})
t.Run("GitCommitID is NA on upload results", func(t *testing.T) {
config := codeqlExecuteScanOptions{BuildTool: "maven", UploadResults: true, ModulePath: "./", CommitID: "NA"}
assert.Error(t, runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()))
})
t.Run("Upload results with token", func(t *testing.T) {
config := codeqlExecuteScanOptions{BuildTool: "maven", ModulePath: "./", UploadResults: true, GithubToken: "test"}
assert.Equal(t, nil, runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()))

View File

@@ -110,7 +110,7 @@ spec:
description: "SHA of commit that was analyzed."
resourceRef:
- name: commonPipelineEnvironment
param: git/commitId
param: git/remoteCommitId
type: string
containers:
- image: ""

View File

@@ -4,6 +4,63 @@ boolean insideWorkTree() {
return sh(returnStatus: true, script: 'git rev-parse --is-inside-work-tree 1>/dev/null 2>&1') == 0
}
boolean isMergeCommit(){
def cmd = 'git rev-parse --verify HEAD^2'
return sh(returnStatus: true, script: cmd) == 0
}
String getGitMergeCommitId(String gitChangeId){
if(!scm){
throw new Exception('scm content not found')
}
def remoteConfig = scm.getUserRemoteConfigs()
if(!remoteConfig || remoteConfig.size() == 0 || !remoteConfig[0].getCredentialsId()){
throw new Exception('scm remote configuration not found')
}
def scmCredId = remoteConfig[0].getCredentialsId()
try{
withCredentials([gitUsernamePassword(credentialsId: scmCredId, gitToolName: 'git-tool')]) {
sh 'git fetch origin "+refs/pull/'+gitChangeId+'/*:refs/remotes/origin/pull/'+gitChangeId+'/*"'
}
} catch (Exception e) {
echo 'Error in running git fetch'
throw e
}
String commitId
def cmd = "git rev-parse refs/remotes/origin/pull/"+gitChangeId+"/merge"
try {
commitId = sh(returnStdout: true, script: cmd).trim()
} catch (Exception e) {
echo 'Exception occurred getting the git merge commitId'
throw e
}
return commitId
}
boolean compareParentsOfMergeAndHead(String mergeCommitId){
try {
String mergeCommitParents = sh(returnStdout: true, script: "git rev-parse ${mergeCommitId}^@ | tac").trim()
String headCommitParents = sh(returnStdout: true, script: "git rev-parse HEAD^@").trim()
if(mergeCommitParents.equals(headCommitParents)){
return true
}
} catch (Exception e) {
echo 'Error comparing merge commit parents and local merge commit parents'
throw e
}
echo "GH merge parents: ${mergeCommitParents}"
echo "Local merge parents: ${headCommitParents}"
echo 'Github merge parents and local merge parents do not match; PR was updated since Jenkins job started. Try re-running the job.'
return false
}
boolean isWorkTreeDirty() {
if(!insideWorkTree()) error 'Method \'isWorkTreeClean\' called outside a git work tree.'

View File

@@ -1,5 +1,6 @@
import com.sap.piper.DefaultValueCache
import com.sap.piper.Utils
import com.sap.piper.GitUtils
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -297,38 +298,120 @@ class SetupCommonPipelineEnvironmentTest extends BasePiperTest {
@Test
void "Set scmInfo parameter sets commit id"() {
def GitUtils gitUtils = new GitUtils() {
boolean isMergeCommit(){
return false
}
}
helper.registerAllowedMethod("fileExists", [String], { String path ->
return path.endsWith('.pipeline/config.yml')
})
def dummyScmInfo = [GIT_COMMIT: 'dummy_git_commit_id', GIT_URL: 'https://github.com/testOrg/testRepo.git']
stepRule.step.setupCommonPipelineEnvironment(script: nullScript, scmInfo: dummyScmInfo)
stepRule.step.setupCommonPipelineEnvironment(script: nullScript, scmInfo: dummyScmInfo, gitUtils: gitUtils)
assertThat(nullScript.commonPipelineEnvironment.gitCommitId, is('dummy_git_commit_id'))
}
@Test
void "Set scmInfo parameter sets git reference for branch"() {
def GitUtils gitUtils = new GitUtils() {
boolean isMergeCommit(){
return false
}
}
helper.registerAllowedMethod("fileExists", [String], { String path ->
return path.endsWith('.pipeline/config.yml')
})
def dummyScmInfo = [GIT_BRANCH: 'origin/testbranch']
def dummyScmInfo = [GIT_COMMIT: 'dummy_git_commit_id', GIT_BRANCH: 'origin/testbranch']
stepRule.step.setupCommonPipelineEnvironment(script: nullScript, scmInfo: dummyScmInfo)
stepRule.step.setupCommonPipelineEnvironment(script: nullScript, scmInfo: dummyScmInfo, gitUtils: gitUtils)
assertThat(nullScript.commonPipelineEnvironment.gitRef, is('refs/heads/testbranch'))
}
@Test
void "Set scmInfo parameter sets git reference for pull request"() {
void "sets gitReference and gitRemoteCommit for pull request"() {
def GitUtils gitUtils = new GitUtils() {
boolean isMergeCommit(){
return false
}
String getGitMergeCommitId(String gitChangeId){
return "dummy_merge_git_commit_id"
}
}
helper.registerAllowedMethod("fileExists", [String], { String path ->
return path.endsWith('.pipeline/config.yml')
})
def dummyScmInfo = [GIT_BRANCH: 'PR-42']
def dummyScmInfo = [GIT_COMMIT: 'dummy_git_commit_id', GIT_BRANCH: 'PR-42']
stepRule.step.setupCommonPipelineEnvironment(script: nullScript, scmInfo: dummyScmInfo)
stepRule.step.setupCommonPipelineEnvironment(script: nullScript, scmInfo: dummyScmInfo, gitUtils: gitUtils)
assertThat(nullScript.commonPipelineEnvironment.gitRef, is('refs/pull/42/head'))
assertThat(nullScript.commonPipelineEnvironment.gitRemoteCommitId, is('dummy_git_commit_id'))
}
@Test
void "sets gitReference and gitRemoteCommit for pull request for merge strategy"() {
def GitUtils gitUtils = new GitUtils() {
boolean isMergeCommit(){
return true
}
String getGitMergeCommitId(String gitChangeId){
return "dummy_merge_git_commit_id"
}
boolean compareParentsOfMergeAndHead(String gitMergeCommitId){
return true
}
}
helper.registerAllowedMethod("fileExists", [String], { String path ->
return path.endsWith('.pipeline/config.yml')
})
def dummyScmInfo = [GIT_COMMIT: 'dummy_git_commit_id', GIT_BRANCH: 'PR-42']
stepRule.step.setupCommonPipelineEnvironment(script: nullScript, scmInfo: dummyScmInfo, gitUtils: gitUtils)
assertThat(nullScript.commonPipelineEnvironment.gitRef, is('refs/pull/42/merge'))
assertThat(nullScript.commonPipelineEnvironment.gitRemoteCommitId, is('dummy_merge_git_commit_id'))
}
@Test
void "Set merge commit id as NA"() {
def GitUtils gitUtils = new GitUtils() {
boolean isMergeCommit(){
return true
}
String getGitMergeCommitId(String gitChangeId){
return "dummy_merge_git_commit_id"
}
boolean compareParentsOfMergeAndHead(String gitMergeCommitId){
return false
}
}
helper.registerAllowedMethod("fileExists", [String], { String path ->
return path.endsWith('.pipeline/config.yml')
})
def dummyScmInfo = [GIT_COMMIT: 'dummy_git_commit_id', GIT_BRANCH: 'PR-42']
stepRule.step.setupCommonPipelineEnvironment(script: nullScript, scmInfo: dummyScmInfo, gitUtils: gitUtils)
assertThat(nullScript.commonPipelineEnvironment.gitRef, is('refs/pull/42/merge'))
assertThat(nullScript.commonPipelineEnvironment.gitRemoteCommitId, is('NA'))
}
@Test
@@ -369,4 +452,3 @@ class SetupCommonPipelineEnvironmentTest extends BasePiperTest {
}
}
}

View File

@@ -137,6 +137,24 @@ class GitUtilsTest extends BasePiperTest {
assertThat(log.size(),is(equalTo(0)))
}
@Test
void testIsMergeCommitTrue() {
shellRule.setReturnValue('git rev-parse --verify HEAD^2', 0)
assertTrue(gitUtils.isMergeCommit())
}
@Test
void testIsMergeCommitFalse() {
shellRule.setReturnValue('git rev-parse --verify HEAD^2', 1)
assertFalse(gitUtils.isMergeCommit())
}
@Test
void testGetGitMergeCommitIdException() {
thrown.expect(Exception)
gitUtils.getGitMergeCommitId("1")
}
@Test
void testHandleTestRepository() {
def result, gitMap, stashName, config = [

View File

@@ -24,8 +24,14 @@ class commonPipelineEnvironment implements Serializable {
//Stores the current buildResult
String buildResult = 'SUCCESS'
//stores the gitCommitId as well as additional git information for the build during pipeline run
//stores the gitCommitId and gitRemoteCommitId as additional git information for the build during pipeline run
/*
Incase of 'Merging the pull request with the current target branch revision' stratergy in jenkins,
the jenkins creates its own local merge commit which is stored in gitCommitId.
gitRemoteCommitId will contain the actual remote merge commitId on git rather than local commitId
*/
String gitCommitId
String gitRemoteCommitId
String gitHeadCommitId
String gitCommitMessage
String gitSshUrl
@@ -86,6 +92,7 @@ class commonPipelineEnvironment implements Serializable {
containerProperties = [:]
gitCommitId = null
gitRemoteCommitId = null
gitHeadCommitId = null
gitCommitMessage = null
gitSshUrl = null
@@ -202,6 +209,7 @@ class commonPipelineEnvironment implements Serializable {
[filename: '.pipeline/commonPipelineEnvironment/github/repository', property: 'githubRepo'],
[filename: '.pipeline/commonPipelineEnvironment/git/branch', property: 'gitBranch'],
[filename: '.pipeline/commonPipelineEnvironment/git/commitId', property: 'gitCommitId'],
[filename: '.pipeline/commonPipelineEnvironment/git/remoteCommitId', property: 'gitRemoteCommitId'],
[filename: '.pipeline/commonPipelineEnvironment/git/headCommitId', property: 'gitHeadCommitId'],
[filename: '.pipeline/commonPipelineEnvironment/git/httpsUrl', property: 'gitHttpsUrl'],
[filename: '.pipeline/commonPipelineEnvironment/git/ref', property: 'gitRef'],

View File

@@ -6,6 +6,7 @@ import com.sap.piper.GenerateDocumentation
import com.sap.piper.ConfigurationHelper
import com.sap.piper.Utils
import com.sap.piper.analytics.InfluxData
import com.sap.piper.GitUtils
import groovy.transform.Field
@@ -118,7 +119,9 @@ void call(Map parameters = [:]) {
if (scmInfo) {
setGitUrlsOnCommonPipelineEnvironment(script, scmInfo.GIT_URL)
script.commonPipelineEnvironment.setGitCommitId(scmInfo.GIT_COMMIT)
setGitRefOnCommonPipelineEnvironment(script, scmInfo.GIT_BRANCH)
def gitUtils = parameters.gitUtils ?: new GitUtils()
setGitRefOnCommonPipelineEnvironment(script, scmInfo.GIT_COMMIT, scmInfo.GIT_BRANCH, gitUtils)
}
}
}
@@ -261,7 +264,7 @@ private void setGitUrlsOnCommonPipelineEnvironment(script, String gitUrl) {
script.commonPipelineEnvironment.setGithubRepo(gitRepo)
}
private void setGitRefOnCommonPipelineEnvironment(script, String gitBranch) {
private void setGitRefOnCommonPipelineEnvironment(script, String gitCommit, String gitBranch, def gitUtils) {
if(!gitBranch){
return
}
@@ -270,10 +273,31 @@ private void setGitRefOnCommonPipelineEnvironment(script, String gitBranch) {
gitBranch = gitBranch.split("/")[1]
}
//TODO: refs for merge pull requests
if (gitBranch.contains("PR")) {
script.commonPipelineEnvironment.setGitRef("refs/pull/" + gitBranch.split("-")[1] + "/head")
} else {
script.commonPipelineEnvironment.setGitRef("refs/heads/" + gitBranch)
}
if (!gitBranch.contains("PR")) {
script.commonPipelineEnvironment.setGitRef("refs/heads/" + gitBranch)
script.commonPipelineEnvironment.setGitRemoteCommitId(gitCommit)
return
}
boolean isMergeCommit = gitUtils.isMergeCommit()
def mergeOrHead = isMergeCommit?"merge":"head"
def changeId = gitBranch.split("-")[1]
script.commonPipelineEnvironment.setGitRef("refs/pull/" + changeId + "/" + mergeOrHead)
if(!isMergeCommit){
script.commonPipelineEnvironment.setGitRemoteCommitId(gitCommit)
return
}
try{
String gitRemoteCommitId = gitUtils.getGitMergeCommitId(changeId)
if(gitRemoteCommitId?.trim() && gitUtils.compareParentsOfMergeAndHead(gitRemoteCommitId)){
script.commonPipelineEnvironment.setGitRemoteCommitId(gitRemoteCommitId)
return
}
}catch(Exception e){
echo "Exception in getting git merge commit id or comparing git merge commit parents: ${e}"
}
script.commonPipelineEnvironment.setGitRemoteCommitId("NA")
}