[ci] Replace pre-commit with prek (#18029)

This commit is contained in:
Jesse Hills
2026-08-04 16:18:46 +12:00
committed by GitHub
parent dafc86d960
commit 4ceae9cf85
7 changed files with 49 additions and 77 deletions
+20 -56
View File
@@ -65,7 +65,7 @@ jobs:
python -m venv venv python -m venv venv
. venv/bin/activate . venv/bin/activate
python --version python --version
uv pip install -r requirements.txt -r requirements_dev.txt -r requirements_test.txt pre-commit uv pip install -r requirements.txt -r requirements_dev.txt -r requirements_test.txt
uv pip install -e . uv pip install -e .
determine-jobs: determine-jobs:
@@ -208,69 +208,34 @@ jobs:
run: script/ci-suggest-changes run: script/ci-suggest-changes
if: always() if: always()
pre-commit-ci-lite: lint-format:
name: pre-commit.ci lite name: Check lint and formatting
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- common
- determine-jobs - determine-jobs
if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release') && needs.determine-jobs.outputs.core-ci == 'true' if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release') && needs.determine-jobs.outputs.core-ci == 'true'
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Restore Python - name: Run prek
uses: ./.github/actions/restore-python uses: j178/prek-action@4e14d07f9231acabce116ccfca13b13dd9755ece # v3.0.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} # Keep in sync with requirements_test.txt.
cache-key: ${{ needs.common.outputs.cache-key }} prek-version: "0.4.11"
# Inlined from esphome/pre-commit-action with a restore-only cache # This job only runs on pull requests, so nothing ever populates
# step: the pre-commit-seed-cache job owns saving this cache, so # the cache on dev. Every run would miss and then write a per-pull
# pull request runs never write per-PR copies. # request copy, which is what the old seed-cache job existed to
- name: Restore pre-commit cache # avoid. Building the hooks from scratch takes seconds, so skip it.
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 cache: false
with:
path: ~/.cache/pre-commit
# Must match the key pre-commit-seed-cache saves
# yamllint disable-line rule:line-length
key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run pre-commit
env: env:
SKIP: pylint,ci-custom PREK_SKIP: pylint,ci-custom
run: | # Pushes any fixes the hooks made back to the pull request. This step
python -m pip install pre-commit # must keep its default name: the GitHub App that performs the push
pre-commit run --show-diff-on-failure --color=always --all-files # locates the workflow run by that name.
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0 - uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
if: always() if: always()
pre-commit-seed-cache:
name: Seed pre-commit cache
runs-on: ubuntu-latest
needs:
- common
# Saves a dev-scoped pre-commit cache that pull request runs can
# restore, since pre-commit.ci lite itself never runs on dev pushes.
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
steps:
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Restore Python
uses: ./.github/actions/restore-python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} msg: apply automatic formatting fixes
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache pre-commit environments
id: cache-pre-commit
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/pre-commit
# Must match the restore key in pre-commit-ci-lite
# yamllint disable-line rule:line-length
key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Install pre-commit hook environments
if: steps.cache-pre-commit.outputs.cache-hit != 'true'
run: |
python -m pip install pre-commit
pre-commit install-hooks
pytest: pytest:
name: Run pytest name: Run pytest
@@ -1456,16 +1421,15 @@ jobs:
ci-status: ci-status:
name: CI Status name: CI Status
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
# Listed in the same order the jobs are defined above. Two jobs are # Listed in the same order the jobs are defined above. One job is
# deliberately left out: "benchmarks" reports through CodSpeed rather than # deliberately left out: "benchmarks" reports through CodSpeed rather than
# this check, and "pre-commit-seed-cache" only populates a cache on pushes # this check.
# to dev.
needs: needs:
- common - common
- determine-jobs - determine-jobs
- ci-custom - ci-custom
- pylint - pylint
- pre-commit-ci-lite - lint-format
- pytest - pytest
- codecov-empty-upload - codecov-empty-upload
- integration-tests - integration-tests
+11 -11
View File
@@ -44,7 +44,7 @@ jobs:
- name: Set up uv - name: Set up uv
# An order of magnitude faster than pip on cold boots, with its # An order of magnitude faster than pip on cold boots, with its
# own wheel cache. ``--system`` (below) installs into the # own wheel cache. ``--system`` (below) installs into the
# setup-python interpreter so subsequent ``pre-commit`` / # setup-python interpreter so subsequent ``prek`` /
# ``script/run-in-env.py`` steps find the deps without a # ``script/run-in-env.py`` steps find the deps without a
# ``uv run`` prefix. # ``uv run`` prefix.
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
@@ -58,19 +58,19 @@ jobs:
- name: Install Home Assistant - name: Install Home Assistant
run: | run: |
uv pip install --system -e lib/home-assistant uv pip install --system -e lib/home-assistant
uv pip install --system -r requirements.txt -r requirements_test.txt pre-commit uv pip install --system -r requirements.txt -r requirements_test.txt
- name: Sync - name: Sync
run: | run: |
python ./script/sync-device_class.py python ./script/sync-device_class.py
- name: Apply pre-commit auto-fixes - name: Apply prek auto-fixes
# First pass: let formatters (ruff, end-of-file-fixer, etc.) modify # First pass: let formatters (ruff, end-of-file-fixer, etc.) modify
# files. pre-commit exits non-zero whenever a hook touches anything, # files. prek exits non-zero whenever a hook touches anything,
# which would otherwise abort the workflow before the auto-fixes # which would otherwise abort the workflow before the auto-fixes
# can flow into the sync PR. # can flow into the sync PR.
# #
# SKIP: # PREK_SKIP:
# - no-commit-to-branch is a local guard against committing on # - no-commit-to-branch is a local guard against committing on
# dev/release/beta; CI runs on dev by definition, and # dev/release/beta; CI runs on dev by definition, and
# peter-evans/create-pull-request creates the branch itself. # peter-evans/create-pull-request creates the branch itself.
@@ -79,18 +79,18 @@ jobs:
# the runtime deps (HA + requirements*.txt); main CI already # the runtime deps (HA + requirements*.txt); main CI already
# gates pylint on real PRs. # gates pylint on real PRs.
env: env:
SKIP: pylint,no-commit-to-branch PREK_SKIP: pylint,no-commit-to-branch
run: python script/run-in-env.py pre-commit run --all-files || true run: python script/run-in-env.py prek run --all-files || true
- name: Verify pre-commit clean - name: Verify prek clean
# Second pass: re-run all hooks against the now-fixed tree. # Second pass: re-run all hooks against the now-fixed tree.
# Auto-fixers exit 0 (nothing to change); any remaining failure # Auto-fixers exit 0 (nothing to change); any remaining failure
# from a check-only hook (flake8 / yamllint / ci-custom) is a # from a check-only hook (flake8 / yamllint / ci-custom) is a
# real issue and fails the workflow loudly. Same SKIP list as # real issue and fails the workflow loudly. Same PREK_SKIP list as
# above for the same reasons. # above for the same reasons.
env: env:
SKIP: pylint,no-commit-to-branch PREK_SKIP: pylint,no-commit-to-branch
run: python script/run-in-env.py pre-commit run --all-files run: python script/run-in-env.py prek run --all-files
- name: Commit changes - name: Commit changes
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
+3 -3
View File
@@ -412,7 +412,7 @@ This document provides essential context for AI models interacting with this pro
* **Configuration:** * **Configuration:**
* `pyproject.toml`: Defines the Python project metadata and dependencies. * `pyproject.toml`: Defines the Python project metadata and dependencies.
* `platformio.ini`: Configures the PlatformIO build environments for different microcontrollers. * `platformio.ini`: Configures the PlatformIO build environments for different microcontrollers.
* `.pre-commit-config.yaml`: Configures the pre-commit hooks for linting and formatting. * `.pre-commit-config.yaml`: Configures the lint and format hooks, run by `prek`.
* **CI/CD Pipeline:** Defined in `.github/workflows`. * **CI/CD Pipeline:** Defined in `.github/workflows`.
* **Static Analysis & Development:** * **Static Analysis & Development:**
* `esphome/core/defines.h`: A comprehensive header file containing all `#define` directives that can be added by components using `cg.add_define()` in Python. This file is used exclusively for development, static analysis tools, and CI testing - it is not used during runtime compilation. When developing components that add new defines, they must be added to this file to ensure proper IDE support and static analysis coverage. The file includes feature flags, build configurations, and platform-specific defines that help static analyzers understand the complete codebase without needing to compile for specific platforms. * `esphome/core/defines.h`: A comprehensive header file containing all `#define` directives that can be added by components using `cg.add_define()` in Python. This file is used exclusively for development, static analysis tools, and CI testing - it is not used during runtime compilation. When developing components that add new defines, they must be added to this file to ensure proper IDE support and static analysis coverage. The file includes feature flags, build configurations, and platform-specific defines that help static analyzers understand the complete codebase without needing to compile for specific platforms.
@@ -420,7 +420,7 @@ This document provides essential context for AI models interacting with this pro
## 6. Development & Testing Workflow ## 6. Development & Testing Workflow
* **Local Development Environment:** Use the provided Docker container or create a Python virtual environment and install dependencies from `requirements_dev.txt`. * **Local Development Environment:** Use the provided Docker container or create a Python virtual environment and install dependencies from `requirements_dev.txt`.
* **Running Commands:** Use the `script/run-in-env.py` script to execute commands within the project's virtual environment. For example, to run the linter: `python3 script/run-in-env.py pre-commit run`. * **Running Commands:** Use the `script/run-in-env.py` script to execute commands within the project's virtual environment. For example, to run the linter: `python3 script/run-in-env.py prek run`.
* **Testing:** * **Testing:**
* **Python:** Run unit tests with `pytest`. * **Python:** Run unit tests with `pytest`.
* **C++:** Use `clang-tidy` for static analysis. * **C++:** Use `clang-tidy` for static analysis.
@@ -493,7 +493,7 @@ This document provides essential context for AI models interacting with this pro
1. **Fork & Branch:** Create a new branch based on the `dev` branch (always use `git checkout -b <branch-name> dev` to ensure you're branching from `dev`, not the currently checked out branch). 1. **Fork & Branch:** Create a new branch based on the `dev` branch (always use `git checkout -b <branch-name> dev` to ensure you're branching from `dev`, not the currently checked out branch).
2. **Make Changes:** Adhere to all coding conventions and patterns. 2. **Make Changes:** Adhere to all coding conventions and patterns.
3. **Test:** Create component tests for all supported platforms and run the full test suite locally. 3. **Test:** Create component tests for all supported platforms and run the full test suite locally.
4. **Lint:** Run `pre-commit` to ensure code is compliant. 4. **Lint:** Run `prek` to ensure code is compliant.
5. **Commit:** Commit your changes. There is no strict format for commit messages. 5. **Commit:** Commit your changes. There is no strict format for commit messages.
6. **Pull Request:** Submit a PR against the `dev` branch. The Pull Request title must start with a `[tag]` prefix. For component work, use the component name (e.g., `[display] Fix bug`, `[abc123] Add new component`); for changes to shared/core code that isn't tied to a single component, use `[core]` (e.g., `[core] Add validator`). Update documentation, examples, and add `CODEOWNERS` entries as needed. Pull requests should always be made using the `.github/PULL_REQUEST_TEMPLATE.md` template - fill out all sections completely without removing any parts of the template. 6. **Pull Request:** Submit a PR against the `dev` branch. The Pull Request title must start with a `[tag]` prefix. For component work, use the component name (e.g., `[display] Fix bug`, `[abc123] Add new component`); for changes to shared/core code that isn't tied to a single component, use `[core]` (e.g., `[core] Add validator`). Update documentation, examples, and add `CODEOWNERS` entries as needed. Pull requests should always be made using the `.github/PULL_REQUEST_TEMPLATE.md` template - fill out all sections completely without removing any parts of the template.
+1 -1
View File
@@ -2,7 +2,7 @@ pylint==4.0.6
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
ruff==0.16.1 # also change in .pre-commit-config.yaml when updating ruff==0.16.1 # also change in .pre-commit-config.yaml when updating
pyupgrade==3.21.2 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.2 # also change in .pre-commit-config.yaml when updating
pre-commit prek==0.4.11 # also change in .github/workflows/ci.yml when updating
# Unit tests # Unit tests
pytest==9.1.1 pytest==9.1.1
+4 -4
View File
@@ -23,7 +23,7 @@ what files have changed. It outputs JSON with the following structure:
} }
The CI workflow uses this information to: The CI workflow uses this information to:
- Gate the unconditional jobs (ci-custom, pytest, pre-commit-ci-lite) via core_ci; - Gate the unconditional jobs (ci-custom, pytest, lint-format) via core_ci;
false when a pull_request only touches CI-irrelevant meta paths (other workflow false when a pull_request only touches CI-irrelevant meta paths (other workflow
files, .github/actions/build-image/*, .yamllint, .github/dependabot.yml, docker/**) files, .github/actions/build-image/*, .yamllint, .github/dependabot.yml, docker/**)
so workflow-only PRs satisfy the required CI Status check without running the so workflow-only PRs satisfy the required CI Status check without running the
@@ -708,7 +708,7 @@ def should_run_benchmarks(branch: str | None = None) -> bool:
# Files / path patterns whose changes alone don't warrant running the # Files / path patterns whose changes alone don't warrant running the
# unconditional CI jobs (`ci-custom`, `pytest`, `pre-commit-ci-lite`). # unconditional CI jobs (`ci-custom`, `pytest`, `lint-format`).
# Single source of truth for what we treat as "CI-irrelevant" on # Single source of truth for what we treat as "CI-irrelevant" on
# pull_request events; ci.yml used to encode this in its own # pull_request events; ci.yml used to encode this in its own
# `pull_request.paths` filter, but that hid the required `CI Status` # `pull_request.paths` filter, but that hid the required `CI Status`
@@ -752,7 +752,7 @@ def _is_ci_irrelevant_path(path: str) -> bool:
def should_run_core_ci(branch: str | None = None) -> bool: def should_run_core_ci(branch: str | None = None) -> bool:
"""Determine if the unconditional CI jobs (ci-custom/pytest/pre-commit-ci-lite) should run. """Determine if the unconditional CI jobs (ci-custom/pytest/lint-format) should run.
Returns False only when every changed file is in the CI-irrelevant set Returns False only when every changed file is in the CI-irrelevant set
above (see ``_is_ci_irrelevant_path``). Empty diffs return True so we above (see ``_is_ci_irrelevant_path``). Empty diffs return True so we
@@ -1177,7 +1177,7 @@ def main() -> None:
# Determine what should run # Determine what should run
# core_ci gates the unconditional jobs in ci.yml (ci-custom, pytest, # core_ci gates the unconditional jobs in ci.yml (ci-custom, pytest,
# pre-commit-ci-lite). Non-pull_request events (push to dev/beta/release # lint-format). Non-pull_request events (push to dev/beta/release
# and merge_group) always run them so behavior like venv-cache saves on # and merge_group) always run them so behavior like venv-cache saves on
# push to dev is preserved. # push to dev is preserved.
event_name = os.environ.get("GITHUB_EVENT_NAME", "") event_name = os.environ.get("GITHUB_EVENT_NAME", "")
+5 -1
View File
@@ -25,7 +25,11 @@ fi
uv pip install setuptools wheel uv pip install setuptools wheel
uv pip install -e ".[dev,test]" --config-settings editable_mode=compat uv pip install -e ".[dev,test]" --config-settings editable_mode=compat
pre-commit install # --overwrite replaces any hook already in place. Without it, prek finds a
# previously installed pre-commit hook, moves it aside to
# .git/hooks/pre-commit.legacy and keeps calling it, so every commit would
# run both tools.
prek install --overwrite
mkdir -p .temp mkdir -p .temp
+5 -1
View File
@@ -17,7 +17,11 @@ pip3 install -r requirements.txt -r requirements_test.txt -r requirements_dev.tx
pip3 install setuptools wheel pip3 install setuptools wheel
pip3 install -e ".[dev,test]" --config-settings editable_mode=compat pip3 install -e ".[dev,test]" --config-settings editable_mode=compat
pre-commit install rem --overwrite replaces any hook already in place. Without it, prek finds a
rem previously installed pre-commit hook, moves it aside to
rem .git/hooks/pre-commit.legacy and keeps calling it, so every commit would
rem run both tools.
prek install --overwrite
echo . echo .
echo . echo .