-
Notifications
You must be signed in to change notification settings - Fork 7
214 lines (188 loc) · 7.04 KB
/
python-pip-test.yml
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
name: Pip Build
on:
workflow_call:
inputs:
package-name:
description: "The Python-importable package name to be tested"
required: true
type: string
python-version:
description: "The Python version to build and test with"
required: true
type: string
experimental:
description: "Mark this version as experimental and not required to pass"
required: false
default: false
type: boolean
deploy-on-success:
description: "Deploy to PyPI on success (and when appropriate)"
required: false
default: false
type: boolean
testing-extras:
default: ""
description: "Extra packages to be installed for testing"
required: false
type: string
ci-extras:
default: ""
description: "CI-specific packages to be installed"
required: false
type: string
system-packages:
default: ""
description: "CI-specific system packages required for installation"
required: false
type: string
outputs: {}
env:
MPLBACKEND: "agg"
QT_QPA_PLATFORM: "offscreen"
jobs:
test:
name: "Python ${{ inputs.python-version }}: pip"
runs-on: ubuntu-20.04
continue-on-error: ${{ inputs.experimental }}
defaults:
run:
# The following allows for each run step to utilize ~/.bash_profile
# for setting up the per-step initial state.
# --login: a login shell. Source ~/.bash_profile
# -e: exit on first error
# -o pipefail: piped processes are important; fail if they fail
shell: bash --login -eo pipefail {0}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: 'recursive'
- name: Check version to be built
run: |
# NOTE: If you run CI on your own fork, you may not have the right version
# number for the package. Synchronize your tags with the upstream,
# otherwise cross-dependencies may result in confusing build failure.
(echo "Package version: $(git describe --tags)" | tee "$GITHUB_STEP_SUMMARY") || \
echo "::warning::Git tags not found in repository. Build may fail!"
- name: Check environment variables for issues
run: |
echo "* Package to be built: ${{ inputs.package-name }}"
echo "* Pip 'extras' for CI testing: ${{ inputs.testing-extras }}"
echo "* General pip packages required for CI testing: ${{ inputs.ci-extras }}"
- name: Install required system packages
if: inputs.system-packages != ''
run: |
sudo apt-get update
sudo apt-get -y install ${{ inputs.system-packages }}
- name: Prepare for log files
run: |
mkdir $HOME/logs
- uses: actions/setup-python@v5
with:
python-version: '${{ inputs.python-version }}'
- name: Upgrade pip
run: |
pip install --upgrade pip
- name: Build wheel and source distribution
run: |
python -m pip install twine build
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
echo "Source date epoch set to ${SOURCE_DATE_EPOCH} for reproducible build"
# See: https://github.com/python/cpython/pull/5200
# And: https://reproducible-builds.org/docs/source-date-epoch/
# (I learned about this from DLS)
python -m build --sdist --wheel --outdir ./dist
- name: Check the source distribution
run: |
python -m venv test-source-env
source test-source-env/bin/activate
python -m pip install ./dist/*.gz
python -c "import ${{ inputs.package-name }}; print('Imported ${{ inputs.package-name }} successfully')"
- name: Use the wheel for testing
run: |
python -m pip install ./dist/*.whl
python -c "import ${{ inputs.package-name }}; print('Imported ${{ inputs.package-name }} successfully')"
- name: Installing CI extras and testing extras
run: |
# 1. escape '<' so the user doesn't have to
# 2. escape '>' so the user doesn't have to
# 3. allow conda/pip to use the same requirements spec;
# conda expects pkg=ver but pip expects pkg==ver; using a basic
# (not =<>)=(not =) to avoid incompatibility with macOS sed not supporting
# '=\+'
input_requirements=$(
echo "${{ inputs.ci-extras }} ${{ inputs.testing-extras }}" |
sed -e "s/</\</g" |
sed -e "s/>/\>/g" |
sed -e 's/\([^=<>]\)=\([^=]\)/\1==\2/g'
)
declare -a test_requirements=()
for req in $input_requirements; do
test_requirements+=( "$req" )
done
set -x
if [[ ${#test_requirements[@]} -gt 0 ]]; then
echo "CI extras: ${{ inputs.ci-extras }}"
echo "Testing extras: ${{ inputs.testing-extras }}"
pip install "${test_requirements[@]}" .[test]
else
echo "No extras to install."
pip install .[test]
fi
- name: Check the pip packages in the test env
run: |
pip list
- name: Run tests
run: |
pytest -v \
--log-file="$HOME/logs/debug_log.txt" \
--log-format='%(asctime)s.%(msecs)03d %(module)-15s %(levelname)-8s %(threadName)-10s %(message)s' \
--log-file-date-format='%H:%M:%S' \
--log-level=DEBUG \
2>&1 | tee "$HOME/logs/pytest_log.txt"
- name: After failure
if: ${{ failure() }}
run: |
# On failure:
# * Include the pip package details
# * Include the pytest log in the step summary (but not in the step output as it's available in the previous step)
# * Include the debug log in the step output (but not the step summary as it's too verbose)
(
echo "### Pip list"
echo "<details>"
echo ""
echo '```'
pip list | egrep -v -e "^#"
echo '```'
echo "</details>"
echo ""
echo "### Pytest log"
echo '```python'
cat "$HOME/logs/pytest_log.txt" || echo "# Pytest log not found?"
echo '```'
) >> "$GITHUB_STEP_SUMMARY"
echo "## Debug log"
cat "$HOME/logs/debug_log.txt" || echo "Debug logfile not found?"
- name: Upload log file artifacts
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: Python ${{ inputs.python-version }} - pip - testing log
path: "~/logs"
- name: Upload the package as an artifact
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: Python ${{ inputs.python-version }} - pip - package
path: dist
- name: PyPI deployment
if: inputs.deploy-on-success && github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: |
if [ -z "$TWINE_PASSWORD" ]; then
echo "# No PYPI_TOKEN secret in job!" | tee -a "$GITHUB_STEP_SUMMARY"
exit 1
fi
twine upload --verbose dist/*