Skip to content

Commit a165cde

Browse files
committed
Compute framework coverage diff in artifacts job
1 parent d6361d8 commit a165cde

File tree

7 files changed

+234
-242
lines changed

7 files changed

+234
-242
lines changed

.github/workflows/csv-coverage-pr-artifacts.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,23 @@ jobs:
4949
gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip"
5050
- name: Unzip CodeQL CLI
5151
run: unzip -d codeql-cli codeql-linux64.zip
52-
- name: Generate CSV files on merge and base of the PR
52+
- name: Generate CSV files on merge commit of the PR
5353
run: |
5454
echo "Running generator on merge"
5555
PATH="$PATH:codeql-cli/codeql" python merge/misc/scripts/library-coverage/generate-report.py ci merge merge
5656
mkdir out_merge
5757
cp framework-coverage-*.csv out_merge/
5858
cp framework-coverage-*.rst out_merge/
59-
59+
- name: Generate CSV files on base commit of the PR
60+
run: |
6061
echo "Running generator on base"
6162
PATH="$PATH:codeql-cli/codeql" python base/misc/scripts/library-coverage/generate-report.py ci base base
6263
mkdir out_base
6364
cp framework-coverage-*.csv out_base/
6465
cp framework-coverage-*.rst out_base/
66+
- name: Generate diff of coverage reports
67+
run: |
68+
python base/misc/scripts/library-coverage/compare-folders.py out_base out_merge comparison.md
6569
- name: Upload CSV package list
6670
uses: actions/upload-artifact@v2
6771
with:
@@ -76,6 +80,12 @@ jobs:
7680
path: |
7781
out_base/framework-coverage-*.csv
7882
out_base/framework-coverage-*.rst
83+
- name: Upload comparison results
84+
uses: actions/upload-artifact@v2
85+
with:
86+
name: comparison
87+
path: |
88+
comparison.md
7989
- name: Save PR number
8090
run: |
8191
mkdir -p pr

.github/workflows/csv-coverage-pr-comment.yml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,4 @@ jobs:
3131
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3232
RUN_ID: ${{ github.event.workflow_run.id }}
3333
run: |
34-
python misc/scripts/library-coverage/compare-files-comment-pr.py \
35-
comparison.md "$GITHUB_REPOSITORY" "$RUN_ID"
36-
- name: Upload comparison results
37-
uses: actions/upload-artifact@v2
38-
with:
39-
name: comparison
40-
path: |
41-
comparison.md
34+
python misc/scripts/library-coverage/comment-pr.py "$GITHUB_REPOSITORY" "$RUN_ID"
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import sys
2+
import os
3+
import utils
4+
import shutil
5+
import json
6+
import filecmp
7+
8+
"""
9+
This script compares the generated CSV coverage files with the ones in the codebase.
10+
"""
11+
12+
artifacts_workflow_name = "Check framework coverage changes"
13+
comparison_artifact_name = "comparison"
14+
comparison_artifact_file_name = "comparison.md"
15+
16+
17+
def get_comment_text(output_file, repo, run_id):
18+
size = os.path.getsize(output_file)
19+
if size == 0:
20+
print("No difference in the coverage reports")
21+
return
22+
23+
comment = ":warning: The head of this PR and the base branch were compared for differences in the framework coverage reports. " + \
24+
f"The generated reports are available in the [artifacts of this workflow run](https://github.com/{repo}/actions/runs/{run_id}). " + \
25+
"The differences will be picked up by the nightly job after the PR gets merged. "
26+
27+
if size < 2000:
28+
print("There's a small change in the CSV framework coverage reports")
29+
comment += "The following differences were found: \n\n"
30+
with open(output_file, 'r') as file:
31+
comment += file.read()
32+
else:
33+
print("There's a large change in the CSV framework coverage reports")
34+
comment += f"The differences can be found in the {comparison_artifact_name} artifact of this workflow run](https://github.com/{repo}/actions/runs/{run_id})."
35+
36+
return comment
37+
38+
39+
def comment_pr(repo, run_id):
40+
"""
41+
Generates coverage diff produced by the changes in the current PR. If the diff is not empty, then post it as a comment.
42+
If a workflow run produces the same diff as the directly preceeding one, then don't post a comment.
43+
"""
44+
45+
# Store diff for current run
46+
current_diff_folder = "current_diff"
47+
utils.download_artifact(repo, comparison_artifact_name,
48+
current_diff_folder, run_id)
49+
50+
utils.download_artifact(repo, "pr", "pr", run_id)
51+
52+
try:
53+
with open("pr/NR") as file:
54+
pr_number = int(file.read())
55+
finally:
56+
if os.path.isdir("pr"):
57+
shutil.rmtree("pr")
58+
59+
# Try storing diff for previous run:
60+
try:
61+
prev_run_id = get_previous_run_id(repo, run_id, pr_number)
62+
prev_diff_folder = "prev_diff"
63+
utils.download_artifact(repo, comparison_artifact_name,
64+
prev_diff_folder, prev_run_id)
65+
66+
if filecmp.cmp(f"{current_diff_folder}/{comparison_artifact_file_name}", f"{prev_diff_folder}/{comparison_artifact_file_name}", shallow=False):
67+
print(
68+
f"Previous run {prev_run_id} resulted in the same diff, so not commenting again.")
69+
return
70+
else:
71+
print(f"Diff of previous run {prev_run_id} differs, commenting.")
72+
except Exception:
73+
# this is not mecessarily a failure, it can also mean that there was no previous run yet.
74+
print("Couldn't generate diff for previous run:", sys.exc_info()[1])
75+
76+
comment = get_comment_text(
77+
f"{current_diff_folder}/{comparison_artifact_file_name}", repo, run_id)
78+
post_comment(comment, repo, pr_number)
79+
80+
81+
def post_comment(comment, repo, pr_number):
82+
print(f"Posting comment to PR #{pr_number}")
83+
utils.subprocess_run(["gh", "pr", "comment", str(pr_number),
84+
"--repo", repo, "--body", comment])
85+
86+
87+
def get_previous_run_id(repo, run_id, pr_number):
88+
"""
89+
Gets the previous run id for a given workflow run, considering that the previous workflow run needs to come from the same PR.
90+
"""
91+
92+
# Get branch and repo from run:
93+
this_run = utils.subprocess_check_output(
94+
["gh", "api", "-X", "GET", f"repos/{repo}/actions/runs/{run_id}", "--jq", "{ head_branch: .head_branch, head_repository: .head_repository.full_name }"])
95+
96+
this_run = json.loads(this_run)
97+
pr_branch = this_run["head_branch"]
98+
pr_repo = this_run["head_repository"]
99+
100+
# Get all previous runs that match branch, repo and workflow name:
101+
ids = utils.subprocess_check_output(["gh", "api", "-X", "GET", f"repos/{repo}/actions/runs", "-f", "event=pull_request", "-f", "status=success", "-f", "name=\"" + artifacts_workflow_name + "\"", "--jq",
102+
f"[.workflow_runs.[] | select(.head_branch==\"{pr_branch}\" and .head_repository.full_name==\"{pr_repo}\") | {{ created_at: .created_at, run_id: .id}}] | sort_by(.created_at) | reverse | [.[].run_id]"])
103+
104+
ids = json.loads(ids)
105+
if ids[0] != int(run_id):
106+
raise Exception(
107+
f"Expected to find {run_id} in the list of matching runs.")
108+
109+
for previous_run_id in ids[1:]:
110+
utils.download_artifact(repo, "pr", "prev_run_pr", previous_run_id)
111+
112+
try:
113+
with open("prev_run_pr/NR") as file:
114+
prev_pr_number = int(file.read())
115+
print(f"PR number: {prev_pr_number}")
116+
finally:
117+
if os.path.isdir("prev_run_pr"):
118+
shutil.rmtree("prev_run_pr")
119+
120+
# the previous run needs to be coming from the same PR:
121+
if pr_number == prev_pr_number:
122+
return previous_run_id
123+
124+
raise Exception("Couldn't find previous run.")
125+
126+
127+
repo = sys.argv[1]
128+
run_id = sys.argv[2]
129+
130+
comment_pr(repo, run_id)

0 commit comments

Comments
 (0)