Repository navigation
172 lines (145 loc) · 6.68 KB
/
Copy pathtest-python.yml
File metadata and controls
172 lines (145 loc) · 6.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# Reusable Job for Testing Python
name: Test Python
on:
workflow_call:
inputs:
# App Installation Settings #
installation_mode:
required: true
description: "Installation mode for the package. Options are 'edit', 'sdist', or 'wheel'"
type: string
# Upload build to CI Artifacts
upload_distro:
required: false
default: false
type: boolean
description: "Whether to upload built python distro to CI Artifacts"
# Trigger Behavior Settings #
default_trigger:
required: false
default: true
description: "Default trigger for the workflow. If true the 'default behavior' is to run on call. If false, the 'default behavior' is to be skipped."
type: boolean
override:
required: false
description: "Override the default trigger, 'true' gurantees run, 'false' not run"
type: string # IMPORTANT: must be string, to model 3 states {true, false, none}
# Environment Settings #
python_version:
required: false
default: '3.10'
description: "Python version to use for the job. Default is 3.10"
type: string
platform:
required: false
default: 'ubuntu-latest'
description: "Platform to use for the job. Default is 'ubuntu-latest'"
type: string
# Pytest Settings #
pytest_args:
required: false
description: "Arguments to pass to pytest. Pass value to override the default value"
type: string
jobs:
test:
if: inputs.override == 'true' || (inputs.override != 'false' && inputs.default_trigger == true)
runs-on: ${{ inputs.platform }}
steps:
- uses: actions/checkout@v4
- name: Set up Python ${ inputs.python_version }
uses: actions/setup-python@v5
with:
python-version: '${{ inputs.python_version }}'
# Install uv
- name: Install uv
if: inputs.platform != 'windows-latest'
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install the latest version of uv
if: inputs.platform == 'windows-latest'
uses: astral-sh/setup-uv@v5
with:
version: "latest"
- name: 'Export pinned Prod + Test dependencies'
run: uv export --no-emit-project --no-dev --extra test --frozen --format requirements-txt -o requirements.txt
# Install dependencies in virtualenv
- name: 'Install "Prod + Test" dependencies'
shell: bash
run: |
uv venv
if [[ "${{ matrix.platform }}" == "windows-latest" ]]; then
. .venv/Scripts/activate
fi
echo "[INFO] Installing exact 'Prod + Test' Dependencies"
uv pip install --no-deps -r requirements.txt
# here the .venv is built with the exact dependencies
#### [IF] Build Sdist and Wheel Distributions ####
- name: 'Build SDist and Wheel Distributions if inputs.installation_mode in [sdist, wheel]'
if: contains(fromJSON('["sdist", "wheel"]'), inputs.installation_mode)
run: uv build --out-dir dist
## Install Package in 'edit' mode or from Sdist/Wheel Distribution file ##
- name: 'Install Package Wheel, without dependencies'
shell: bash
run: |
if [[ "${{ matrix.platform }}" == "windows-latest" ]]; then
. .venv/Scripts/activate
fi
uv pip install --no-deps ${{ inputs.installation_mode == 'edit' && '-e .' || inputs.installation_mode == 'sdist' && 'dist/*.tar.gz' || inputs.installation_mode == 'wheel' && 'dist/*.whl' }}
- name: Compute Pytest default Arguments
id: pytest_args
run: echo DEFAULT_PYTEST_ARGS='-ra --run-requires_uv --run-network_bound -vvs --cov --cov-report=term-missing --cov-report=html:test-${{ inputs.installation_mode }}/htmlcov --cov-context=test --cov-report=xml:coverage.test-${{ inputs.installation_mode }}.xml -n auto tests' >> $GITHUB_OUTPUT
#### Run Test Suite against the package and measure Coverage ####
- name: Run Tests and measure Coverage
shell: bash
env:
# log deletion post hook fails on windows, due to permission error! (other process is using the file, so removing is denied)
# windows spawn multiple processes, so log deletion is not possible, even when running 1 Single Unit Test
BUG_LOG_DEL_WIN: 'permission_error' # required on Windows Job
PY_WHEEL: 1 # required on Windows Job
COVERAGE_FILE: '.coverage.${{ inputs.installation_mode }}'
PYTEST_ARGS: "${{ inputs.pytest_args || steps.pytest_args.outputs.DEFAULT_PYTEST_ARGS }}"
run: |
if [[ "${{ matrix.platform }}" == "windows-latest" ]]; then
echo "[INFO] Activating virtual environment for Windows"
. .venv/Scripts/activate
else
# Linux and MacOS
echo "[INFO] Activating virtual environment for Linux/MacOS"
. .venv/bin/activate
fi
pytest ${{ env.PYTEST_ARGS }}
## Code Coverage ## TODO; enable combine by moving each .coverage file to other dir to avoid override of subsequent coverage invocations with pytest
- name: "Discover and Combine Coverage data into XML Reports"
env:
COVERAGE_FILE: .coverage
OUTPUT_XML: coverage-${{ inputs.platform }}-${{ inputs.python_version }}-${{ inputs.installation_mode }}.xml
shell: bash
run: |
uv venv cov-env
if [[ "${{ matrix.platform }}" == "windows-latest" ]]; then
. cov-env/Scripts/activate
else
# Linux and MacOS
. cov-env/bin/activate
fi
uv pip install 'coverage[toml] >= 5.1' 'diff_cover >=6'
echo '[INFO] Running coverage'
uv run --active coverage combine --keep
uv run --active coverage report --skip-covered --show-missing -i
uv run --active coverage xml -o ./${{ env.OUTPUT_XML }} -i
# uv run --active coverage html -d ./htmlcov -i
echo "OUTPUT_XML=${{ env.OUTPUT_XML }}" >> $GITHUB_OUTPUT
id: coverage
- name: "Upload Test Coverage as Artifacts"
uses: actions/upload-artifact@v4
with:
overwrite: false
name: '${{ steps.coverage.outputs.OUTPUT_XML }}'
path: '${{ steps.coverage.outputs.OUTPUT_XML }}'
if-no-files-found: error
- name: Upload Source & Wheel distributions as Artefacts
if: contains(fromJSON('["sdist", "wheel"]'), inputs.installation_mode) && inputs.upload_distro
uses: actions/upload-artifact@v4
with:
name: dist-${{ inputs.platform }}-${{ inputs.python_version }}
path: dist
if-no-files-found: error