Skip to content

Commit 9554985

Browse files
adiroibanaltendky
andauthored
Generate coverage reports using only GitHub Actions, without codecov.io (#447)
* Initial coverage report. * Enable for windows. * Try to support windows paths for coverage. * Try the branch. * Update all-success to newer version that as no set-output warnings. * Add a comment. * Pass comment via a file to handle newlines. * Use nodejs like a pro. * Apply suggestions from code review Co-authored-by: Kyle Altendorf <[email protected]> * Include diff. Wrap in markdown. * Enable test report. * Fix after re-review. * Remove run_id from coverage artifact. * Add a name for the comment script. * Remove comment about single artifact as this is what we have by default for a workflow. * Add restricted permissions for token. * Use markdown report for diff-cover. Co-authored-by: Kyle Altendorf <[email protected]>
1 parent 827d3b9 commit 9554985

File tree

7 files changed

+172
-7
lines changed

7 files changed

+172
-7
lines changed

.github/CODEOWNERS

Lines changed: 0 additions & 2 deletions
This file was deleted.

.github/scripts/pr_comment.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
Have a single comment on a PR, identified by a comment marker.
3+
4+
Create a new comment if no comment already exists.
5+
Update the content of the existing comment.
6+
7+
8+
https://octokit.github.io/rest.js/v19
9+
*/
10+
module.exports = async ({github, context, process}) => {
11+
var octokit_rest = github
12+
if (context.eventName != "pull_request") {
13+
// Only PR are supported.
14+
return
15+
}
16+
17+
var sleep = function(second) {
18+
return new Promise(resolve => setTimeout(resolve, second * 1000))
19+
}
20+
21+
/*
22+
Perform the actual logic.
23+
24+
This is wrapped so that we can retry on errors.
25+
*/
26+
var doAction = async function() {
27+
28+
console.log(context)
29+
30+
fs = require('fs');
31+
32+
const body = fs.readFileSync(
33+
process.env.GITHUB_WORKSPACE + "/" + process.env.COMMENT_BODY, 'utf8');
34+
var comment_id = null
35+
var comment_marker = '\n' + process.env.COMMENT_MARKER
36+
var comment_body = body + comment_marker
37+
38+
var comments = await octokit_rest.issues.listComments({
39+
owner: context.repo.owner,
40+
repo: context.repo.repo,
41+
issue_number: context.payload.number,
42+
})
43+
44+
console.log(comments)
45+
46+
comments.data.forEach(comment => {
47+
if (comment.body.endsWith(comment_marker)) {
48+
comment_id = comment.id
49+
}
50+
})
51+
52+
if (comment_id) {
53+
// We have an existing comment.
54+
// update the content.
55+
await octokit_rest.issues.updateComment({
56+
owner: context.repo.owner,
57+
repo: context.repo.repo,
58+
comment_id: comment_id,
59+
body: comment_body,
60+
})
61+
return
62+
}
63+
64+
// Create a new comment.
65+
await octokit_rest.issues.createComment({
66+
owner: context.repo.owner,
67+
repo: context.repo.repo,
68+
issue_number: context.payload.number,
69+
body: comment_body,
70+
})
71+
72+
}
73+
74+
try {
75+
await doAction()
76+
} catch (e) {
77+
await sleep(5)
78+
await doAction()
79+
}
80+
}

.github/workflows/ci.yml

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
tags: [ "**" ]
77
pull_request:
88

9+
permissions:
10+
contents: read
11+
912
defaults:
1013
run:
1114
shell: bash
@@ -96,8 +99,18 @@ jobs:
9699

97100
- run: nox --python ${{ matrix.python.action }} -e ${{ matrix.task.nox }} -- --use-wheel dist/*.whl
98101

102+
- name: Store coverage file
103+
uses: actions/upload-artifact@v3
104+
with:
105+
name: coverage
106+
# It is important to keep the unique names for the coverage files
107+
# so that when uploaded to the same artifact, they don't overlap.
108+
path: .coverage*
109+
99110
- name: Codecov
100111
run: |
112+
ls -al .coverage*
113+
coverage combine
101114
codecov -n "GitHub Actions - ${{ matrix.task.name}} - ${{ matrix.os.name }} ${{ matrix.python.name }}"
102115
103116
@@ -137,8 +150,18 @@ jobs:
137150

138151
- run: nox --python ${{ matrix.python.action }} -e ${{ matrix.task.nox }} -- --use-wheel dist/*.whl
139152

153+
- name: Store coverage file
154+
uses: actions/upload-artifact@v3
155+
with:
156+
name: coverage
157+
# It is important to keep the unique names for the coverage files
158+
# so that when uploaded to the same artifact, they don't overlap.
159+
path: .coverage*
160+
140161
- name: Codecov
141162
run: |
163+
ls -al .coverage*
164+
coverage combine
142165
codecov -n "GitHub Actions - ${{ matrix.task.name}} - ${{ matrix.os.name }} ${{ matrix.python.name }}"
143166
144167
check:
@@ -232,6 +255,65 @@ jobs:
232255
password: ${{ secrets.PYPI_TOKEN }}
233256
verbose: true
234257

258+
coverage-report:
259+
name: Coverage report
260+
runs-on: ubuntu-latest
261+
permissions:
262+
# Even we send a comment to a PR, we use the issues API.
263+
# Issues and PR share the same comment API.
264+
issues: write
265+
needs:
266+
# We are waiting only for test jobs.
267+
- test-linux
268+
- test-windows
269+
steps:
270+
- uses: actions/checkout@v3
271+
with:
272+
fetch-depth: 0
273+
274+
- name: Install dependencies
275+
run: |
276+
python -m pip install --upgrade pip
277+
python -m pip install --upgrade coverage[toml] diff_cover
278+
279+
- name: Download coverage reports
280+
uses: actions/download-artifact@v3
281+
with:
282+
name: coverage
283+
path: .
284+
285+
- name: Combine coverage
286+
run: coverage combine
287+
288+
- name: Report coverage
289+
run: |
290+
coverage xml
291+
coverage report --no-skip-covered --show-missing
292+
# Wrap output in markdown verbatim text.
293+
echo '```' > coverage-report.txt
294+
coverage report --show-missing >> coverage-report.txt
295+
echo '```' >> coverage-report.txt
296+
diff-cover --markdown-report diff-cover.md --compare-branch origin/trunk coverage.xml
297+
cat diff-cover.md >> coverage-report.txt
298+
299+
# Use the generic JS script to call our custom script
300+
# for sending a comment to a PR.
301+
- name: Send coverage comment to PR
302+
uses: actions/github-script@v3
303+
env:
304+
COMMENT_MARKER: "<!--- automatic-coverage-report -->"
305+
COMMENT_BODY: coverage-report.txt
306+
with:
307+
script: |
308+
const script = require(`${process.env.GITHUB_WORKSPACE}/.github/scripts/pr_comment.js`)
309+
// Only pass top level objects as GHA does dependecy injection.
310+
await script({github, context, process})
311+
312+
- name: Enforce diff coverage
313+
run: |
314+
diff-cover --fail-under=100 --compare-branch origin/trunk coverage.xml
315+
316+
235317
# This is a meta-job to simplify PR CI enforcement configuration in GitHub.
236318
# Inside the GitHub config UI you only configure this job as required.
237319
# All the extra requirements are defined "as code" as part of the `needs`
@@ -250,8 +332,9 @@ jobs:
250332
- test-windows
251333
- check
252334
- pypi-publish
335+
- coverage-report
253336
steps:
254337
- name: Require all successes
255-
uses: re-actors/alls-green@3a2de129f0713010a71314c74e33c0e3ef90e696
338+
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe
256339
with:
257340
jobs: ${{ toJSON(needs) }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ dist/
1111
venv/
1212
htmlcov/
1313
.coverage
14+
coverage.xml
1415
*~
1516
*.lock
1617
apidocs/

noxfile.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ def tests(session: nox.Session) -> None:
3636
session.run("coverage", "run", "--module", "twisted.trial", *posargs)
3737

3838
if os.environ.get("CI") != "true":
39+
# When running the test locally, show the coverage report.
3940
session.notify("coverage_report")
40-
else:
41-
session.run("coverage", "combine")
4241

4342

4443
@nox.session

pyproject.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ branch = true
9191
source = ["towncrier"]
9292

9393
[tool.coverage.paths]
94-
source = ["src", ".nox/*/site-packages"]
94+
source = [
95+
"src",
96+
"*.nox/*/site-packages",
97+
# required until coverage 6.6.0 is used: https://github.com/nedbat/coveragepy/issues/991
98+
"*.nox\\*\\*\\site-packages"
99+
]
95100

96101
[tool.coverage.report]
97102
show_missing = true
@@ -102,5 +107,4 @@ exclude_lines = [
102107
]
103108
omit = [
104109
"src/towncrier/__main__.py",
105-
"src/towncrier/test/*",
106110
]

src/towncrier/newsfragments/410.misc

Whitespace-only changes.

0 commit comments

Comments
 (0)