name: Validate on: push: # The branch commit is the release-gate authority. An annotated tag points # to the same SHA and must not duplicate the browser validation jobs. branches: - '**' pull_request: # A new push supersedes an unfinished validation for the same branch or PR. # Exact-SHA release gates never depend on an obsolete commit. concurrency: group: validate-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: provenance: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: { fetch-depth: 0 } - uses: actions/setup-node@v4 with: { node-version: 22 } - name: Validate commit trailers and hook mode env: EVENT_NAME: ${{ github.event_name }} BEFORE_SHA: ${{ github.event.before }} BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.sha }} DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} run: | node scripts/validate-commit-provenance.mjs --check-hook-mode --github-range # Догоняющая проверка процесса (PROCESS.md §10.3). Хуки ловят нарушение на # машине автора, но их можно обойти `--no-verify`, а коммиты идут прямо в dev # без PR — GitHub на своей стороне не блокирует ничего. Это последнее место, # где нарушение правила №1 ловится машиной. Job независимый: краснеет сам и # не роняет остальные, откат — удалить его отсюда, скрипт остаётся рабочим. process-gate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: { fetch-depth: 0 } - uses: actions/setup-node@v4 with: { node-version: 22 } - name: Process gate env: EVENT_NAME: ${{ github.event_name }} BEFORE_SHA: ${{ github.event.before }} BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.sha }} DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} TARGET_REF: ${{ github.ref }} # Публичный репозиторий: штатного токена хватает на чтение issue. GH_TOKEN: ${{ github.token }} run: | node scripts/process-gate.mjs --github-range --issues hacs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: HACS validation uses: hacs/action@main with: category: integration hassfest: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Hassfest validation uses: home-assistant/actions/hassfest@master frontend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - name: Typecheck run: npm run typecheck - name: Unit tests run: npm test - name: Build run: npm run build - name: Card bundle snapshots in sync run: | cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js cmp dist/houseplan-card.js demo/srv/assets/houseplan-card.js smoke: # Gated on `frontend` so a typecheck failure does not burn browser minutes. needs: frontend runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - name: Install Chromium for Playwright run: npx playwright install --with-deps chromium - name: Build a fresh bundle for the smokes run: npm run build && cp dist/houseplan-card.js demo/srv/assets/houseplan-card.js - name: Smoke suite run: | fail=0 mkdir -p /tmp/smoke-logs for f in demo/smoke_*.mjs; do name=$(basename "$f" .mjs) if node "$f" > "/tmp/smoke-logs/$name.log" 2>&1; then echo "ok $name" else echo "FAIL $name" tail -20 "/tmp/smoke-logs/$name.log" fail=1 fi done exit $fail - name: Upload smoke logs if: failure() uses: actions/upload-artifact@v4 with: name: smoke-logs path: /tmp/smoke-logs golden: # Deterministic visual correctness stays in every prerelease gate: it is # inexpensive and catches a different class of regressions than timings. needs: frontend runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - name: Install pinned Chromium run: npx playwright install --with-deps chromium - name: Build the exact source under review run: npm run build && cp dist/houseplan-card.js demo/srv/assets/houseplan-card.js - name: Capture or verify golden matrix id: golden run: | if find demo/golden/baselines -maxdepth 1 -name '*.png' -print -quit | grep -q .; then echo "has_baselines=true" >> "$GITHUB_OUTPUT" npm run golden:verify else echo "has_baselines=false" >> "$GITHUB_OUTPUT" npm run golden:capture fi - name: Upload golden candidates/diffs if: failure() || steps.golden.outputs.has_baselines == 'false' uses: actions/upload-artifact@v4 with: name: golden-images path: artifacts/golden performance_smoke: # Candidate-only catastrophic-regression guard for ordinary pushes and # prereleases. The expensive same-runner comparison lives in performance.yml. needs: frontend runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - name: Install pinned Chromium run: npx playwright install --with-deps chromium - name: Build the exact candidate source run: npm run build && cp dist/houseplan-card.js demo/srv/assets/houseplan-card.js - name: Capture the heaviest Glow state run: | npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --variants=60 --samples=3 --warmups=1 --output=artifacts/performance-smoke/candidate.json - name: Enforce absolute smoke ceilings run: | npm run benchmark:compare -- --absolute-only --budgets=demo/performance/budgets-glow-smoke.json --candidate=artifacts/performance-smoke/candidate.json --output=artifacts/performance-smoke/comparison.json - name: Upload performance smoke report if: always() uses: actions/upload-artifact@v4 with: name: performance-smoke path: artifacts/performance-smoke backend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # Browser fixtures are generated by their real ESM factories and then # validated through the Python CONFIG_SCHEMA/LAYOUT_SCHEMA in the same test. - uses: actions/setup-node@v4 with: { node-version: 22 } - uses: actions/setup-python@v5 with: { python-version: "3.13" } - run: pip install pytest voluptuous pytest-homeassistant-custom-component home-assistant-frontend - name: Backend unit tests (pure + HA harness) run: python -m pytest tests_backend/ -q