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