Skip to content

Commit 6bff0f2

Browse files
authored
fix: Align all coverage thresholds to 80% (#2162)
1 parent 03de43f commit 6bff0f2

6 files changed

Lines changed: 121 additions & 16 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
- name: Check per-service coverage
7373
if: github.event_name == 'pull_request'
7474
run: |
75-
chmod +x scripts/check-service-coverage.sh
75+
chmod +x scripts/check-service-coverage.sh scripts/codecov-exclude-pattern.sh
7676
./scripts/check-service-coverage.sh
7777
7878
- name: Run tests

.github/workflows/nightly.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,16 @@ jobs:
134134
--jsonfile test-results.json \
135135
-- -race -coverprofile=coverage/coverage.out -covermode=atomic -timeout 15m ./...
136136
137-
- name: Filter generated files from coverage
137+
- name: Filter excluded paths from coverage
138138
if: always()
139139
run: |
140140
if [ -f coverage/coverage.out ]; then
141-
grep -v -E '\.pb\.go|\.pb\.validate\.go|_grpc\.pb\.go' coverage/coverage.out > coverage/coverage-filtered.out || true
141+
exclude_pattern=$(./scripts/codecov-exclude-pattern.sh) || exclude_pattern=""
142+
if [ -n "$exclude_pattern" ]; then
143+
grep -v -E "$exclude_pattern" coverage/coverage.out > coverage/coverage-filtered.out || true
144+
else
145+
cp coverage/coverage.out coverage/coverage-filtered.out
146+
fi
142147
fi
143148
144149
- name: Upload coverage to Codecov

.github/workflows/test.yml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,13 @@ jobs:
167167
exit 0
168168
fi
169169
gocovmerge "${cov_files[@]}" > coverage/coverage.out
170-
echo "Filtering generated proto files from coverage..."
171-
grep -v -E '\.pb\.go|\.pb\.validate\.go|_grpc\.pb\.go' coverage/coverage.out > coverage/coverage-filtered.out || true
170+
echo "Filtering excluded paths from coverage (derived from codecov.yml)..."
171+
exclude_pattern=$(./scripts/codecov-exclude-pattern.sh) || exclude_pattern=""
172+
if [ -n "$exclude_pattern" ]; then
173+
grep -v -E "$exclude_pattern" coverage/coverage.out > coverage/coverage-filtered.out || true
174+
else
175+
cp coverage/coverage.out coverage/coverage-filtered.out
176+
fi
172177
173178
- name: Generate coverage report
174179
run: go tool cover -html=coverage/coverage.out -o coverage/coverage.html
@@ -193,9 +198,9 @@ jobs:
193198
- name: Check test coverage threshold
194199
run: |
195200
coverage=$(go tool cover -func=coverage/coverage-filtered.out | grep total | awk '{print $3}' | sed 's/%//')
196-
echo "Coverage (excluding generated proto files): ${coverage}%"
197-
if (( $(echo "$coverage < 70.0" | bc -l) )); then
198-
echo "❌ Coverage ${coverage}% is below 70% threshold"
201+
echo "Coverage (excluding codecov.yml ignore paths): ${coverage}%"
202+
if (( $(echo "$coverage < 80.0" | bc -l) )); then
203+
echo "❌ Coverage ${coverage}% is below 80% threshold"
199204
exit 1
200205
fi
201206
echo "✅ Coverage ${coverage}% meets threshold"

codecov.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ ignore:
1616
- "**/*_grpc.pb.go"
1717
- "**/mocks/**"
1818
- "**/testhelpers/**"
19+
- "**/cmd/**"
1920
- "utilities/**"
2021
- "frontend/src/api/gen/**"
21-
- "services/mcp-server/cmd/wire.go"
22-
- "**/cmd/main.go"
2322

2423
comment:
2524
layout: "condensed_header, condensed_files, components, condensed_footer"

scripts/check-service-coverage.sh

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,48 @@
44
# Runs unit tests (with -short to skip integration tests) for each Go service
55
# and fails if any service falls below the minimum coverage threshold.
66
#
7+
# Exclusions are read from codecov.yml (single source of truth).
8+
# This script converts codecov glob patterns to grep filters applied to
9+
# Go coverprofiles, so both Codecov and this script enforce the same rules.
10+
#
711
# Usage:
812
# ./scripts/check-service-coverage.sh
913
#
1014
# Environment variables:
11-
# COVERAGE_THRESHOLD Minimum coverage % required per service (default: 70)
15+
# COVERAGE_THRESHOLD Minimum coverage % required per service (default: 80)
1216

1317
set -euo pipefail
1418

1519
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
16-
THRESHOLD="${COVERAGE_THRESHOLD:-70}"
20+
THRESHOLD="${COVERAGE_THRESHOLD:-80}"
1721
TMPDIR="${TMPDIR:-/tmp}"
1822

19-
# Validate THRESHOLD is a non-negative integer in range 0100
23+
# Validate THRESHOLD is a non-negative integer in range 0-100
2024
if ! [[ "${THRESHOLD}" =~ ^[0-9]+$ ]] || [ "${THRESHOLD}" -gt 100 ]; then
2125
echo "ERROR: COVERAGE_THRESHOLD must be an integer between 0 and 100 (got: '${THRESHOLD}')" >&2
2226
exit 1
2327
fi
2428

29+
# Build exclude pattern from codecov.yml (single source of truth for exclusions).
30+
# See scripts/codecov-exclude-pattern.sh for the glob-to-grep conversion logic.
31+
EXCLUDE_PATTERN=""
32+
exclude_script="${REPO_ROOT}/scripts/codecov-exclude-pattern.sh"
33+
if [ -x "${exclude_script}" ]; then
34+
if exclude=$("${exclude_script}"); then
35+
EXCLUDE_PATTERN="${exclude}"
36+
echo "Exclude pattern (from codecov.yml): ${EXCLUDE_PATTERN}"
37+
else
38+
echo "WARNING: Failed to read exclusions from codecov.yml"
39+
fi
40+
else
41+
echo "WARNING: ${exclude_script} not found, running without exclusions"
42+
fi
43+
2544
FAILED=0
2645
PASSED=0
2746
SKIPPED=0
2847

48+
echo ""
2949
echo "Per-service Go coverage check (threshold: ${THRESHOLD}%)"
3050
echo "Using -short flag to skip integration tests"
3151
echo ""
@@ -70,13 +90,25 @@ for service_dir in "${REPO_ROOT}"/services/*/; do
7090
continue
7191
fi
7292

73-
if ! coverage="$(go tool cover -func="${coverage_file}" | awk '/^total:/ { gsub(/%/, "", $3); print $3 }')"; then
93+
# Filter coverprofile using exclusions derived from codecov.yml
94+
target_file="${coverage_file}"
95+
if [ -n "${EXCLUDE_PATTERN}" ]; then
96+
filtered_file="${TMPDIR}/meridian_coverage_${service}_filtered.out"
97+
head -1 "${coverage_file}" > "${filtered_file}"
98+
tail -n +2 "${coverage_file}" \
99+
| grep -v -E "${EXCLUDE_PATTERN}" \
100+
>> "${filtered_file}" || true
101+
rm -f "${coverage_file}"
102+
target_file="${filtered_file}"
103+
fi
104+
105+
if ! coverage="$(go tool cover -func="${target_file}" | awk '/^total:/ { gsub(/%/, "", $3); print $3 }')"; then
74106
echo " SKIP ${service} (cover command failed)"
75107
SKIPPED=$((SKIPPED + 1))
76-
rm -f "${coverage_file}"
108+
rm -f "${target_file}"
77109
continue
78110
fi
79-
rm -f "${coverage_file}"
111+
rm -f "${target_file}"
80112

81113
if [ -z "${coverage}" ]; then
82114
echo " SKIP ${service} (could not parse coverage)"

scripts/codecov-exclude-pattern.sh

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env bash
2+
# codecov-exclude-pattern.sh
3+
#
4+
# Reads codecov.yml ignore globs and outputs a grep -E pattern for filtering
5+
# Go coverprofile lines. This is the bridge that keeps the per-service coverage
6+
# script and CI workflow filters aligned with codecov.yml (single source of truth).
7+
#
8+
# Usage:
9+
# pattern=$(./scripts/codecov-exclude-pattern.sh)
10+
# grep -v -E "$pattern" coverage.out > coverage-filtered.out
11+
#
12+
# Handles three glob categories:
13+
# **/*.ext -> file extension match (\.ext:)
14+
# **/dir/** -> directory match (/dir/)
15+
# path/to/file -> exact path match (path/to/file:)
16+
#
17+
# Non-Go patterns (frontend/*, utilities/*) are skipped.
18+
19+
set -euo pipefail
20+
21+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
22+
CODECOV_YML="${1:-${REPO_ROOT}/codecov.yml}"
23+
24+
if [ ! -f "${CODECOV_YML}" ]; then
25+
echo "ERROR: codecov.yml not found at ${CODECOV_YML}" >&2
26+
exit 1
27+
fi
28+
29+
if ! command -v yq &>/dev/null; then
30+
echo "ERROR: yq is required but not found" >&2
31+
exit 1
32+
fi
33+
34+
patterns=()
35+
36+
while IFS= read -r glob; do
37+
case "${glob}" in
38+
# File extension: **/*.pb.go -> \.pb\.go:
39+
\*\*/\**)
40+
ext="${glob##*\*}"
41+
patterns+=("$(echo "${ext}" | sed 's/\./\\./g'):")
42+
;;
43+
# Directory: **/cmd/** -> /cmd/
44+
\*\*/*\*\*)
45+
dir="${glob#\*\*/}"
46+
dir="${dir%/\*\*}"
47+
patterns+=("/${dir}/")
48+
;;
49+
# Skip non-Go paths
50+
frontend/*|utilities/*) continue ;;
51+
# Specific file: path/to/file.go -> path/to/file\.go:
52+
*)
53+
patterns+=("$(echo "${glob}" | sed 's/\./\\./g'):")
54+
;;
55+
esac
56+
done < <(yq -r '.ignore[]' "${CODECOV_YML}" 2>/dev/null)
57+
58+
if [ ${#patterns[@]} -eq 0 ]; then
59+
echo "WARNING: No exclude patterns found in ${CODECOV_YML}" >&2
60+
exit 1
61+
fi
62+
63+
IFS='|'
64+
echo "${patterns[*]}"

0 commit comments

Comments
 (0)