From 503075c4436b17aa649c903bb8547a893d333954 Mon Sep 17 00:00:00 2001 From: Julian Pawlowski Date: Sat, 15 Nov 2025 17:40:53 +0000 Subject: [PATCH] refactor(config_flow): restructure package to satisfy hassfest validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Home Assistant's hassfest validation requires config flows to be defined in a file named config_flow.py (not a package directory). Changes: - Renamed custom_components/tibber_prices/config_flow/ โ†’ config_flow_handlers/ - Created config_flow.py as bridge file re-exporting from config_flow_handlers/ - Updated all import paths across 5 files (user_flow, options_flow, subentry_flow, etc.) - Added ./scripts/hassfest for local validation (JSON/Python syntax, required files) - Added ./scripts/clean with three modes (--minimal, normal, --deep) - Refactored develop/lint/lint-check to use centralized cleanup (DRY principle) - Updated documentation in AGENTS.md and docs/development/ Technical details: - Bridge file uses __all__ exports to maintain clean public API - hassfest script uses ast.parse() for syntax validation (no disk artifacts) - clean --minimal removes .egg-info only (silent, for automated scripts) - Dual pip/uv pip compatibility for package uninstallation Impact: Integration now passes hassfest validation. Local validation available via ./scripts/hassfest before pushing to GitHub. Cleanup logic centralized and DRY across all development scripts. --- AGENTS.md | 58 +++++++--- .../tibber_prices/config_flow.py | 52 +++++++++ .../__init__.py | 10 +- .../options_flow.py | 2 +- .../schemas.py | 0 .../subentry_flow.py | 2 +- .../user_flow.py | 8 +- .../validators.py | 0 docs/development/README.md | 14 ++- docs/development/coding-guidelines.md | 17 ++- docs/development/setup.md | 20 ++-- docs/development/testing.md | 27 ++++- scripts/clean | 89 +++++++++++++++ scripts/develop | 4 + scripts/hassfest | 106 ++++++++++++++++++ scripts/lint | 4 +- scripts/lint-check | 4 +- 17 files changed, 367 insertions(+), 50 deletions(-) create mode 100644 custom_components/tibber_prices/config_flow.py rename custom_components/tibber_prices/{config_flow => config_flow_handlers}/__init__.py (73%) rename custom_components/tibber_prices/{config_flow => config_flow_handlers}/options_flow.py (98%) rename custom_components/tibber_prices/{config_flow => config_flow_handlers}/schemas.py (100%) rename custom_components/tibber_prices/{config_flow => config_flow_handlers}/subentry_flow.py (98%) rename custom_components/tibber_prices/{config_flow => config_flow_handlers}/user_flow.py (96%) rename custom_components/tibber_prices/{config_flow => config_flow_handlers}/validators.py (100%) create mode 100755 scripts/clean create mode 100755 scripts/hassfest diff --git a/AGENTS.md b/AGENTS.md index ffd3ad0..4f5fea2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -435,6 +435,15 @@ If you notice commands failing or missing dependencies: ```bash ./scripts/develop # Starts HA in debug mode with config/ dir, sets PYTHONPATH + # Automatically runs minimal cleanup (.egg-info only) +``` + +**Clean up artifacts:** + +```bash +./scripts/clean # Remove build artifacts, caches (pytest, ruff, coverage) +./scripts/clean --deep # Also remove __pycache__ (normally not needed) +./scripts/clean --minimal # Only critical issues (.egg-info) - used by develop ``` **Linting (auto-fix):** @@ -449,6 +458,14 @@ If you notice commands failing or missing dependencies: ./scripts/lint-check # CI mode, no modifications ``` +**Local validation:** + +```bash +./scripts/hassfest # Lightweight local integration validation +``` + +Note: The local `hassfest` script performs basic validation checks (JSON syntax, required files, Python syntax). Full hassfest validation runs in GitHub Actions. + **Testing:** ```bash @@ -1044,19 +1061,17 @@ cargo install git-cliff # or download binary from releases - **Markdown**: Standard GitHub-flavored Markdown supported - **HTML**: Can include `` tags for special notices (HA update entities only) -**Validate JSON files:** +**Validate integration:** ```bash -# After editing translation files, validate syntax (ruff doesn't check JSON) -python -m json.tool custom_components/tibber_prices/translations/de.json > /dev/null +# Run local validation (checks JSON syntax, Python syntax, required files) +./scripts/hassfest -# Or validate all translation files at once: -for f in custom_components/tibber_prices/translations/*.json; do - python -m json.tool "$f" > /dev/null && echo "โœ“ $f" || echo "โœ— INVALID: $f" -done +# Or validate JSON files manually if needed: +python -m json.tool custom_components/tibber_prices/translations/de.json > /dev/null ``` -**Why:** `ruff` only formats/lints Python code. JSON syntax errors (trailing commas, missing quotes) will cause HA to fail at runtime with cryptic error messages. Always validate JSON after manual edits. +**Why:** The `./scripts/hassfest` script validates JSON syntax (translations, manifest), Python syntax, and required files. This catches common errors before pushing to GitHub Actions. For quick JSON-only checks, you can still use `python -m json.tool` directly. ## Linting Best Practices @@ -1071,28 +1086,43 @@ done Calling `ruff` or `uv run ruff` directly can cause unintended side effects: -- May install the integration as a Python package (creates `__pycache__`, `.egg-info`, etc.) +- May install the integration as a Python package (creates `.egg-info`, etc.) - HA will then load the **installed** version instead of the **development** version from `custom_components/` - Causes confusing behavior where code changes don't take effect +**About `__pycache__` directories:** + +- **Normal and expected** when Home Assistant runs - this is Python's bytecode cache for faster loading +- **Not a problem** in development - speeds up HA startup +- **Already in `.gitignore`** - won't be committed +- **Only problematic** if the package gets installed in `.venv` (then HA loads installed version, not dev version) +- `./scripts/develop`, `./scripts/lint`, and `./scripts/lint-check` automatically clean up accidental installations + **Exception:** If you need to run `ruff` with custom flags not supported by our scripts: 1. Run your custom `ruff` command 2. **Immediately after**, clean up any installation artifacts: ```bash - # Remove any accidentally installed package - uv pip uninstall tibber_prices 2>/dev/null || true + # Use our cleanup script (uses both pip and uv pip for compatibility) + ./scripts/clean --minimal - # Clean up cache and build artifacts - find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true - find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true + # Or manually: + pip uninstall -y tibber_prices 2>/dev/null || true + uv pip uninstall tibber_prices 2>/dev/null || true ``` 3. Ask user to restart HA: `./scripts/develop` **When in doubt:** Stick to `./scripts/lint` - it's tested and safe. +**Note on pip vs. uv pip:** + +- `scripts/clean` uses **both** `pip` and `uv pip` for maximum compatibility +- Regular `pip uninstall` has cleaner output (no "Using Python X.Y..." messages) +- `uv pip uninstall` is used as fallback for robustness +- Both are needed because different commands may install via different methods + **Key commands:** - Dev container includes `hass` CLI for manual HA operations diff --git a/custom_components/tibber_prices/config_flow.py b/custom_components/tibber_prices/config_flow.py new file mode 100644 index 0000000..b4f1c15 --- /dev/null +++ b/custom_components/tibber_prices/config_flow.py @@ -0,0 +1,52 @@ +""" +Config flow for Tibber Prices integration. + +This module serves as the entry point for Home Assistant's config flow discovery. +The actual implementation is in the config_flow_handlers package. +""" + +from __future__ import annotations + +from .config_flow_handlers.options_flow import ( + TibberPricesOptionsFlowHandler as OptionsFlowHandler, +) +from .config_flow_handlers.schemas import ( + get_best_price_schema, + get_options_init_schema, + get_peak_price_schema, + get_price_rating_schema, + get_price_trend_schema, + get_reauth_confirm_schema, + get_select_home_schema, + get_subentry_init_schema, + get_user_schema, + get_volatility_schema, +) +from .config_flow_handlers.subentry_flow import ( + TibberPricesSubentryFlowHandler as SubentryFlowHandler, +) +from .config_flow_handlers.user_flow import TibberPricesFlowHandler as ConfigFlow +from .config_flow_handlers.validators import ( + CannotConnectError, + InvalidAuthError, + validate_api_token, +) + +__all__ = [ + "CannotConnectError", + "ConfigFlow", + "InvalidAuthError", + "OptionsFlowHandler", + "SubentryFlowHandler", + "get_best_price_schema", + "get_options_init_schema", + "get_peak_price_schema", + "get_price_rating_schema", + "get_price_trend_schema", + "get_reauth_confirm_schema", + "get_select_home_schema", + "get_subentry_init_schema", + "get_user_schema", + "get_volatility_schema", + "validate_api_token", +] diff --git a/custom_components/tibber_prices/config_flow/__init__.py b/custom_components/tibber_prices/config_flow_handlers/__init__.py similarity index 73% rename from custom_components/tibber_prices/config_flow/__init__.py rename to custom_components/tibber_prices/config_flow_handlers/__init__.py index 2ebc378..30e7c4d 100644 --- a/custom_components/tibber_prices/config_flow/__init__.py +++ b/custom_components/tibber_prices/config_flow_handlers/__init__.py @@ -3,10 +3,10 @@ from __future__ import annotations # Phase 3: Import flow handlers from their new modular structure -from custom_components.tibber_prices.config_flow.options_flow import ( +from custom_components.tibber_prices.config_flow_handlers.options_flow import ( TibberPricesOptionsFlowHandler, ) -from custom_components.tibber_prices.config_flow.schemas import ( +from custom_components.tibber_prices.config_flow_handlers.schemas import ( get_best_price_schema, get_options_init_schema, get_peak_price_schema, @@ -18,13 +18,13 @@ from custom_components.tibber_prices.config_flow.schemas import ( get_user_schema, get_volatility_schema, ) -from custom_components.tibber_prices.config_flow.subentry_flow import ( +from custom_components.tibber_prices.config_flow_handlers.subentry_flow import ( TibberPricesSubentryFlowHandler, ) -from custom_components.tibber_prices.config_flow.user_flow import ( +from custom_components.tibber_prices.config_flow_handlers.user_flow import ( TibberPricesFlowHandler, ) -from custom_components.tibber_prices.config_flow.validators import ( +from custom_components.tibber_prices.config_flow_handlers.validators import ( CannotConnectError, InvalidAuthError, validate_api_token, diff --git a/custom_components/tibber_prices/config_flow/options_flow.py b/custom_components/tibber_prices/config_flow_handlers/options_flow.py similarity index 98% rename from custom_components/tibber_prices/config_flow/options_flow.py rename to custom_components/tibber_prices/config_flow_handlers/options_flow.py index b7bad58..4c69f9e 100644 --- a/custom_components/tibber_prices/config_flow/options_flow.py +++ b/custom_components/tibber_prices/config_flow_handlers/options_flow.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import Any, ClassVar -from custom_components.tibber_prices.config_flow.schemas import ( +from custom_components.tibber_prices.config_flow_handlers.schemas import ( get_best_price_schema, get_options_init_schema, get_peak_price_schema, diff --git a/custom_components/tibber_prices/config_flow/schemas.py b/custom_components/tibber_prices/config_flow_handlers/schemas.py similarity index 100% rename from custom_components/tibber_prices/config_flow/schemas.py rename to custom_components/tibber_prices/config_flow_handlers/schemas.py diff --git a/custom_components/tibber_prices/config_flow/subentry_flow.py b/custom_components/tibber_prices/config_flow_handlers/subentry_flow.py similarity index 98% rename from custom_components/tibber_prices/config_flow/subentry_flow.py rename to custom_components/tibber_prices/config_flow_handlers/subentry_flow.py index c2b07d0..6f7a73b 100644 --- a/custom_components/tibber_prices/config_flow/subentry_flow.py +++ b/custom_components/tibber_prices/config_flow_handlers/subentry_flow.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import Any -from custom_components.tibber_prices.config_flow.schemas import ( +from custom_components.tibber_prices.config_flow_handlers.schemas import ( get_select_home_schema, get_subentry_init_schema, ) diff --git a/custom_components/tibber_prices/config_flow/user_flow.py b/custom_components/tibber_prices/config_flow_handlers/user_flow.py similarity index 96% rename from custom_components/tibber_prices/config_flow/user_flow.py rename to custom_components/tibber_prices/config_flow_handlers/user_flow.py index 8617816..812ce83 100644 --- a/custom_components/tibber_prices/config_flow/user_flow.py +++ b/custom_components/tibber_prices/config_flow_handlers/user_flow.py @@ -4,18 +4,18 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any -from custom_components.tibber_prices.config_flow.options_flow import ( +from custom_components.tibber_prices.config_flow_handlers.options_flow import ( TibberPricesOptionsFlowHandler, ) -from custom_components.tibber_prices.config_flow.schemas import ( +from custom_components.tibber_prices.config_flow_handlers.schemas import ( get_reauth_confirm_schema, get_select_home_schema, get_user_schema, ) -from custom_components.tibber_prices.config_flow.subentry_flow import ( +from custom_components.tibber_prices.config_flow_handlers.subentry_flow import ( TibberPricesSubentryFlowHandler, ) -from custom_components.tibber_prices.config_flow.validators import ( +from custom_components.tibber_prices.config_flow_handlers.validators import ( CannotConnectError, InvalidAuthError, validate_api_token, diff --git a/custom_components/tibber_prices/config_flow/validators.py b/custom_components/tibber_prices/config_flow_handlers/validators.py similarity index 100% rename from custom_components/tibber_prices/config_flow/validators.py rename to custom_components/tibber_prices/config_flow_handlers/validators.py diff --git a/docs/development/README.md b/docs/development/README.md index c087a60..4115a6b 100644 --- a/docs/development/README.md +++ b/docs/development/README.md @@ -64,18 +64,21 @@ If you're working with AI tools on this project, the [`AGENTS.md`](../../AGENTS. 4. **Start development environment**: `./scripts/develop` 5. **Make your changes** following the [Coding Guidelines](coding-guidelines.md) 6. **Run linting**: `./scripts/lint` -7. **Test your changes** in the running Home Assistant instance -8. **Commit using Conventional Commits** format -9. **Open a Pull Request** with clear description +7. **Validate integration**: `./scripts/hassfest` +8. **Test your changes** in the running Home Assistant instance +9. **Commit using Conventional Commits** format +10. **Open a Pull Request** with clear description ## ๐Ÿ› ๏ธ Development Tools The project includes several helper scripts in `./scripts/`: - `bootstrap` - Initial setup of dependencies -- `develop` - Start Home Assistant in debug mode +- `develop` - Start Home Assistant in debug mode (auto-cleans .egg-info) +- `clean` - Remove build artifacts and caches - `lint` - Auto-fix code issues with ruff - `lint-check` - Check code without modifications (CI mode) +- `hassfest` - Validate integration structure (JSON, Python syntax, required files) - `setup` - Install development tools (git-cliff, @github/copilot) - `prepare-release` - Prepare a new release (bump version, create tag) - `generate-release-notes` - Generate release notes from commits @@ -132,6 +135,9 @@ custom_components/tibber_prices/ ## ๐Ÿงช Testing ```bash +# Validate integration structure +./scripts/hassfest + # Run all tests pytest tests/ diff --git a/docs/development/coding-guidelines.md b/docs/development/coding-guidelines.md index a1e3ecd..441a717 100644 --- a/docs/development/coding-guidelines.md +++ b/docs/development/coding-guidelines.md @@ -4,14 +4,16 @@ ## Code Style -- **Formatter/Linter**: Ruff (replaces Black, Flake8, isort) -- **Max line length**: 120 characters -- **Max complexity**: 25 (McCabe) -- **Target**: Python 3.13 +- **Formatter/Linter**: Ruff (replaces Black, Flake8, isort) +- **Max line length**: 120 characters +- **Max complexity**: 25 (McCabe) +- **Target**: Python 3.13 Run before committing: + ```bash -./scripts/lint +./scripts/lint # Auto-fix issues +./scripts/hassfest # Validate integration structure ``` ## Import Order @@ -23,7 +25,9 @@ Run before committing: ## Critical Patterns ### Time Handling + Always use `dt_util` from `homeassistant.util`: + ```python from homeassistant.util import dt as dt_util @@ -33,6 +37,7 @@ now = dt_util.now() ``` ### Translation Loading + ```python # In __init__.py async_setup_entry: await async_load_translations(hass, "en") @@ -40,7 +45,9 @@ await async_load_standard_translations(hass, "en") ``` ### Price Data Enrichment + Always enrich raw API data: + ```python from .price_utils import enrich_price_info_with_differences diff --git a/docs/development/setup.md b/docs/development/setup.md index 871935e..7feefd7 100644 --- a/docs/development/setup.md +++ b/docs/development/setup.md @@ -4,9 +4,9 @@ ## Prerequisites -- VS Code with Dev Container support -- Docker installed and running -- GitHub account (for Tibber API token) +- VS Code with Dev Container support +- Docker installed and running +- GitHub account (for Tibber API token) ## Quick Setup @@ -25,11 +25,12 @@ code . ## Development Environment The DevContainer includes: -- Python 3.13 with `.venv` at `/home/vscode/.venv/` -- `uv` package manager (fast, modern Python tooling) -- Home Assistant development dependencies -- Ruff linter/formatter -- Git, GitHub CLI, Node.js, Rust toolchain + +- Python 3.13 with `.venv` at `/home/vscode/.venv/` +- `uv` package manager (fast, modern Python tooling) +- Home Assistant development dependencies +- Ruff linter/formatter +- Git, GitHub CLI, Node.js, Rust toolchain ## Running the Integration @@ -48,6 +49,9 @@ Visit http://localhost:8123 # Check-only (CI mode) ./scripts/lint-check + +# Validate integration structure +./scripts/hassfest ``` See [`AGENTS.md`](../../AGENTS.md) for detailed patterns and conventions. diff --git a/docs/development/testing.md b/docs/development/testing.md index 4d0b22c..6a9a37e 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -2,6 +2,24 @@ > **Note:** This guide is under construction. +## Integration Validation + +Before running tests or committing changes, validate the integration structure: + +```bash +# Run local validation (JSON syntax, Python syntax, required files) +./scripts/hassfest +``` + +This lightweight script checks: + +- โœ“ `config_flow.py` exists +- โœ“ `manifest.json` is valid JSON with required fields +- โœ“ Translation files have valid JSON syntax +- โœ“ All Python files compile without syntax errors + +**Note:** Full hassfest validation runs in GitHub Actions on push. + ## Running Tests ```bash @@ -23,10 +41,11 @@ pytest --cov=custom_components.tibber_prices tests/ ``` Then test in Home Assistant UI: -- Configuration flow -- Sensor states and attributes -- Services -- Translation strings + +- Configuration flow +- Sensor states and attributes +- Services +- Translation strings ## Test Guidelines diff --git a/scripts/clean b/scripts/clean new file mode 100755 index 0000000..3e95522 --- /dev/null +++ b/scripts/clean @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +# script/clean: Clean up development artifacts and caches +# Usage: +# ./scripts/clean # Standard cleanup (recommended) +# ./scripts/clean --deep # Also remove __pycache__ +# ./scripts/clean --minimal # Only critical issues (.egg-info) + +set -e + +cd "$(dirname "$0")/.." + +MINIMAL_MODE=false +DEEP_MODE=false + +if [ "$1" = "--minimal" ]; then + MINIMAL_MODE=true +elif [ "$1" = "--deep" ]; then + DEEP_MODE=true +fi + +if [ "$MINIMAL_MODE" = false ]; then + echo "==> Cleaning development artifacts..." +fi + +# Clean up accidental package installation (always, even in minimal mode) +if [ -d "tibber_prices.egg-info" ]; then + if [ "$MINIMAL_MODE" = false ]; then + echo " โ†’ Removing tibber_prices.egg-info" + fi + rm -rf tibber_prices.egg-info +fi + +# Uninstall if accidentally installed (always, even in minimal mode) +# Try both pip and uv pip for maximum compatibility +PACKAGE_INSTALLED=false +if pip show tibber_prices >/dev/null 2>&1 || uv pip show tibber_prices >/dev/null 2>&1; then + PACKAGE_INSTALLED=true + if [ "$MINIMAL_MODE" = false ]; then + echo " โ†’ Uninstalling accidentally installed package" + fi + # Use regular pip (cleaner output, always works in venv) + pip uninstall -y tibber_prices >/dev/null 2>&1 || true + # Also try uv pip as fallback + uv pip uninstall tibber_prices >/dev/null 2>&1 || true +fi + +# Exit early if minimal mode +if [ "$MINIMAL_MODE" = true ]; then + exit 0 +fi + +# Clean pytest cache +if [ -d ".pytest_cache" ]; then + echo " โ†’ Removing .pytest_cache" + rm -rf .pytest_cache +fi + +# Clean coverage files +if [ -f ".coverage" ]; then + echo " โ†’ Removing .coverage" + rm -f .coverage +fi +if [ -f "coverage.xml" ]; then + echo " โ†’ Removing coverage.xml" + rm -f coverage.xml +fi + +# Clean ruff cache +if [ -d ".ruff_cache" ]; then + echo " โ†’ Removing .ruff_cache" + rm -rf .ruff_cache +fi + +# Optional: Clean __pycache__ (normally not needed, but useful for troubleshooting) +if [ "$DEEP_MODE" = true ]; then + echo " โ†’ Deep clean: Removing all __pycache__ directories" + find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true + echo " โ†’ Deep clean: Removing all .pyc files" + find . -type f -name "*.pyc" -delete 2>/dev/null || true +fi + +echo "" +echo "==> Cleanup complete!" +if [ "$DEEP_MODE" = false ]; then + echo "" + echo "Tip: Use './scripts/clean --deep' to also remove __pycache__ directories" + echo " (normally not needed - __pycache__ speeds up Home Assistant startup)" +fi diff --git a/scripts/develop b/scripts/develop index a9bf096..949f79a 100755 --- a/scripts/develop +++ b/scripts/develop @@ -10,6 +10,10 @@ if [ -z "$VIRTUAL_ENV" ]; then . "$HOME/.venv/bin/activate" fi +# Clean up critical issues (accidental package installations) +# Uses minimal mode to avoid deleting useful caches +"$(dirname "$0")/clean" --minimal + # Create config dir if not present if [ ! -d "${PWD}/config" ]; then mkdir -p "${PWD}/config" diff --git a/scripts/hassfest b/scripts/hassfest new file mode 100755 index 0000000..4fac32f --- /dev/null +++ b/scripts/hassfest @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +# script/hassfest: Lightweight local validation for Home Assistant integration +# Note: This is a simplified version. Full hassfest runs in GitHub Actions. + +set -e + +cd "$(dirname "$0")/.." + +INTEGRATION_PATH="custom_components/tibber_prices" +ERRORS=0 + +echo "==> Running local integration validation..." +echo "" + +# Check 1: config_flow.py exists +echo "โœ“ Checking config_flow.py existence..." +if [ ! -f "$INTEGRATION_PATH/config_flow.py" ]; then + echo " โœ— ERROR: config_flow.py not found" + ERRORS=$((ERRORS + 1)) +else + echo " โœ“ config_flow.py exists" +fi + +# Check 2: manifest.json syntax +echo "โœ“ Checking manifest.json syntax..." +if ! python -m json.tool "$INTEGRATION_PATH/manifest.json" > /dev/null 2>&1; then + echo " โœ— ERROR: manifest.json has invalid JSON syntax" + ERRORS=$((ERRORS + 1)) +else + echo " โœ“ manifest.json is valid JSON" +fi + +# Check 3: Translation files syntax +echo "โœ“ Checking translation files syntax..." +for lang_file in "$INTEGRATION_PATH"/translations/*.json; do + if [ -f "$lang_file" ]; then + lang=$(basename "$lang_file") + if ! python -m json.tool "$lang_file" > /dev/null 2>&1; then + echo " โœ— ERROR: $lang has invalid JSON syntax" + ERRORS=$((ERRORS + 1)) + else + echo " โœ“ $lang is valid JSON" + fi + fi +done + +# Check 4: Custom translation files syntax +if [ -d "$INTEGRATION_PATH/custom_translations" ]; then + echo "โœ“ Checking custom translation files syntax..." + for lang_file in "$INTEGRATION_PATH"/custom_translations/*.json; do + if [ -f "$lang_file" ]; then + lang=$(basename "$lang_file") + if ! python -m json.tool "$lang_file" > /dev/null 2>&1; then + echo " โœ— ERROR: custom_translations/$lang has invalid JSON syntax" + ERRORS=$((ERRORS + 1)) + else + echo " โœ“ custom_translations/$lang is valid JSON" + fi + fi + done +fi + +# Check 5: Python syntax +# Note: We use ast.parse() instead of py_compile to avoid creating __pycache__ artifacts +# ast.parse() validates syntax without writing any files to disk +echo "โœ“ Checking Python syntax..." +PYTHON_ERRORS=0 +while IFS= read -r py_file; do + if ! python -c "import ast; ast.parse(open('$py_file').read())" 2>/dev/null; then + echo " โœ— ERROR: $py_file has syntax errors" + PYTHON_ERRORS=$((PYTHON_ERRORS + 1)) + ERRORS=$((ERRORS + 1)) + fi +done < <(find "$INTEGRATION_PATH" -name "*.py" -type f) +if [ $PYTHON_ERRORS -eq 0 ]; then + echo " โœ“ All Python files have valid syntax" +fi + +# Check 6: Required manifest fields +echo "โœ“ Checking required manifest fields..." +REQUIRED_FIELDS=("domain" "name" "version" "documentation" "issue_tracker" "codeowners") +for field in "${REQUIRED_FIELDS[@]}"; do + if ! python -c "import json; data=json.load(open('$INTEGRATION_PATH/manifest.json')); exit(0 if '$field' in data else 1)" 2>/dev/null; then + echo " โœ— ERROR: manifest.json missing required field: $field" + ERRORS=$((ERRORS + 1)) + fi +done +if [ $ERRORS -eq 0 ]; then + echo " โœ“ All required manifest fields present" +fi + +echo "" +if [ $ERRORS -eq 0 ]; then + echo "==> โœ“ All local validation checks passed!" + echo "" + echo "Note: Full hassfest validation runs in GitHub Actions." + echo " Push your changes to run complete validation." + exit 0 +else + echo "==> โœ— Found $ERRORS error(s)" + echo "" + echo "Note: This is a simplified local validation." + echo " Full hassfest validation runs in GitHub Actions." + exit 1 +fi diff --git a/scripts/lint b/scripts/lint index 15cfa08..2c452a1 100755 --- a/scripts/lint +++ b/scripts/lint @@ -18,7 +18,7 @@ uv run --active ruff format . echo "==> Running Ruff check..." uv run --active ruff check . --fix -# Uninstall editable package to avoid conflicts with Home Assistant -pip uninstall -y tibber_prices >/dev/null 2>&1 || true +# Clean up any accidental package installation from uv run +"$(dirname "$0")/clean" --minimal echo "==> Linting completed!" diff --git a/scripts/lint-check b/scripts/lint-check index 15d6f90..4f773d5 100755 --- a/scripts/lint-check +++ b/scripts/lint-check @@ -16,7 +16,7 @@ uv run --active ruff format . --check echo "==> Checking code with Ruff..." uv run --active ruff check . -# Uninstall editable package to avoid conflicts with Home Assistant -pip uninstall -y tibber_prices >/dev/null 2>&1 || true +# Clean up any accidental package installation from uv run +"$(dirname "$0")/clean" --minimal echo "==> Linting check completed!"