Skip to content

Commit c08ad17

Browse files
frozenspiderWiseMrMusa
authored andcommitted
Cache llvm-cov tool (#34)
Installing `cargo-llvm-cov` each time takes a long time and is wasteful. Cache it the same way we cache `cargo-deny`. | GH Action | Time before | Time after | | ------------- | ------------- | ------------- | | Coverage (PR) | [166 seconds](https://github.com/NethermindEth/rust-template/actions/runs/19867825551) | [38 seconds](https://github.com/NethermindEth/rust-template/actions/runs/20507497399) | | Coverage (Push) | [87 seconds](https://github.com/NethermindEth/rust-template/actions/runs/19242367930) |[ 18 seconds](https://github.com/NethermindEth/rust-template/actions/runs/20507478141) |
1 parent d57c99d commit c08ad17

2 files changed

Lines changed: 190 additions & 2 deletions

File tree

.github/workflows/coverage-pr.yml

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
name: Coverage (PR)
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
actions: read
11+
12+
jobs:
13+
cov-head:
14+
runs-on: ubuntu-latest
15+
outputs:
16+
pct: ${{ steps.cov.outputs.coverage }}
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
ref: ${{ github.event.pull_request.head.sha }}
21+
22+
- name: Cache llvm-cov build
23+
id: cache-llvm-cov
24+
uses: actions/cache@v4
25+
continue-on-error: false
26+
with:
27+
path: |
28+
~/.cargo/bin/
29+
~/.cargo/registry/index/
30+
~/.cargo/registry/cache/
31+
~/.cargo/git/db/
32+
target/
33+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
34+
restore-keys: ${{ runner.os }}-cargo-
35+
36+
- run: which cargo-llvm-cov || cargo install cargo-llvm-cov
37+
- run: rustup component add llvm-tools-preview
38+
- run: cargo llvm-cov --workspace --lcov --output-path lcov.info --ignore-filename-regex '^examples/'
39+
40+
- name: Upload head coverage artifact (for stacked PRs)
41+
uses: actions/upload-artifact@v4
42+
with:
43+
name: coverage-lcov
44+
path: lcov.info
45+
retention-days: 21
46+
47+
- name: Extract total coverage % (head)
48+
id: cov
49+
shell: bash
50+
run: |
51+
pct=$(awk -F: '
52+
/^LH:/ {lh += $2}
53+
/^LF:/ {lf += $2}
54+
END { if (lf>0) printf "%.2f", (lh/lf)*100; else print "0.00" }
55+
' lcov.info)
56+
echo "coverage=$pct" >> "$GITHUB_OUTPUT"
57+
58+
compare-and-comment:
59+
needs: cov-head
60+
runs-on: ubuntu-latest
61+
steps:
62+
# 1) Try to download base artifact from the base branch
63+
- name: Download base branch artifact (if present)
64+
id: dl
65+
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5
66+
with:
67+
name: coverage-lcov
68+
branch: ${{ github.event.pull_request.base.ref }}
69+
if_no_artifact_found: ignore # don't fail if missing
70+
71+
- name: Check if base artifact was found
72+
id: base_art
73+
shell: bash
74+
run: |
75+
if [ -f "coverage-lcov/lcov.info" ]; then
76+
echo "found=true" >> "$GITHUB_OUTPUT"
77+
else
78+
echo "found=false" >> "$GITHUB_OUTPUT"
79+
fi
80+
81+
# 2) Fallback: if no artifact, checkout base commit and compute base coverage here
82+
- name: Checkout base
83+
if: ${{ steps.base_art.outputs.found == 'false' }}
84+
uses: actions/checkout@v4
85+
with:
86+
ref: ${{ github.event.pull_request.base.sha }}
87+
88+
- name: Cache llvm-cov build
89+
id: cache-llvm-cov
90+
if: ${{ steps.base_art.outputs.found == 'false' }}
91+
uses: actions/cache@v4
92+
continue-on-error: false
93+
with:
94+
path: |
95+
~/.cargo/bin/
96+
~/.cargo/registry/index/
97+
~/.cargo/registry/cache/
98+
~/.cargo/git/db/
99+
target/
100+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
101+
restore-keys: ${{ runner.os }}-cargo-
102+
103+
- name: Install cargo-llvm-cov (fallback path)
104+
if: ${{ steps.base_art.outputs.found == 'false' }}
105+
run: which cargo-llvm-cov || cargo install cargo-llvm-cov
106+
107+
- name: Install llvm-tools (fallback path)
108+
if: ${{ steps.base_art.outputs.found == 'false' }}
109+
run: rustup component add llvm-tools-preview
110+
111+
- name: Run base coverage (fallback path)
112+
if: ${{ steps.base_art.outputs.found == 'false' }}
113+
run: cargo llvm-cov --workspace --lcov --output-path base.lcov.info --ignore-filename-regex '^examples/'
114+
115+
# 3) Extract base coverage (from artifact or from freshly computed fallback)
116+
- name: Extract total coverage % (base)
117+
id: basecov
118+
shell: bash
119+
run: |
120+
file="coverage-lcov/lcov.info"
121+
if [ "${{ steps.base_art.outputs.found }}" != "true" ]; then
122+
file="base.lcov.info"
123+
fi
124+
pct=$(awk -F: '
125+
/^LH:/ {lh += $2}
126+
/^LF:/ {lf += $2}
127+
END { if (lf>0) printf "%.2f", (lh/lf)*100; else print "0.00" }
128+
' "$file")
129+
echo "coverage=$pct" >> "$GITHUB_OUTPUT"
130+
131+
- name: Find existing coverage comment
132+
id: find_comment
133+
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e
134+
with:
135+
issue-number: ${{ github.event.pull_request.number }}
136+
comment-author: 'github-actions[bot]'
137+
body-includes: '<!-- coverage-bot -->'
138+
139+
- name: Comment only if head < base
140+
uses: actions/github-script@v7
141+
with:
142+
script: |
143+
const base = parseFloat(`${{ steps.basecov.outputs.coverage }}`);
144+
const head = parseFloat(`${{ needs.cov-head.outputs.pct }}`);
145+
if (!(base >= 0) || !(head >= 0)) {
146+
core.setFailed(`Bad coverage values. base=${base} head=${head}`);
147+
return;
148+
}
149+
if (head >= base) {
150+
core.info(`No drop (head ${head}% >= base ${base}%).`);
151+
return;
152+
}
153+
const delta = (head - base).toFixed(2);
154+
const body = `<!-- coverage-bot -->
155+
**Coverage (base → head):** \`${base}% → ${head}%\` ⬇️ ${Math.abs(delta)} pp
156+
_Only posts when PR coverage is lower than the base branch's latest push (or freshly computed base)._
157+
`;
158+
const commentId = `${{ steps.find_comment.outputs.comment-id }}`;
159+
if (commentId) {
160+
await github.rest.issues.updateComment({
161+
owner: context.repo.owner,
162+
repo: context.repo.repo,
163+
comment_id: parseInt(commentId, 10),
164+
body
165+
});
166+
} else {
167+
await github.rest.issues.createComment({
168+
owner: context.repo.owner,
169+
repo: context.repo.repo,
170+
issue_number: context.issue.number,
171+
body
172+
});
173+
}

.github/workflows/coverage.yml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,23 @@ jobs:
1717
steps:
1818
- uses: actions/checkout@v4
1919

20-
- name: Install cargo-llvm-cov
21-
run: cargo install cargo-llvm-cov
20+
- name: Cache llvm-cov build
21+
id: cache-llvm-cov
22+
uses: actions/cache@v4
23+
continue-on-error: false
24+
with:
25+
path: |
26+
~/.cargo/bin/
27+
~/.cargo/registry/index/
28+
~/.cargo/registry/cache/
29+
~/.cargo/git/db/
30+
target/
31+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
32+
restore-keys: ${{ runner.os }}-cargo-
33+
34+
- run: which cargo-llvm-cov || cargo install cargo-llvm-cov
35+
- run: rustup component add llvm-tools-preview
36+
- run: cargo llvm-cov --workspace --lcov --output-path lcov.info --ignore-filename-regex '^examples/'
2237

2338
- name: Install llvm-tools
2439
run: rustup component add llvm-tools-preview

0 commit comments

Comments
 (0)