# Quality gate: lint + unit tests + the HA API-drift sweep. # # The test suite is stdlib-only (parsers, intents, running-apps) and runs the # vendored ha_adb_common helpers without installing Home Assistant — see # tests/conftest.py. androidtvremote2 is installed so the real-library # volume/TYPED-dict tests execute (tests/test_protocol_transport.py). Real # firmware captures live in tests/fixtures # (how to capture more: docs/RESEARCH_SHIELD.md §8). # # The `ha-api-drift` job is the ONE place Home Assistant is installed: every # other job validates our code against hand-written stubs, which stay green # even when a call site no longer matches the real API. That is how three # drifts reached master (t36 D1/D2/D3) — see tests/ha_api_drift.py. name: Test on: push: branches: [master] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install test tooling run: | set -euo pipefail # Try the plain install first: it succeeds on images whose Python is # not externally managed (and on pip < 23, which has no PEP 668 # enforcement at all). When the runner image DOES mark its Python as # externally managed — Debian/Ubuntu based Gitea act images do — pip # refuses with externally-managed-environment and we retry with the # sanctioned override. That override is safe here specifically # because the job runs in a disposable container, and because the # flag only exists on pip >= 23.0.1, the same version that raises # the refusal in the first place. # # Tools are pinned to the versions the suite was validated against so # a newer ruff rule set cannot redden the gate on unrelated commits; # androidtvremote2 tracks the manifest floor so CI exercises the same # dependency users install. PKGS="pytest~=9.1 pytest-asyncio~=1.4 ruff~=0.17.0 'androidtvremote2>=0.2.0'" if ! eval python3 -m pip install --quiet $PKGS; then echo "system Python is externally managed; retrying with --break-system-packages" eval python3 -m pip install --quiet --break-system-packages $PKGS fi - name: Lint run: ruff check custom_components tests - name: Unit tests run: python3 -m pytest tests -q # Resolve every Home-Assistant API use in our production modules against a # real install: imports, keyword arguments and await/sync mismatches. The # sweep is repo-owned (tests/ha_api_drift.py) and skips cleanly when # Home Assistant is absent, so it is inert in the job above and live here. ha-api-drift: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Check the runner Python can host Home Assistant run: | set -euo pipefail # Home Assistant 2026.x requires Python >= 3.13; a newer runner just # works. Fail loudly with the fix instead of a confusing resolver # error when the image is older. if ! python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3, 13) else 1)'; then echo "Home Assistant needs Python >= 3.13 but this runner has $(python3 -V)." echo "Add 'actions/setup-python@v5' with python-version '3.13' to this job." exit 1 fi - name: Install Home Assistant + test tooling run: | set -euo pipefail # Same PEP 668 dance as the test job (see the note there). The # version floor tracks what we validate against; a resolver failure # here is a real signal (Python too old), not something to swallow. PKGS="pytest~=9.1 pytest-asyncio~=1.4 'homeassistant>=2026.10'" if ! eval python3 -m pip install --quiet $PKGS; then echo "system Python is externally managed; retrying with --break-system-packages" eval python3 -m pip install --quiet --break-system-packages $PKGS fi - name: HA API-drift sweep run: python3 -m pytest tests/test_ha_api_drift.py -q