[ci] Run downstream device-builder tests against PR Python code

Add a CI job that checks out esphome/device-builder@main, installs it,
overlays the PR's esphome via `pip install -e ./esphome`, and runs its
pytest suite. Gated by determine-jobs.py: only runs when Python files
under esphome/ change or runtime deps (requirements.txt, pyproject.toml)
change -- the surface device-builder consumes when it imports esphome.

This catches downstream breakage at PR time instead of after release,
mirroring the yarl -> aiohttp pattern.
This commit is contained in:
J. Nick Koston
2026-05-03 08:49:59 -05:00
parent 8046ff7e1e
commit 365d93f01b
3 changed files with 139 additions and 0 deletions
+41
View File
@@ -136,6 +136,44 @@ jobs:
if-no-files-found: ignore
retention-days: 14
device-builder:
name: Test downstream esphome/device-builder
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: needs.determine-jobs.outputs.device-builder == 'true'
steps:
- name: Check out esphome (this PR)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: esphome
- name: Check out esphome/device-builder
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: esphome/device-builder
ref: main
path: device-builder
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.13"
cache: pip
cache-dependency-path: |
device-builder/pyproject.toml
esphome/requirements.txt
- name: Install device-builder + esphome from PR
run: |
python -m pip install --upgrade pip
# Install device-builder with its esphome + test extras first so its
# pinned versions of pytest/etc. land, then overlay the PR's esphome
# so the downstream tests run against this PR's Python code.
pip install -e './device-builder[esphome]' -e './device-builder[test]'
pip install -e ./esphome
- name: Run device-builder pytest
working-directory: device-builder
run: pytest -q --maxfail=5 --durations=10 --no-cov --ignore=tests/benchmarks
pytest:
name: Run pytest
strategy:
@@ -204,6 +242,7 @@ jobs:
clang-tidy-mode: ${{ steps.determine.outputs.clang-tidy-mode }}
python-linters: ${{ steps.determine.outputs.python-linters }}
import-time: ${{ steps.determine.outputs.import-time }}
device-builder: ${{ steps.determine.outputs.device-builder }}
changed-components: ${{ steps.determine.outputs.changed-components }}
changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }}
directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }}
@@ -247,6 +286,7 @@ jobs:
echo "clang-tidy-mode=$(echo "$output" | jq -r '.clang_tidy_mode')" >> $GITHUB_OUTPUT
echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT
echo "import-time=$(echo "$output" | jq -r '.import_time')" >> $GITHUB_OUTPUT
echo "device-builder=$(echo "$output" | jq -r '.device_builder')" >> $GITHUB_OUTPUT
echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT
echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $GITHUB_OUTPUT
echo "directly-changed-components-with-tests=$(echo "$output" | jq -c '.directly_changed_components_with_tests')" >> $GITHUB_OUTPUT
@@ -1063,6 +1103,7 @@ jobs:
- clang-tidy-nosplit
- clang-tidy-split
- determine-jobs
- device-builder
- test-build-components-split
- pre-commit-ci-lite
- memory-impact-target-branch