Repository navigation
188 lines (172 loc) · 9.76 KB
/
Copy pathpython-tests.yml
File metadata and controls
188 lines (172 loc) · 9.76 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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
name: python-tests
on:
pull_request:
branches: [master, 'release/**', 'fix/bad-epoll-security-audit', 'port/**']
types: [opened, synchronize, reopened, labeled]
push:
branches: [master, 'release/**']
paths:
- 'bench/**'
- 'cmax/**'
- 'pyproject.toml'
- 'setup.py'
- 'tests/**'
- '.github/scripts/**'
- '.github/requirements/**'
- 'docs/releases/**'
- 'tools/check_public_data.py'
- 'docs/public-data-policy.md'
- '.github/workflows/python-nightly.yml'
- '.github/workflows/python-tests.yml'
- '.github/workflows/minimum-versions-refresh.yml'
schedule:
- cron: '41 6 * * *'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: python-tests-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
fetch-depth: 0
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
with:
python-version: '3.12'
cache: pip
cache-dependency-path: |
pyproject.toml
.github/requirements/minimum-versions.txt
- name: Install pinned pip
run: python -m pip install "pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70"
- name: Select tests
id: select
env:
EVENT: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
FULL_LABEL: ${{ contains(github.event.pull_request.labels.*.name, 'full-tests') }}
run: |
if [ "$EVENT" != pull_request ] || [ "$FULL_LABEL" = true ]; then
selection=ALL
else
selection=$(python .github/scripts/select_tests.py --base "$BASE_SHA")
fi
printf '%s\n' "${selection:-<none>}" | tee -a "$GITHUB_STEP_SUMMARY"
{
echo 'tests<<EOF'
if [ "$selection" != ALL ]; then printf '%s\n' "$selection"; fi
echo EOF
} >> "$GITHUB_OUTPUT"
echo "all=$([ "$selection" = ALL ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
echo "none=$([ -z "$selection" ] && echo true || echo false)" >> "$GITHUB_OUTPUT"
if [ "$EVENT" != pull_request ] || [ "$selection" = ALL ]; then
package=true
else
package=$(git diff --name-only "$BASE_SHA...HEAD" | grep -qE '^(cmax/|pyproject\.toml$|setup\.py$)' && echo true || echo false)
fi
echo "package=$package" >> "$GITHUB_OUTPUT"
# Lockfile and release build checks run for any change that can alter
# the release toolchain. Dependabot does not follow the `uv pip compile`
# header (for example, it drops setuptools), so these checks keep such
# a lockfile out of master.
if [ "$EVENT" != pull_request ]; then
requirements=true
else
requirements=$(git diff --name-only "$BASE_SHA...HEAD" | grep -qE '^(\.github/requirements/|pyproject\.toml$|setup\.py$|\.github/workflows/(python-nightly|python-tests)\.yml$)' && echo true || echo false)
fi
echo "requirements=$requirements" >> "$GITHUB_OUTPUT"
- name: Install test runner
if: steps.select.outputs.none != 'true' || steps.select.outputs.package == 'true'
run: python -m pip install pytest pytest-xdist pyyaml prompt-toolkit
- name: Check public data policy
run: python tools/check_public_data.py
- name: Check that lockfiles match their uv pip compile output
if: steps.select.outputs.requirements == 'true'
run: |
python -m venv /tmp/clustermax-uv
/tmp/clustermax-uv/bin/python -m pip install --disable-pip-version-check "uv @ https://files.pythonhosted.org/packages/4b/a1/de02c0913726728eca56ee258875cbba9c7b0572c63f494fd9f55c7852e3/uv-0.13.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=05e4332791b4d55b6dde8695b7fa6079b26991519f44760887c5dfbd5ca4fe53"
# The existing pins are kept as preferences, so this only fails when a
# lockfile does not match its .in file or was made by another tool.
for name in release minimum-versions; do
/tmp/clustermax-uv/bin/uv pip compile ".github/requirements/${name}.in" \
--output-file ".github/requirements/${name}.txt" \
--generate-hashes --python-version 3.12 \
--python-platform x86_64-manylinux2014 --only-binary :all: -q
done
if ! git diff --exit-code -- .github/requirements/; then
echo '::error::A lockfile in .github/requirements does not match uv pip compile output. Run the command in the file header and commit the result.'
exit 1
fi
- name: Check that setuptools pins agree
if: steps.select.outputs.requirements == 'true'
shell: python
run: |
import re, sys, tomllib
from pathlib import Path
def pin(lines, where):
found = [m.group(1) for line in lines if (m := re.fullmatch(r'\s*setuptools==([^\s\\;]+).*', line))]
if len(found) != 1:
sys.exit(f'::error::{where} must pin setuptools exactly once, found {found}')
return found[0]
requires = tomllib.loads(Path('pyproject.toml').read_text())['build-system']['requires']
pins = {
'pyproject.toml build-system.requires': pin(requires, 'pyproject.toml'),
'.github/requirements/release.in': pin(Path('.github/requirements/release.in').read_text().splitlines(), 'release.in'),
'.github/requirements/release.txt': pin(Path('.github/requirements/release.txt').read_text().splitlines(), 'release.txt'),
}
for where, version in pins.items():
print(f'{where}: setuptools=={version}')
if len(set(pins.values())) != 1:
sys.exit('::error::setuptools pins disagree. Update pyproject.toml and .github/requirements together.')
- name: Build the release distributions from release.txt
if: steps.select.outputs.requirements == 'true'
run: |
# Same install and build steps as the python-nightly build job.
python -m venv /tmp/clustermax-release
/tmp/clustermax-release/bin/python -m pip install "pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70"
/tmp/clustermax-release/bin/python -m pip install \
--require-hashes \
--only-binary=:all: \
--disable-pip-version-check \
-r .github/requirements/release.txt
/tmp/clustermax-release/bin/python -m build --no-isolation --outdir /tmp/clustermax-release-dist
/tmp/clustermax-release/bin/python -m twine check /tmp/clustermax-release-dist/*
/tmp/clustermax-release/bin/check-wheel-contents /tmp/clustermax-release-dist/clustermax-*.whl
rm -rf build ./*.egg-info
- name: Run Python tests
if: steps.select.outputs.none != 'true'
env:
ALL: ${{ steps.select.outputs.all }}
TESTS: ${{ steps.select.outputs.tests }}
run: |
if [ "$ALL" = true ]; then
python -m pytest -q
else
mapfile -t files <<< "$TESTS"
python -m pytest -q "${files[@]}"
fi
- name: Smoke-test checkout-free public wheel
if: steps.select.outputs.package == 'true'
run: |
python -m pip wheel --no-deps --wheel-dir /tmp/clustermax-wheel .
python -c 'import glob, zipfile; wheel = glob.glob("/tmp/clustermax-wheel/clustermax-*.whl")[0]; names = zipfile.ZipFile(wheel).namelist(); assert not any("__pycache__" in name or name.endswith((".pyc", ".pyo")) for name in names)'
python -m venv /tmp/clustermax-wheel-venv
/tmp/clustermax-wheel-venv/bin/python -m pip install "pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70"
/tmp/clustermax-wheel-venv/bin/pip install /tmp/clustermax-wheel/clustermax-*.whl
cd /tmp
/tmp/clustermax-wheel-venv/bin/cmax audit security --show
/tmp/clustermax-wheel-venv/bin/cmax audit --show
/tmp/clustermax-wheel-venv/bin/cmax audit --standalone --show
/tmp/clustermax-wheel-venv/bin/cmax audit --help | grep -F "full cluster configuration and health audit"
/tmp/clustermax-wheel-venv/bin/cmax audit security --help | grep -F "focused, read-only security report"
if /tmp/clustermax-wheel-venv/bin/cmax init 2>/dev/null; then exit 1; fi
/tmp/clustermax-wheel-venv/bin/cmax audit security --standalone --dry-run -o yaml > /tmp/clustermax-security-plan.yaml
grep -q '^audit_profile:' /tmp/clustermax-security-plan.yaml
/tmp/clustermax-wheel-venv/bin/python -c 'from cmax.security import find_runtime_root; assert find_runtime_root().is_dir()'
/tmp/clustermax-wheel-venv/bin/python -c 'from cmax.security import find_runtime_root; root = find_runtime_root(); assert root.name == "cmax"; assert (root / "cmax.yaml").is_file(); assert (root / "scripts/1-audit/run.sh").is_file()'
/tmp/clustermax-wheel-venv/bin/python -c 'import cmax; from importlib.metadata import version; assert cmax.__version__ == version("clustermax")'