1+ name : Code Coverage
2+
3+ permissions :
4+ contents : read
5+
6+ on : [pull_request, workflow_dispatch]
7+
8+ jobs :
9+ coverage :
10+ runs-on : ubuntu-latest
11+ environment : azure-prod
12+ env :
13+ DATABRICKS_SERVER_HOSTNAME : ${{ secrets.DATABRICKS_HOST }}
14+ DATABRICKS_HTTP_PATH : ${{ secrets.TEST_PECO_WAREHOUSE_HTTP_PATH }}
15+ DATABRICKS_TOKEN : ${{ secrets.DATABRICKS_TOKEN }}
16+ DATABRICKS_CATALOG : peco
17+ DATABRICKS_USER : ${{ secrets.TEST_PECO_SP_ID }}
18+ steps :
19+ # ----------------------------------------------
20+ # check-out repo and set-up python
21+ # ----------------------------------------------
22+ - name : Check out repository
23+ uses : actions/checkout@v4
24+ with :
25+ fetch-depth : 0 # Needed for coverage comparison
26+ ref : ${{ github.event.pull_request.head.ref || github.ref_name }}
27+ repository : ${{ github.event.pull_request.head.repo.full_name || github.repository }}
28+ - name : Set up python
29+ id : setup-python
30+ uses : actions/setup-python@v5
31+ with :
32+ python-version : " 3.10"
33+ # ----------------------------------------------
34+ # ----- install & configure poetry -----
35+ # ----------------------------------------------
36+ - name : Install Poetry
37+ uses : snok/install-poetry@v1
38+ with :
39+ virtualenvs-create : true
40+ virtualenvs-in-project : true
41+ installer-parallel : true
42+
43+ # ----------------------------------------------
44+ # load cached venv if cache exists
45+ # ----------------------------------------------
46+ - name : Load cached venv
47+ id : cached-poetry-dependencies
48+ uses : actions/cache@v4
49+ with :
50+ path : .venv
51+ key : venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ github.event.repository.name }}-${{ hashFiles('**/poetry.lock') }}
52+ # ----------------------------------------------
53+ # install dependencies if cache does not exist
54+ # ----------------------------------------------
55+ - name : Install dependencies
56+ if : steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
57+ run : poetry install --no-interaction --no-root
58+ # ----------------------------------------------
59+ # install your root project, if required
60+ # ----------------------------------------------
61+ - name : Install library
62+ run : poetry install --no-interaction --all-extras
63+ # ----------------------------------------------
64+ # run all tests
65+ # ----------------------------------------------
66+ - name : Run tests with coverage
67+ continue-on-error : true
68+ run : |
69+ poetry run python -m pytest \
70+ tests/unit tests/e2e \
71+ --cov=src --cov-report=xml --cov-report=term -v
72+ # ----------------------------------------------
73+ # check for coverage override
74+ # ----------------------------------------------
75+ - name : Check for coverage override
76+ id : override
77+ run : |
78+ OVERRIDE_COMMENT=$(echo "${{ github.event.pull_request.body }}" | grep -E "SKIP_COVERAGE_CHECK\s*=" || echo "")
79+ if [ -n "$OVERRIDE_COMMENT" ]; then
80+ echo "override=true" >> $GITHUB_OUTPUT
81+ REASON=$(echo "$OVERRIDE_COMMENT" | sed -E 's/.*SKIP_COVERAGE_CHECK\s*=\s*(.+)/\1/')
82+ echo "reason=$REASON" >> $GITHUB_OUTPUT
83+ echo "Coverage override found in PR description: $REASON"
84+ else
85+ echo "override=false" >> $GITHUB_OUTPUT
86+ echo "No coverage override found"
87+ fi
88+ # ----------------------------------------------
89+ # check coverage percentage
90+ # ----------------------------------------------
91+ - name : Check coverage percentage
92+ if : steps.override.outputs.override == 'false'
93+ run : |
94+ COVERAGE_FILE="coverage.xml"
95+ if [ ! -f "$COVERAGE_FILE" ]; then
96+ echo "ERROR: Coverage file not found at $COVERAGE_FILE"
97+ exit 1
98+ fi
99+
100+ # Install xmllint if not available
101+ if ! command -v xmllint &> /dev/null; then
102+ sudo apt-get update && sudo apt-get install -y libxml2-utils
103+ fi
104+
105+ COVERED=$(xmllint --xpath "string(//coverage/@lines-covered)" "$COVERAGE_FILE")
106+ TOTAL=$(xmllint --xpath "string(//coverage/@lines-valid)" "$COVERAGE_FILE")
107+ PERCENTAGE=$(python3 -c "covered=${COVERED}; total=${TOTAL}; print(round((covered/total)*100, 2))")
108+
109+ echo "Branch Coverage: $PERCENTAGE%"
110+ echo "Required Coverage: 85%"
111+
112+ # Use Python to compare the coverage with 85
113+ python3 -c "import sys; sys.exit(0 if float('$PERCENTAGE') >= 85 else 1)"
114+ if [ $? -eq 1 ]; then
115+ echo "ERROR: Coverage is $PERCENTAGE%, which is less than the required 85%"
116+ exit 1
117+ else
118+ echo "SUCCESS: Coverage is $PERCENTAGE%, which meets the required 85%"
119+ fi
120+
121+ # ----------------------------------------------
122+ # coverage enforcement summary
123+ # ----------------------------------------------
124+ - name : Coverage enforcement summary
125+ run : |
126+ if [ "${{ steps.override.outputs.override }}" == "true" ]; then
127+ echo "⚠️ Coverage checks bypassed: ${{ steps.override.outputs.reason }}"
128+ echo "Please ensure this override is justified and temporary"
129+ else
130+ echo "✅ Coverage checks enforced - minimum 85% required"
131+ fi
0 commit comments