Improve CI/CD: labeling, auto-merge, workflow organization, and Python checks#794
Conversation
Use actions/labeler for file-based labels and github/issue-labeler for body-based labels. Both support sync-labels to add AND remove labels when patterns match or no longer match. - Added .github/labeler.yml for file-based label patterns - Added .github/issue-labeler.yml for body-based (checkbox) patterns - Added .github/workflows/labeler.yaml to run both labelers - Removed autolabeler section from release-drafter.yml - Removed related TODO item Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR replaces release-drafter's autolabeler with dedicated GitHub Actions for labeling pull requests. The change addresses a limitation where release-drafter only adds labels but never removes them when patterns no longer match (e.g., when a checkbox is unchecked).
Changes:
- Added dedicated labeling workflow using actions/labeler (file-based) and github/issue-labeler (body-based) with sync-labels enabled
- Migrated autolabeler configuration from release-drafter.yml to separate configuration files
- Removed completed TODO item about exploring alternative autolabeler workflows
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/labeler.yaml |
New workflow with two jobs for file-based and body-based labeling |
.github/labeler.yml |
File-based label patterns migrated from release-drafter autolabeler |
.github/issue-labeler.yml |
Body-based (checkbox) label patterns migrated from release-drafter autolabeler |
.github/release-drafter.yml |
Removed autolabeler section, retained release notes generation functionality |
TODO.md |
Removed completed TODO item about autolabeler alternatives |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Self-contained workflow that polls for all other checks to complete. Use "All Checks Pass" as the single required status check in branch protection - solves the issue where path-filtered workflows don't always run. No external action dependencies - uses inline gh API calls. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes race condition where CI gate could complete before slower workflows even start. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Workflows now always trigger but skip jobs internally when paths don't match. This ensures a consistent number of check runs regardless of which files changed. Uses dorny/paths-filter to check changed files, then jobs use if: conditions to skip when no relevant changes. Also removed initial delay from CI gate since workflows now start immediately. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove 'dependencies' from body-based labeler to avoid conflict with file-based labeler (both had sync-labels which could fight) - Match both [x] and [X] in checkbox patterns for manual edits Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #794 +/- ##
=======================================
Coverage 95.26% 95.26%
=======================================
Files 29 29
Lines 2384 2384
Branches 88 88
=======================================
Hits 2271 2271
Misses 113 113
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
After all checks appear complete, wait 60 seconds and re-verify to catch late-arriving checks like Codecov that report asynchronously. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of using branch protection required checks + CI gate workflow: - Auto-merge now polls for specific checks directly - "Check Changes" must exist and pass (always runs) - Other checks (Pytest, HACS, etc.) must pass IF they run - Deleted CI gate workflow and branch protection ruleset This simplifies the CI setup and avoids race conditions with late-arriving checks like Codecov. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Give workflows 15 seconds to register their check runs before polling starts. This prevents the edge case where auto-merge starts polling before other workflows have registered, which would cause a "No required checks found" error. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
94c4000 to
52c6d19
Compare
Merge frontend.yaml into integration.yaml with a single check-changes job that outputs both python and frontend filters. Jobs conditionally run based on their respective filter outputs. Benefits: - Single paths-filter execution instead of two - Unified workflow view in GitHub Actions - Simpler auto-merge check list (one Check Changes job) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Split integration.yaml into reusable workflows: - python-checks.yml: Pytest, HACS, Hassfest jobs - frontend-checks.yml: Yarn Lint and Build, Vitest jobs - integration.yaml: Orchestrates with check-changes and calls reusable workflows Benefits: - GitHub UI shows collapsible "Python" and "Frontend" groups - Clear visual hierarchy in the Actions tab - Maintains skip behavior via caller job conditions Update auto-merge to use prefixed check names (e.g., "Python / Pytest") since reusable workflow jobs get prefixed with caller job name. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
HACS and Hassfest validate the Home Assistant integration as a whole, not just Python code. "Integration" better reflects the scope of these checks. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
HACS and Hassfest validate different aspects of the integration: - HACS: hacs.json, README.md, custom_components/ structure - Hassfest: custom_components/ manifest, translations, services Each now has its own path filter and runs as a direct job rather than nested under Python. This means they only run when relevant files change. Structure: - Check Changes: Outputs python, hacs, hassfest, frontend filters - Python (reusable): Pytest only - HACS (direct): Runs when hacs.json, README.md, or custom_components changes - Hassfest (direct): Runs when custom_components changes - Frontend (reusable): Vitest, Yarn Lint and Build Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove path filtering for HACS and Hassfest - they're quick validation checks (~30 seconds) that can run on every PR. Making them required for auto-merge provides a natural delay, eliminating the need for an artificial sleep. Changes: - HACS and Hassfest run unconditionally (no needs/if) - Moved to MUST_PASS in auto-merge (always exist and must pass) - Removed 60-second sleep delay (HACS/Hassfest provide natural delay) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Python checks now include: - Ruff: Format check and linting (runs on 3.13) - Mypy: Type checking (runs on 3.13) - Pytest: Tests with coverage (matrix: 3.13, 3.14) Notes: - Static analysis (Ruff, Mypy) runs on single version - Pytest uses matrix with fail-fast: false for independent results - Coverage only uploaded for 3.13 to avoid duplicates - Python 3.14 uses allow-prereleases: true Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Mypy has ~30 errors in the codebase. Removing from this PR to keep scope focused on CI improvements. Added TODO to: - Add mypy (or alternative) to pre-commit hooks - Add type checking CI job - Explore alternatives (Astral may have a replacement) - Fix existing type errors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Define Python versions as env vars at workflow level: - TARGET_PYTHON: Primary version for coverage and static analysis - OTHER_PYTHON_VERSIONS: JSON array of additional test versions A setup job computes the combined matrix and passes it to downstream jobs. This makes version management centralized - just update the env vars to test different Python versions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use * suffix for pattern matching in IF_RUN_PASS checks: - "Python / Pytest *" matches "Python / Pytest (3.13)", "Python / Pytest (3.14)", etc. This decouples repository.yaml from specific Python versions defined in python-checks.yml. When Python versions change, only python-checks.yml needs updating. Also optimized to fetch all check runs once per iteration instead of per-check queries. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…n checks (#794) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…n checks (#794) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…n checks (#794) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…n checks (#794) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…n checks (#794) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add Zigbee2MQTT lock provider for MQTT-based locks Add support for Zigbee2MQTT locks by publishing/subscribing to MQTT topics. This enables user code management for locks exposed via Zigbee2MQTT: - Detects Zigbee2MQTT devices by their device identifiers (zigbee2mqtt_*) - Uses MQTT publish to set/clear PIN codes - Subscribes to device topic for push updates - Polls via MQTT get requests with async response handling Maps to the "mqtt" domain in INTEGRATIONS_CLASS_MAP since Zigbee2MQTT devices appear as MQTT entities in Home Assistant. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add mqtt to after_dependencies in manifest 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Run autolabeling on PR description edits (#793) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * Improve CI/CD: labeling, auto-merge, workflow organization, and Python checks (#794) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * Update Zigbee2MQTT provider for current BaseLock interface Align zigbee2mqtt.py with codebase changes since the PR was created: - Use SlotCode.EMPTY instead of "" for cleared/empty slots - Rename subscribe/unsubscribe_push_updates to setup/teardown_push_subscription - Rename async_is_connection_up to async_is_integration_connected - Fix async_set_usercode signature: usercode is str, not int | str - Fix return types to dict[int, str | SlotCode] - Import SlotCode from models, use LOGGER from providers.const - Remove redundant lock_config_entry field (already in BaseLock) - Use comprehension for Zigbee2MQTT device identifier check Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(hacs): allow branch installs by disabling zip_release Made-with: Cursor * fix(config_flow): match MQTT locks using HA platform mqtt for Z2M Document Zigbee2MQTT prerequisites in README. Made-with: Cursor * fix(config_flow): build lock entity filters from INTEGRATIONS_CLASS_MAP Avoid pkgutil.iter_modules(providers) returning empty under HACS/HA, which made the selector show no locks. Made-with: Cursor * fix(mqtt): stop using removed hass.components for MQTT API Use homeassistant.components.mqtt async_publish/async_subscribe and mqtt_config_entry_enabled per current Home Assistant core. Made-with: Cursor * fix(mqtt): dispatch Z2M MQTT callbacks via hass.add_job for thread safety Made-with: Cursor * fix(mqtt): clear Z2M PIN with pin_code null and user_type Made-with: Cursor * fix(mqtt): deliver Z2M MQTT payloads with loop.call_soon_threadsafe Made-with: Cursor * fix(mqtt): optimistic coordinator updates after Z2M set/clear publish Made-with: Cursor * fix(zigbee2mqtt): avoid false EMPTY when PIN is hidden in MQTT state Zigbee2MQTT omits pin_code when expose_pin is false; treating that as an empty slot prevented sync from calling clear_usercode when disabling. Also refresh coordinator on pin_code_deleted actions. Adds unit tests for users payload parsing. Made-with: Cursor * style(zigbee2mqtt): satisfy ruff D202 after docstring Made-with: Cursor * chore: add git commit template and setup helpers Made-with: Cursor: * Revert "chore: add git commit template and setup helpers" This reverts commit 07302ef. * docs(README): include Zigbee2MQTT in supported install step Made-with: Cursor * fix(zigbee2mqtt): use get_managed_slots for EntryConfig (main API) get_entry_data was removed upstream; async_get_usercodes must resolve managed slots via the same helper as the rest of the integration. Made-with: Cursor * test(zigbee2mqtt): minimal mock includes lock.platform for BaseLock BaseLock.__post_init__ logs platform and entry ids when device_id is missing. Made-with: Cursor * docs(config_flow): explain lock picker uses INTEGRATIONS_CLASS_MAP vs pkgutil Made-with: Cursor * chore: gitignore .cursor (local Cursor IDE rules) Made-with: Cursor * fix(zigbee2mqtt): accept source in async_set_usercode (BaseLock API) Required by async_internal_set_usercode after main merge; prevents TypeError when sync manager sets usercodes. Made-with: Cursor * chore::Clean up comments in config_flow.py Removed comments explaining the lock entity picker and integration handling. * refactor(zigbee2mqtt): remove unused USER_STATUS constants Made-with: Cursor * test(zigbee2mqtt): cover setup_push_subscription and push_update path - Assert async_subscribe uses zigbee2mqtt/{friendly_name} topic - Verify MQTT callback schedules coordinator.push_update on the loop - Add idempotent setup and LockDisconnected when device is not Z2M Made-with: Cursor * test(zigbee2mqtt): cover async_get_usercodes MQTT futures and timeouts - Assert one get publish per managed slot on the device get topic - Resolve pin_code responses into slot values - Timeout path maps to SlotCode.EMPTY Made-with: Cursor * docs: wrap Zigbee2MQTT footnote for markdownlint (MD013) Made-with: Cursor * fix(zigbee2mqtt): refresh Z2M topic name, loop futures, parallel get - Read device name each call so HA renames update MQTT topics - Use running loop create_future for pin_code waits (Copilot review) - Publish all get requests then gather wait_for per slot concurrently Made-with: Cursor * fix(zigbee2mqtt): subscribe in async_setup, split availability, stricter PIN reads and tests Made-with: Cursor * docs(zigbee2mqtt): clarify code events and optimistic push_update Made-with: Cursor * fix(zigbee2mqtt): explicit PIN checks, clearer non-Z2M disconnect, tests Made-with: Cursor * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs(readme): wrap Zigbee2MQTT footnote for markdownlint MD013 Made-with: Cursor * fix(zigbee2mqtt): use hass.add_job for MQTT callbacks (HA deprecates async_add_job) Made-with: Cursor * fix(zigbee2mqtt): serial GET usercodes and tolerate per-slot timeouts Parallel gather raised LockDisconnected on any slot timeout, failing the whole refresh and leaving coordinator.data without keys — SlotSyncManager then skipped those slots. Restore sequential get with per-slot EMPTY on timeout/error so every managed slot has coordinator state. Made-with: Cursor * refactor(zigbee2mqtt): align try/except/else with review feedback Made-with: Cursor * docs(zigbee2mqtt): shorten setup_push_subscription docstring Made-with: Cursor * fix(zigbee2mqtt): wrap setup_push_subscription docstring for flake8 E501 Made-with: Cursor * fix(zigbee2mqtt): use UNREADABLE_CODE on GET read failures Transient publish/timeout/wait errors map to SlotCode.UNREADABLE_CODE so sync does not assume confirmed-empty slots during MQTT outages. Log at warning. Docstring for _subscribe_or_log kept to two lines (closing quote on own line). Made-with: Cursor * fix(zigbee2mqtt): log GET failures at debug level Transient MQTT publish/timeout/read errors are expected during churn; keep UNREADABLE_CODE behavior but avoid warning spam in HA logs. Made-with: Cursor * test(zigbee2mqtt): raise zigbee2mqtt coverage above 96% Cover bool PIN payload guard and async_clear_usercode OSError path (publish failure uses HomeAssistantError/OSError; RuntimeError skipped that branch). Made-with: Cursor --------- Co-authored-by: raman325 <7243222+raman325@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Proposed change
Comprehensive CI/CD improvements covering labeling, auto-merge, workflow organization, and Python testing.
Labeling Improvements
Replace release-drafter's autolabeler with dedicated GitHub Actions that support both adding AND removing labels when patterns match or no longer match:
sync-labels: true) for file-based labelssync-labels: 1) for body-based (checkbox) labelsAuto-merge Improvements
Replace branch protection-based auto-merge with explicit check polling:
MUST_PASS(always run) andIF_RUN_PASS(conditional)Python / Pytest *matches all Pytest versions)Workflow Organization
Consolidate and reorganize CI workflows:
Python Check Improvements
TARGET_PYTHONandOTHER_PYTHON_VERSIONSenv varsFile Changes
Added:
.github/labeler.yml- file-based label patterns.github/issue-labeler.yml- body-based (checkbox) patterns.github/workflows/labeler.yaml- runs both labelers on PR events.github/workflows/python-checks.yml- reusable Ruff + Pytest workflow.github/workflows/frontend-checks.yml- reusable Vitest/Lint workflowUpdated:
.github/workflows/integration.yaml- orchestrates all checks.github/workflows/repository.yaml- auto-merge with pattern-based check pollingTODO.md- added item for mypy/type checking (deferred)Removed:
.github/workflows/frontend.yaml(consolidated)release-drafter.ymlType of change
Additional information