mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-29 21:03:40 +00:00
refactor(config_flow): restructure package to satisfy hassfest validation
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.
This commit is contained in:
parent
decca432df
commit
503075c443
17 changed files with 367 additions and 50 deletions
58
AGENTS.md
58
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 `<ha-alert>` 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
|
||||
|
|
|
|||
52
custom_components/tibber_prices/config_flow.py
Normal file
52
custom_components/tibber_prices/config_flow.py
Normal file
|
|
@ -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",
|
||||
]
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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,
|
||||
|
|
@ -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/
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@
|
|||
- **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
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ 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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,6 +41,7 @@ pytest --cov=custom_components.tibber_prices tests/
|
|||
```
|
||||
|
||||
Then test in Home Assistant UI:
|
||||
|
||||
- Configuration flow
|
||||
- Sensor states and attributes
|
||||
- Services
|
||||
|
|
|
|||
89
scripts/clean
Executable file
89
scripts/clean
Executable file
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
106
scripts/hassfest
Executable file
106
scripts/hassfest
Executable file
|
|
@ -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
|
||||
|
|
@ -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!"
|
||||
|
|
|
|||
|
|
@ -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!"
|
||||
|
|
|
|||
Loading…
Reference in a new issue