Skip to content

feat: detect last_changed/last_updated duration math and suggest for: field (#1157) #3290

feat: detect last_changed/last_updated duration math and suggest for: field (#1157)

feat: detect last_changed/last_updated duration math and suggest for: field (#1157) #3290

Workflow file for this run

name: PR Validation Pipeline
permissions:
contents: read
on:
pull_request:
branches: [ master ]
workflow_dispatch:
env:
PYTHON_VERSION: "3.13"
UV_CACHE_DIR: /tmp/.uv-cache
# renovate: datasource=docker depName=ghcr.io/home-assistant/home-assistant
HA_IMAGE_GHCR: "ghcr.io/home-assistant/home-assistant:2026.5.3"
# Disable Testcontainers' Ryuk reaper: it has been reported to leave
# zombie containers on GHA runners (see #366, Ilya0527 2026-05-18).
# The E2E fixture in tests/src/e2e/conftest.py relies instead on the
# enclosing ``with container:`` context manager — Python guarantees
# its ``__exit__`` fires on both normal and exception flows, so the
# Ryuk safety net is not needed for deterministic cleanup. Refs #366.
TESTCONTAINERS_RYUK_DISABLED: "true"
jobs:
lockfile:
name: Check uv.lock
runs-on: ubuntu-latest
container:
# renovate: datasource=docker depName=ghcr.io/astral-sh/uv
image: ghcr.io/astral-sh/uv:0.11.15-python3.13-trixie-slim
timeout-minutes: 2
steps:
- uses: actions/checkout@v6
- name: Verify uv.lock is in sync with pyproject.toml
run: uv lock --check
lint:
name: Ruff Lint
runs-on: ubuntu-latest
container:
# renovate: datasource=docker depName=ghcr.io/astral-sh/uv
image: ghcr.io/astral-sh/uv:0.11.15-python3.13-trixie-slim
timeout-minutes: 5
steps:
- name: Install git for changed-files diff
run: apt-get update -qq && apt-get install -y -qq git >/dev/null 2>&1
- uses: actions/checkout@v6
- name: Install dependencies
run: uv sync --dev
- name: Run ruff check
run: uv run ruff check src/ tests/ homeassistant-addon/ homeassistant-addon-webhook-proxy/ scripts/
- name: Run ruff format check on changed Python files
run: |
# actions/checkout@v6 sets safe.directory in a temp HOME that this
# step doesn't inherit; re-add it here so git operations succeed
# when the checkout dir is owned by a different UID than the
# container's root.
git config --global --add safe.directory "$GITHUB_WORKSPACE"
# Changed-files-only gate per the maintainer call on issue #1318:
# new edits must be formatted; pre-existing format-debt on
# untouched files stays grandfathered. Avoids the disruption of a
# repo-wide sweep while still preventing fresh debt from landing.
if [ -z "${GITHUB_BASE_REF}" ]; then
echo "No PR base ref (workflow_dispatch); skipping format check."
exit 0
fi
git fetch --depth=1 origin "${GITHUB_BASE_REF}"
base_sha=$(git rev-parse "origin/${GITHUB_BASE_REF}")
changed_py=$(git diff --name-only --diff-filter=ACMR "${base_sha}" -- '*.py')
if [ -z "$changed_py" ]; then
echo "No Python files changed; skipping ruff format check."
exit 0
fi
count=$(echo "$changed_py" | wc -l)
echo "Checking ruff format on $count changed Python files:"
echo "$changed_py" | sed 's/^/ /'
echo "$changed_py" | xargs uv run ruff format --check
ast-grep:
name: AST Lint
runs-on: ubuntu-latest
container:
# renovate: datasource=docker depName=ghcr.io/astral-sh/uv
image: ghcr.io/astral-sh/uv:0.11.15-python3.13-trixie-slim
timeout-minutes: 2
steps:
- uses: actions/checkout@v6
- name: Install dependencies
run: uv sync --dev
- name: Run ast-grep
run: uv run ast-grep scan
mypy:
name: Mypy Type Check
runs-on: ubuntu-latest
container:
# renovate: datasource=docker depName=ghcr.io/astral-sh/uv
image: ghcr.io/astral-sh/uv:0.11.15-python3.13-trixie-slim
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
- name: Install dependencies
run: uv sync --dev
- name: Run mypy
run: |
uv run mypy src/ homeassistant-addon/ scripts/
uv run mypy homeassistant-addon-webhook-proxy/start.py
# Fast unit tests (no Docker, no HA instance needed)
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
container:
# renovate: datasource=docker depName=ghcr.io/astral-sh/uv
image: ghcr.io/astral-sh/uv:0.11.15-python3.13-trixie-slim
timeout-minutes: 5
steps:
- name: Install git for submodule checkout
run: apt-get update -qq && apt-get install -y -qq git >/dev/null 2>&1
- uses: actions/checkout@v6
with:
submodules: true
- name: Install dependencies
run: uv sync --all-extras --dev
- name: Run unit tests
run: uv run pytest tests/src/unit/ -n auto --tb=short -v
# Comprehensive E2E validation for all PRs
e2e-validation:
name: E2E Validation (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest # Linux x64
- ubuntu-24.04-arm # Linux ARM64
include:
- os: ubuntu-latest
pytest_workers: 3
- os: ubuntu-24.04-arm
pytest_workers: 4
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
cache-binary: true
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "latest"
- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: uv sync --all-extras --dev
- name: Cache HA Docker image
id: cache-ha-image
uses: actions/cache@v5
with:
path: /tmp/ha-image.tar
key: ha-image-${{ env.HA_IMAGE_GHCR }}-${{ runner.arch }}
- name: Load cached HA image
if: steps.cache-ha-image.outputs.cache-hit == 'true'
run: docker load -i /tmp/ha-image.tar
- name: Pull HA image (GHCR → Docker Hub fallback)
if: steps.cache-ha-image.outputs.cache-hit != 'true'
run: |
HA_VERSION="${HA_IMAGE_GHCR##*:}"
HA_IMAGE_DOCKERHUB="homeassistant/home-assistant:${HA_VERSION}"
for registry in "$HA_IMAGE_GHCR" "$HA_IMAGE_DOCKERHUB"; do
echo "Trying $registry..."
if docker pull "$registry"; then
if [ "$registry" != "$HA_IMAGE_GHCR" ]; then
docker tag "$registry" "$HA_IMAGE_GHCR"
fi
docker save "$HA_IMAGE_GHCR" -o /tmp/ha-image.tar
echo "Pulled and cached from $registry"
exit 0
fi
echo "Failed to pull from $registry, trying next..."
sleep 15
done
echo "All registries failed" && exit 1
- name: Run full E2E test suite
run: |
echo "🚀 Running full E2E test suite with ${{ matrix.pytest_workers }} workers..."
uv run pytest tests/src/e2e/ \
-n${{ matrix.pytest_workers }} \
--dist loadscope \
--tb=short \
-v
echo "✅ Full E2E test suite passed"
env:
HAMCP_ENV_FILE: "tests/.env.test"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Docker and Add-on validation
docker-validation:
name: Docker & Add-on Validation
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
cache-binary: true
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "latest"
- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Install test dependencies
run: uv sync --dev
- name: Run add-on tests
run: uv run pytest tests/addon/ --ignore=tests/addon/test_skills_config.py --tb=short -v
- name: Validate docker-compose configuration
run: uv run pytest tests/test_docker/test_docker_compose.py -v
- name: Build and test standalone Docker image
run: uv run pytest tests/test_docker/test_docker_build.py -v