-
Notifications
You must be signed in to change notification settings - Fork 3
193 lines (170 loc) · 8.35 KB
/
Copy pathquality.yml
File metadata and controls
193 lines (170 loc) · 8.35 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
189
190
191
192
193
name: AutoControl Code Quality
# Static analysis (ruff, bandit) plus the headless pytest suite added in
# rounds 22-30. Decoupled from the existing dev/stable workflows, which
# run legacy standalone test scripts and exist for hardware integration
# coverage on Windows runners.
on:
push:
branches: [ "dev", "main", "stable" ]
pull_request:
branches: [ "dev", "main", "stable" ]
workflow_dispatch:
permissions:
contents: read
jobs:
dependency-review:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Install ruff
run: "pip install --only-binary :all: ruff==0.15.22"
- name: Run ruff
run: ruff check je_auto_control/
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Install bandit
run: "pip install --only-binary :all: bandit==1.9.4"
- name: Run bandit (recursive, skip tests + i18n dicts)
run: bandit -r je_auto_control/ -c pyproject.toml
pytest-headless:
# The suite ran on Windows alone for its whole life, so every
# platform assumption it holds went unmeasured on the two operating
# systems the project also claims to support. Linux and macOS are
# here to measure them; they carry the two ends of the supported
# Python range rather than all five, because what differs between
# 3.10 and 3.14 is Python and what differs here is the OS.
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- { os: windows-2022, python-version: "3.10" }
- { os: windows-2022, python-version: "3.11" }
- { os: windows-2022, python-version: "3.12" }
- { os: windows-2022, python-version: "3.13" }
- { os: windows-2022, python-version: "3.14" }
- { os: ubuntu-22.04, python-version: "3.10" }
- { os: ubuntu-22.04, python-version: "3.14" }
- { os: macos-14, python-version: "3.10" }
- { os: macos-14, python-version: "3.14" }
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
# Same set the container image installs, and for the same reasons:
# the X11 backend connects to a display at import time, opencv and
# PySide6 hard-require libGL/glib, and Qt's platform plugin needs
# the xcb libraries. Without these the suite fails at collection
# with a linker error rather than a test result.
- name: Install X11 and Qt runtime libraries (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
xvfb xauth x11-utils \
libgl1 libegl1 libglib2.0-0 \
libxkbcommon-x11-0 libdbus-1-3 \
libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-sync1 \
libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1
# One command per step, each on a single line: a `run: |` block scalar
# swallows NOSONAR markers, so a justification inside one is a comment
# that reads as handled and suppresses nothing.
- name: Upgrade the installer
shell: bash
run: python -m pip install --upgrade pip wheel # NOSONAR githubactions:S8544 # reason: pip and wheel are the installer; pinning them here would pin the tool that applies the pins below
# Install the editable package FIRST so its source dir is the one
# Python sees on subsequent imports. We deliberately avoid
# `pip install -r dev_requirements.txt` here because that file pulls in
# `je_auto_control_dev` (a separate PyPI package), which ships its own
# snapshot of `je_auto_control/` straight into site-packages and masks
# the editable install for any sub-package the snapshot doesn't include
# (admin, usb, remote_desktop, vision, …).
- name: Install the project itself
shell: bash
run: pip install -e . # NOSONAR githubactions:S8544 githubactions:S8541 # reason: installs the checked-out project itself, so there is no upstream version to lock and no third-party setup script to run
- name: Install the test tooling
shell: bash
# Quoted: `--only-binary :all:` puts a colon-space inside the
# scalar, which YAML reads as a mapping and refuses.
run: "pip install --only-binary :all: ruff==0.15.22 bandit==1.9.4 pytest==9.1.1 pytest-timeout==2.4.0 pytest-rerunfailures==15.1 coverage==7.15.4 PySide6==6.11.1"
# Paths come from `testpaths` in pyproject.toml. Do NOT pass an explicit
# path here: an argument overrides testpaths, which previously meant the
# flow_control tests were configured to run but silently never did.
#
# Linux runs under a real Xvfb rather than Qt's offscreen platform:
# the X11 backend opens a display at import time, and offscreen
# would hide exactly the breakage this job exists to find.
#
# `coverage run -m pytest`, NOT `pytest --cov`, and that is load-bearing:
# this package registers a `pytest11` entry point, so pytest imports
# `je_auto_control.utils.pytest_plugin.plugin` while loading plugins —
# which executes `je_auto_control/__init__.py`, the facade, and with it
# several hundred modules. pytest-cov only starts measuring after that,
# so every one of those modules' import-time lines was recorded as never
# executed. Measured on this tree: 11,962 statements, 24 points, and the
# worst-hit files were the biggest ones (`action_executor` +786,
# `_handlers` +684). A package that registers a pytest plugin cannot
# measure itself with `pytest --cov`; `coverage run` starts first.
- name: Run headless pytest suite under coverage
shell: bash
run: >-
${{ runner.os == 'Linux' && 'xvfb-run -a -s "-screen 0 1280x800x24"' || '' }}
python -m coverage run -m pytest -v --tb=short --timeout=120
# XML before the floor check, so a square that fails the floor still
# uploads the report that says which lines it was short of.
- name: Write the coverage XML
shell: bash
run: python -m coverage xml
- name: Enforce the coverage floor
shell: bash
run: python -m coverage report # the floor is `fail_under` in pyproject.toml, so the ratchet lives in exactly one place
- name: Upload coverage report
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.os }}-${{ matrix.python-version }}
path: coverage.xml
# The job id is still `typing-stable-api` because it is a required check and
# renaming it silently drops the requirement, but the scope is no longer the
# stable API alone: mypy now checks the whole package, minus the shrink-only
# list in `test/verify/typing_contract_exempt.txt`. The verify script also
# runs the three target platforms mypy can be pointed at, so the Windows and
# macOS backends are checked from this Ubuntu runner rather than skipped.
typing-stable-api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -e . # NOSONAR githubactions:S8541,githubactions:S8544 # reason: installs the checked-out project itself, there is no upstream version to lock and the build must run
- run: "pip install --only-binary :all: mypy==2.3.0"
# Deliberately NOT installing the optional extras: the contract forces
# every non-base third-party module to `Any` so the result cannot depend
# on what is installed, and installing them here would only hide a
# regression in that arrangement.
- run: python test/verify/typing_contract_verify.py