Skip to content

Commit f579abd

Browse files
committed
Initial action definition
0 parents  commit f579abd

File tree

6 files changed

+271
-0
lines changed

6 files changed

+271
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Stefan Schubert
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# go-test-coverage-summary-action
2+
Action to show test and coverage summary as job summary and pull request comment.
3+
4+
## Inputs
5+
6+
### `test_results`
7+
Defines verbose test output file. Defaults to `test.out`.
8+
9+
### `coverage_profile`
10+
Defines coverage profile output file. Defaults to `cover.out`.
11+
12+
### `with_archive`
13+
Enables coverage html report as job artifact attachment. Defaults to `false`.
14+
15+
### `github_token`
16+
Used to comment pull requests. Defaults to `${{ github.token }}`.
17+
18+
## Example usage
19+
```
20+
- name: Run test
21+
run: go test -v -coverprofile cover.out ./... | tee test.out
22+
shell: bash
23+
- name: Process results
24+
if: always()
25+
uses: malaupa/go-test-coverage-summary-action@v0.2.0
26+
with:
27+
test_results: "test.out"
28+
coverage_profile: "cover.out"
29+
with_archive: true
30+
```

action.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: "Process results"
2+
description: "Process test and coverage results"
3+
inputs:
4+
test_results:
5+
description: "Test results file"
6+
default: "test.out"
7+
coverage_profile:
8+
description: "Coverage profile file"
9+
default: "cover.out"
10+
with_archive:
11+
description: "Attaches html coverage results as job artifact"
12+
default: "false"
13+
github_token:
14+
description: "Github token write pull request scope"
15+
default: "${{ github.token }}"
16+
runs:
17+
using: "composite"
18+
steps:
19+
- name: process results
20+
run: ${{ github.action_path }}/summary.sh
21+
env:
22+
TEST_RESULT_LOG: ${{ inputs.test_results }}
23+
COVERAGE_PROFILE: ${{ inputs.coverage_profile }}
24+
WITH_ARCHIVE: ${{ inputs.with_archive }}
25+
GITHUB_TOKEN: ${{ inputs.github_token }}
26+
PULL_REQUEST_NODE_ID: ${{ github.event.pull_request.node_id }}
27+
shell: bash
28+
- name: Archive code coverage results
29+
if: inputs.with_archive == 'true'
30+
uses: actions/upload-artifact@v3
31+
with:
32+
name: code-coverage-report
33+
path: coverage.html
34+
retention-days: 5

coverage_summary.awk

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
function bar(cov) {
2+
if (cov >= 95) {
3+
return ":green_square::green_square::green_square::green_square:"
4+
} else if (cov >= 75) {
5+
return ":green_square::green_square::green_square::red_square:"
6+
} else if (cov >= 50) {
7+
return ":green_square::green_square::red_square::red_square:"
8+
} else if (cov >= 25) {
9+
return ":green_square::red_square::red_square::red_square:"
10+
} else {
11+
return ":red_square::red_square::red_square::red_square:"
12+
}
13+
}
14+
15+
BEGIN {
16+
printf("## Coverage\n\n")
17+
details = "<details>\n<summary>Coverage Details</summary>\n\n"
18+
details = details"|Coverage|File|Uncovered Lines|\n"
19+
details = details"|-|-|-|\n"
20+
file = ""
21+
}
22+
$1 !~ /mode:/ {
23+
if (match($1,"^"package"([^:]+):([0-9]+).[0-9]+,([0-9]+).[0-9]+",f)) {
24+
if (file != "" && file != f[1] ) {
25+
fileCoverage = fileCovered/fileStatements*100
26+
if (lineStart != "" && lineEnd != "") {
27+
uncoveredLines = sprintf("%s[%s-%s](%s%s#L%s-L%s)", uncoveredLines, lineStart, lineEnd, baseUrl, file, lineStart, lineEnd)
28+
}
29+
details = sprintf("%s|%s %.1f%|%s|%s|\n", details, bar(fileCoverage), fileCoverage, file, uncoveredLines)
30+
fileCovered = 0
31+
fileStatements = 0
32+
uncoveredLines = ""
33+
lineStart = ""
34+
lineEnd = ""
35+
}
36+
file = f[1]
37+
statements += $2
38+
fileStatements += $2
39+
if ($3 != "0") {
40+
covered += $2
41+
fileCovered += $2
42+
next
43+
}
44+
if (lineStart == "" && lineEnd == "") {
45+
lineStart = f[2]
46+
lineEnd = f[3]
47+
next
48+
}
49+
if (lineEnd+0 == f[2]+0 || lineEnd+1 == f[2]+0) {
50+
lineEnd = f[3]
51+
next
52+
}
53+
uncoveredLines = sprintf("%s[%s-%s](%s%s#L%s-L%s), ", uncoveredLines, lineStart, lineEnd, baseUrl, file, lineStart, lineEnd)
54+
lineStart = f[2]
55+
lineEnd = f[3]
56+
}
57+
}
58+
END {
59+
if (statements > 0) {
60+
total = covered/statements*100
61+
printf("Total coverage: %s %.1f%\n", bar(total), total)
62+
details = details"\n</details>\n\n"
63+
print(details)
64+
}
65+
}

summary.sh

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/bash
2+
3+
set -e
4+
5+
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
6+
BASE_URL="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/blob/$GITHUB_SHA"
7+
SUMMARY_FILE=summary.md
8+
9+
if [[ -f "$GITHUB_WORKSPACE/$TEST_RESULT_LOG" ]] ; then
10+
echo "process test results"
11+
awk -f $SCRIPT_DIR/test_summary.awk -v workspace="$GITHUB_WORKSPACE" -v baseUrl="$BASE_URL" $GITHUB_WORKSPACE/$TEST_RESULT_LOG >> $SUMMARY_FILE
12+
fi
13+
14+
if [[ -f "$GITHUB_WORKSPACE/$COVERAGE_PROFILE" ]] ; then
15+
echo "process coverage profile"
16+
PACKAGE=$(awk '$1 ~ /module/ { print $2 }' $GITHUB_WORKSPACE/go.mod)
17+
awk -f $SCRIPT_DIR/coverage_summary.awk -v package="$PACKAGE" -v baseUrl="$BASE_URL" $GITHUB_WORKSPACE/$COVERAGE_PROFILE >> $SUMMARY_FILE
18+
fi
19+
20+
if [[ "$WITH_ARCHIVE" == "true" && -f "$GITHUB_WORKSPACE/$COVERAGE_PROFILE" ]] ; then
21+
echo "generate html report"
22+
go tool cover -html $GITHUB_WORKSPACE/$COVERAGE_PROFILE -o coverage.html
23+
fi
24+
25+
SUMMARY="$(cat $SUMMARY_FILE)"
26+
if [[ -f "$SUMMARY_FILE" && "$SUMMARY" != "" ]] ; then
27+
echo "attach job summary"
28+
echo "$SUMMARY" >> $GITHUB_STEP_SUMMARY
29+
30+
if [[ "$PULL_REQUEST_NODE_ID" != "" ]] ; then
31+
echo "fetch comments"
32+
COMMENT_BODY=$(printf '%s\n%s\n' "$SUMMARY" "<!-- go-test-coverage-summary -->")
33+
COMMENTS="$(gh api graphql -F subjectId=$PULL_REQUEST_NODE_ID -f query='
34+
query($subjectId: ID!) {
35+
node(id: $subjectId) {
36+
... on PullRequest {
37+
comments(first: 100) {
38+
nodes {
39+
id
40+
isMinimized
41+
body
42+
}
43+
}
44+
}
45+
}
46+
}
47+
' --jq '.data.node.comments.nodes | map(select((.body | contains("<!-- go-test-coverage-summary -->")) and .isMinimized == false)) | map(.id)[]')"
48+
49+
if [[ -n "$COMMENTS" ]]; then
50+
echo "update comment"
51+
for val in $COMMENTS; do
52+
gh api graphql -X POST -F id=$val -F body="$COMMENT_BODY" -f query='
53+
mutation UpdateComment($id: ID!, $body: String!) {
54+
updateIssueComment(input: {id: $id, body: $body}) {
55+
clientMutationId
56+
}
57+
}
58+
'
59+
done
60+
else
61+
echo "add comment"
62+
gh api graphql -X POST -F subjectId=$PULL_REQUEST_NODE_ID -F body="$COMMENT_BODY" -f query='
63+
mutation AddComment($subjectId: ID!, $body: String!) {
64+
addComment(input: {subjectId: $subjectId, body: $body}) {
65+
clientMutationId
66+
}
67+
}
68+
' || true
69+
fi
70+
fi
71+
fi

test_summary.awk

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
BEGIN {
2+
printf("## Test Result\n\n")
3+
details = "<details>\n<summary>Test Details</summary>\n\n"
4+
}
5+
$1 ~ /===/ {
6+
count++
7+
errorLink = ""
8+
error = ""
9+
errorSummary = ""
10+
next
11+
}
12+
$1 ~ /---/ {
13+
if ( $2 ~ /FAIL:/ ) {
14+
failed++
15+
details = sprintf("%s:red_circle: %s %s\n", details, $3, $4)
16+
details = sprintf("%s<details>\n<summary>Log</summary>\n\n%s\n\n</details>\n\n", details, errorSummary)
17+
}
18+
if ( $2 ~ /SKIP:/) {
19+
skipped++
20+
details = sprintf("%s:white_circle: %s\n", details, $3)
21+
}
22+
if ( $2 ~ /PASS:/) {
23+
details = sprintf("%s:green_circle: %s %s\n", details, $3, $4)
24+
}
25+
next
26+
}
27+
$1 ~ /Test:/ {
28+
error = error$0"\n"
29+
errorSummary = sprintf("%s%s\n\n```\n%s```\n\n", errorSummary, errorLink, error)
30+
errorLink = ""
31+
error = ""
32+
next
33+
}
34+
{
35+
if(match($0,workspace"(.+):([0-9]+)$",e)) {
36+
errorLink = sprintf("[%s:%s](%s%s#L%s)", e[1], e[2], baseUrl, e[1], e[2])
37+
}
38+
error = error$0"\n"
39+
}
40+
END {
41+
if (count > 0) {
42+
if (failed > 0) {
43+
printf(":broken_heart: %d Test(s) failed of %d Tests\n\n", failed, count)
44+
} else {
45+
printf(":raised_hands: %d Tests pass\n\n", count)
46+
}
47+
details = details"\n</details>\n\n"
48+
print(details)
49+
}
50+
}

0 commit comments

Comments
 (0)