Skip to content

Commit d4b93a6

Browse files
fix: warn users when GitHub Actions is making a merge commit (#222)
changes: - Change the signature of `gitGetHead` to return a warning for GitHub Actions merge commits - Propagate the warning to `report` command response
1 parent 33ee4d4 commit d4b93a6

File tree

5 files changed

+85
-34
lines changed

5 files changed

+85
-34
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test:
2222
test_setup:
2323
mkdir -p ${CODE_PATH}
2424
cd ${CODE_PATH} && ls -A1 | xargs rm -rf
25-
git clone https://github.com/DeepSourceCorp/cli ${CODE_PATH}
25+
git clone https://github.com/DeepSourceCorp/july ${CODE_PATH}
2626
chmod +x /tmp/deepsource
2727
cp ./command/report/tests/golden_files/python_coverage.xml /tmp
2828

command/report/git.go

Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,31 @@ import (
1111
)
1212

1313
// gitGetHead accepts a git directory and returns head commit OID / error
14-
func gitGetHead(workspaceDir string) (string, error) {
14+
func gitGetHead(workspaceDir string) (headOID string, warning string, err error) {
15+
// Check if DeepSource's Test coverage action triggered this first before executing any git commands.
16+
headOID, err = getTestCoverageActionCommit()
17+
if headOID != "" {
18+
return
19+
}
20+
21+
// get the top commit manually, using git command
22+
headOID, err = fetchHeadManually(workspaceDir)
23+
if err != nil {
24+
fmt.Println(err)
25+
sentry.CaptureException(err)
26+
return
27+
}
28+
1529
// TRAVIS CI
16-
// Get USER env variable.
1730
if envUser := os.Getenv("USER"); envUser == "travis" {
18-
// Travis creates a merge commit for pull requests on forks.
19-
// The head of commit is this merge commit, which does not match the commit of deepsource check.
20-
// Fetch value of pull request SHA. If this is a PR, it will return SHA of HEAD commit of the PR, else "".
21-
// If prSHA is not empty, that means we got an SHA, which is HEAD. Return this.
22-
if prSHA := os.Getenv("TRAVIS_PULL_REQUEST_SHA"); len(prSHA) > 0 {
23-
return prSHA, nil
24-
}
31+
headOID, warning, err = getTravisCommit(headOID)
32+
return
2533
}
2634

2735
// GITHUB ACTIONS
28-
// Check if it is a GitHub Action Environment
29-
// If it is: then get the HEAD from `GITHUB_SHA` environment
3036
if _, isGitHubEnv := os.LookupEnv("GITHUB_ACTIONS"); isGitHubEnv {
31-
// When GITHUB_REF is not set, GITHUB_SHA points to original commit.
32-
// When set, it points to the "latest *merge* commit in the branch".
33-
// Ref: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request
34-
if _, isBranchCommit := os.LookupEnv("GITHUB_REF"); !isBranchCommit {
35-
return os.Getenv("GITHUB_SHA"), nil
36-
}
37+
headOID, warning, err = getGitHubActionsCommit(headOID)
38+
return
3739
}
3840

3941
// Check if the `GIT_COMMIT_SHA` environment variable exists. If yes, return this as
@@ -47,18 +49,11 @@ func gitGetHead(workspaceDir string) (string, error) {
4749
// GIT_COMMIT_SHA=$(git --no-pager rev-parse HEAD | tr -d '\n')
4850
// docker run -e DEEPSOURCE_DSN -e GIT_COMMIT_SHA ...
4951
if _, isManuallyInjectedSHA := os.LookupEnv("GIT_COMMIT_SHA"); isManuallyInjectedSHA {
50-
return os.Getenv("GIT_COMMIT_SHA"), nil
52+
return os.Getenv("GIT_COMMIT_SHA"), "", nil
5153
}
5254

53-
// If we are here, it means this is neither GitHub Action on default branch,
54-
// nor a travis env with PR. Continue to fetch the headOID via the git command.
55-
headOID, err := fetchHeadManually(workspaceDir)
56-
if err != nil {
57-
fmt.Println(err)
58-
sentry.CaptureException(err)
59-
return "", err
60-
}
61-
return headOID, nil
55+
// If we are here, it means there weren't any special cases. Return the manually found headOID.
56+
return
6257
}
6358

6459
// Fetches the latest commit hash using the command `git rev-parse HEAD`
@@ -80,3 +75,60 @@ func fetchHeadManually(directoryPath string) (string, error) {
8075
// Trim newline suffix from Commit OID
8176
return strings.TrimSuffix(outStr, "\n"), nil
8277
}
78+
79+
// Handle special cases for GitHub Actions.
80+
func getGitHubActionsCommit(topCommit string) (headOID string, warning string, err error) {
81+
// When GITHUB_REF is not set, GITHUB_SHA points to original commit.
82+
// When set, it points to the "latest *merge* commit in the branch".
83+
// Early exit when GITHUB_SHA points to the original commit.
84+
// Ref: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request
85+
if _, isRefPresent := os.LookupEnv("GITHUB_REF"); !isRefPresent {
86+
headOID = os.Getenv("GITHUB_SHA")
87+
return
88+
}
89+
90+
// Case: Detect Merge commit made by GitHub Actions, which pull_request events are nutorious to make.
91+
// We are anyways going to return `headOID` fetched manually, but want to warn users about the merge commit.
92+
93+
// When ref is not provided during the checkout step, headOID would be the same as GITHUB_SHA
94+
// This confirms the merge commit.
95+
// event names where GITHUB_SHA would be of a merge commit:
96+
// "pull_request",
97+
// "pull_request_review",
98+
// "pull_request_review",
99+
// "pull_request_review_comment",
100+
eventName := os.Getenv("GITHUB_EVENT_NAME")
101+
eventCommitSha := os.Getenv("GITHUB_SHA")
102+
if strings.HasPrefix(eventName, "pull_request") && topCommit == eventCommitSha {
103+
warning = "Warning: Looks like the checkout step is making a merge commit. " +
104+
"Test coverage Analyzer would not run for the reported artifact because the merge commit doesn't exist upstream.\n" +
105+
"Please refer to the docs for required changes. Ref: https://docs.deepsource.com/docs/analyzers-test-coverage#with-github-actions"
106+
}
107+
headOID = topCommit
108+
return
109+
}
110+
111+
// Return PR's HEAD ref set as env variable manually by DeepSource's Test coverage action.
112+
func getTestCoverageActionCommit() (headOID string, err error) {
113+
// This is kept separate from `getGitHubActionsCommit` because we don't want to run any git command manually
114+
// before this is checked. Since this is guaranteed to be set if artifact is sent using our GitHub action,
115+
// we can reliably send the commit SHA, and no git commands are executed, making the actions work all the time. \o/
116+
117+
// We are setting PR's head commit as default using github context as env variable: "GHA_HEAD_COMMIT_SHA"
118+
headOID = os.Getenv("GHA_HEAD_COMMIT_SHA")
119+
120+
return
121+
}
122+
123+
// Handle special case for TravisCI
124+
func getTravisCommit(topCommit string) (string, string, error) {
125+
// Travis creates a merge commit for pull requests on forks.
126+
// The head of commit is this merge commit, which does not match the commit of deepsource check.
127+
// Fetch value of pull request SHA. If this is a PR, it will return SHA of HEAD commit of the PR, else "".
128+
// If prSHA is not empty, that means we got an SHA, which is HEAD. Return this.
129+
if prSHA := os.Getenv("TRAVIS_PULL_REQUEST_SHA"); len(prSHA) > 0 {
130+
return prSHA, "", nil
131+
}
132+
133+
return topCommit, "", nil
134+
}

command/report/report.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func (opts *ReportOptions) Run() int {
179179
dsnAccessToken := dsnSplitTokenHost[0]
180180

181181
// Head Commit OID
182-
headCommitOID, err := gitGetHead(currentDir)
182+
headCommitOID, warning, err := gitGetHead(currentDir)
183183
if err != nil {
184184
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to get commit OID HEAD. Make sure you are running the CLI from a git repository")
185185
sentry.CaptureException(err)
@@ -375,5 +375,8 @@ func (opts *ReportOptions) Run() int {
375375
if queryResponse.Data.CreateArtifact.Message != "" {
376376
fmt.Printf("Message %s\n", queryResponse.Data.CreateArtifact.Message)
377377
}
378+
if warning != "" {
379+
fmt.Print(warning)
380+
}
378381
return 0
379382
}

0 commit comments

Comments
 (0)