mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-05 14:19:04 +00:00
Stage 5 of #780. - Import summary: «Strips left unbound after import: {n}» from the backend `unbound_led_strips` count; «Optimize plans» reports strips passing through walls per space and edits none (AC16). - Linear field for long strips (ТЗ §13.2): pieces of at most the radius along the polyline, emitters thinned to r/4, each piece clipped to the visibility fans of its own emitters as separate clipPath children (no boolean pass per piece), one floor clip for the whole layer, no fan at all where nothing blocks within the radius; a grid index of body faces and boxed inside tests; unchanged fields skip re-diffing. 50×50 on the large house: first stable frame ~1.4 s, warm space ~1.1 s locally. - led-strips-v1 profile: demo/benchmark_led_strips.mjs with the derived large-house fixture (10×5, 50×50, none), absolute limits of the ТЗ table in demo/performance/budgets-led-strips.json, exact counters (zero recomputes on HA ticks/camera/colour, ≤50 cache entries, no growth over 20 cycles); added to the full performance workflow. - Bundle: LAZY_LED_GZIP_CEILING 10 KiB, LAZY_LED_EDITOR_GZIP_CEILING 11 KiB (measured + 10 %, rounded up); overlaps with the initial and editor graphs refused; the lazy editor graph stays inside its ceiling. - Smokes smoke_led_strip_draw/bind/glow, linked in smoke-links; 13 mutants in the registry (7 browser guards in the inventory); config field registry entry `spaces[].led_strips`. - Golden: five new scenes on the `golden-led` space of the visual fixture (`ledStrips` option, the designer's four strips on #868D94), matrix v71. - Docs: LIGHT, DEVICE-PRESENTATION, USER-GUIDE (en/ru), UX-MODES, ARCHITECTURE, ISOMETRIC, CONFIG-COMPATIBILITY, TOUCH-SUPPORT, demo/stand README, performance README; docs/design/led-strips with the unchanged designer archive, two paired frames and ACCEPTANCE.md; both changelogs. Issue: #780 User-Visible: yes
240 lines
14 KiB
YAML
240 lines
14 KiB
YAML
name: Полные бенчмарки производительности
|
|
|
|
on:
|
|
# Every main promotion is a stable-release candidate and must have an
|
|
# exact-SHA full comparison before stable assets are published.
|
|
push:
|
|
branches:
|
|
- main
|
|
paths-ignore:
|
|
- ".github/workflows/**"
|
|
- "docs/**"
|
|
schedule:
|
|
# Минута не круглая (#658): доходит до main вместе со стабильным релизом.
|
|
- cron: "11 4 * * 1"
|
|
workflow_dispatch:
|
|
inputs:
|
|
comparison_ref:
|
|
description: "Optional baseline tag, branch or SHA; empty uses the candidate parent"
|
|
required: false
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: full-performance-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
performance:
|
|
name: Бенчмарки рендера и геометрии
|
|
# Every profile keeps base and candidate sequential on one hosted runner.
|
|
# Independent profile pairs run in parallel: cross-profile timing is never
|
|
# compared, while serialising all profile pairs cannot fit the job timeout.
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 60
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
profile:
|
|
- large-house
|
|
- isometric
|
|
- isometric-backdrop
|
|
- isometric-stage3
|
|
- plan-snap
|
|
- interaction
|
|
- blend
|
|
- overlay
|
|
- space-default
|
|
- space-glow
|
|
- led-strips
|
|
steps:
|
|
- name: Check out candidate
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
path: candidate
|
|
fetch-depth: 2
|
|
|
|
- name: Resolve comparison SHA
|
|
id: base
|
|
working-directory: candidate
|
|
env:
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
PUSH_BEFORE_SHA: ${{ github.event.before }}
|
|
MANUAL_BASE: ${{ inputs.comparison_ref }}
|
|
# #587: кандидат стабильного релиза судится о предыдущий стабильный
|
|
# тег, а не о прошлую вершину main. Признак кандидата — трейлер
|
|
# `Release:` в сообщении head-коммита, поэтому оно едет в скрипт.
|
|
HEAD_MESSAGE: ${{ github.event.head_commit.message }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then
|
|
git fetch --force --tags --prune --unshallow origin
|
|
else
|
|
git fetch --force --tags --prune origin
|
|
fi
|
|
# Решение целиком в скрипте: его отрицательные случаи проверяются
|
|
# фикстурами, а shell-развилку прогнать тестом нельзя (#556, #587).
|
|
node scripts/performance-baseline.mjs
|
|
|
|
- name: Check out base SHA
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
ref: ${{ steps.base.outputs.sha }}
|
|
path: baseline
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
cache-dependency-path: |
|
|
candidate/package-lock.json
|
|
baseline/package-lock.json
|
|
|
|
- name: Install candidate and baseline dependencies
|
|
run: npm ci --prefix candidate && npm ci --prefix baseline
|
|
|
|
# То же, что в validate.yml: кэш браузеров, apt не трогаем (#206).
|
|
- name: Кэш браузеров Playwright
|
|
id: pw
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
|
|
with:
|
|
path: ~/.cache/ms-playwright
|
|
key: playwright-${{ runner.os }}-${{ hashFiles('candidate/package-lock.json') }}
|
|
- name: Install pinned Chromium
|
|
if: steps.pw.outputs.cache-hit != 'true'
|
|
working-directory: candidate
|
|
run: npx playwright install chromium
|
|
|
|
- name: Build both exact source trees
|
|
run: |
|
|
npm --prefix candidate run build
|
|
(cd candidate && node scripts/bundle-sync.mjs)
|
|
npm --prefix baseline run build
|
|
if [ -f baseline/scripts/bundle-sync.mjs ]; then
|
|
(cd baseline && node scripts/bundle-sync.mjs)
|
|
else
|
|
echo "::notice::The comparison base predates bundle-sync.mjs; materializing its freshly built bundle with the legacy copy path."
|
|
mkdir -p baseline/custom_components/houseplan/frontend baseline/demo/srv/assets
|
|
cp baseline/dist/houseplan-card.js baseline/custom_components/houseplan/frontend/houseplan-card.js
|
|
cp baseline/dist/houseplan-card.js baseline/demo/srv/assets/houseplan-card.js
|
|
fi
|
|
|
|
- name: Capture base and candidate profile
|
|
working-directory: candidate
|
|
env:
|
|
PROFILE: ${{ matrix.profile }}
|
|
run: |
|
|
mkdir -p ../artifacts/performance
|
|
case "$PROFILE" in
|
|
large-house)
|
|
npm run benchmark:large-house -- --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/baseline.json
|
|
npm run benchmark:large-house -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/candidate.json
|
|
;;
|
|
isometric)
|
|
npm run benchmark:large-house-isometric -- --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/isometric-baseline.json
|
|
npm run benchmark:large-house-isometric -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/isometric-candidate.json
|
|
;;
|
|
isometric-backdrop)
|
|
npm run benchmark:large-house-isometric-backdrop -- --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/isometric-backdrop-baseline.json
|
|
npm run benchmark:large-house-isometric-backdrop -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/isometric-backdrop-candidate.json
|
|
;;
|
|
isometric-stage3)
|
|
npm run benchmark:isometric-stage3-dense -- --allow-stage2-base --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/isometric-stage3-baseline.json
|
|
npm run benchmark:isometric-stage3-dense -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/isometric-stage3-candidate.json
|
|
;;
|
|
plan-snap)
|
|
npm run benchmark:large-house-plan-snap -- --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/plan-snap-baseline.json
|
|
npm run benchmark:large-house-plan-snap -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/plan-snap-candidate.json
|
|
;;
|
|
interaction)
|
|
npm run benchmark:large-house-interaction -- --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/interaction-baseline.json
|
|
npm run benchmark:large-house-interaction -- --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/interaction-candidate.json
|
|
;;
|
|
blend)
|
|
npm run benchmark:glow -- --profile=large-light-blend-v1 --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/blend-baseline.json
|
|
npm run benchmark:glow -- --profile=large-light-blend-v1 --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/blend-candidate.json
|
|
;;
|
|
overlay)
|
|
npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/overlay-baseline.json
|
|
npm run benchmark:glow -- --profile=large-house-glow-overlay-v1 --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/overlay-candidate.json
|
|
if ! grep -q "glow_enabled" ../baseline/src/logic.ts; then
|
|
echo "Base predates independent Glow; bootstrap relative overlay baseline, keep absolute gate"
|
|
cp ../artifacts/performance/overlay-candidate.json ../artifacts/performance/overlay-baseline.json
|
|
fi
|
|
;;
|
|
space-default)
|
|
npm run benchmark:glow -- --profile=large-space-card-default-v1 --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/space-default-baseline.json
|
|
npm run benchmark:glow -- --profile=large-space-card-default-v1 --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/space-default-candidate.json
|
|
;;
|
|
space-glow)
|
|
npm run benchmark:glow -- --profile=large-space-card-glow-v1 --target-root=../baseline --samples=7 --warmups=1 --output=../artifacts/performance/space-glow-baseline.json
|
|
npm run benchmark:glow -- --profile=large-space-card-glow-v1 --target-root=. --samples=7 --warmups=1 --output=../artifacts/performance/space-glow-candidate.json
|
|
if ! grep -q "light_pools" ../baseline/src/space-card.ts; then
|
|
echo "Base predates opt-in static Glow; bootstrap relative baseline, keep absolute gate"
|
|
cp ../artifacts/performance/space-glow-candidate.json ../artifacts/performance/space-glow-baseline.json
|
|
fi
|
|
;;
|
|
led-strips)
|
|
# #780 led-strips-v1: base predates LED strips, so the profile is
|
|
# judged by its absolute limits inside the runner; the zero-LED
|
|
# View stays covered by the relative interaction profile above.
|
|
npm run benchmark:led-strips -- --size=10x5 --samples=7 --warmups=1 --output=../artifacts/performance/led-strips-10x5.json
|
|
npm run benchmark:led-strips -- --size=50x50 --samples=7 --warmups=1 --output=../artifacts/performance/led-strips-50x50.json
|
|
npm run benchmark:led-strips -- --size=none --samples=7 --warmups=1 --output=../artifacts/performance/led-strips-none.json
|
|
;;
|
|
*)
|
|
echo "::error::Unknown performance profile: $PROFILE"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
- name: Enforce relative and absolute performance budget
|
|
working-directory: candidate
|
|
env:
|
|
PROFILE: ${{ matrix.profile }}
|
|
run: |
|
|
case "$PROFILE" in
|
|
large-house)
|
|
npm run benchmark:compare -- --baseline=../artifacts/performance/baseline.json --candidate=../artifacts/performance/candidate.json --output=../artifacts/performance/comparison.json
|
|
;;
|
|
isometric)
|
|
npm run benchmark:compare -- --budgets=demo/performance/budgets-large-house-isometric.json --baseline=../artifacts/performance/isometric-baseline.json --candidate=../artifacts/performance/isometric-candidate.json --baseline-sha="$(git -C ../baseline rev-parse HEAD)" --candidate-sha="$(git rev-parse HEAD)" --output=../artifacts/performance/isometric-comparison.json
|
|
;;
|
|
isometric-backdrop)
|
|
npm run benchmark:compare -- --budgets=demo/performance/budgets-large-house-isometric-backdrop.json --baseline=../artifacts/performance/isometric-backdrop-baseline.json --candidate=../artifacts/performance/isometric-backdrop-candidate.json --baseline-sha="$(git -C ../baseline rev-parse HEAD)" --candidate-sha="$(git rev-parse HEAD)" --output=../artifacts/performance/isometric-backdrop-comparison.json
|
|
;;
|
|
isometric-stage3)
|
|
npm run benchmark:compare -- --budgets=demo/performance/budgets-isometric-stage3-dense.json --baseline=../artifacts/performance/isometric-stage3-baseline.json --candidate=../artifacts/performance/isometric-stage3-candidate.json --baseline-sha="$(git -C ../baseline rev-parse HEAD)" --candidate-sha="$(git rev-parse HEAD)" --output=../artifacts/performance/isometric-stage3-comparison.json
|
|
;;
|
|
plan-snap)
|
|
npm run benchmark:compare -- --budgets=demo/performance/budgets-large-house-plan-snap.json --baseline=../artifacts/performance/plan-snap-baseline.json --candidate=../artifacts/performance/plan-snap-candidate.json --output=../artifacts/performance/plan-snap-comparison.json
|
|
;;
|
|
interaction)
|
|
npm run benchmark:compare -- --budgets=demo/performance/budgets-large-house-interaction.json --baseline=../artifacts/performance/interaction-baseline.json --candidate=../artifacts/performance/interaction-candidate.json --output=../artifacts/performance/interaction-comparison.json
|
|
;;
|
|
blend)
|
|
npm run benchmark:compare -- --budgets=demo/performance/budgets-large-light-blend.json --baseline=../artifacts/performance/blend-baseline.json --candidate=../artifacts/performance/blend-candidate.json --output=../artifacts/performance/blend-comparison.json
|
|
;;
|
|
overlay)
|
|
npm run benchmark:compare -- --budgets=demo/performance/budgets-large-house-glow-overlay.json --baseline=../artifacts/performance/overlay-baseline.json --candidate=../artifacts/performance/overlay-candidate.json --output=../artifacts/performance/overlay-comparison.json
|
|
;;
|
|
space-default)
|
|
npm run benchmark:compare -- --budgets=demo/performance/budgets-large-space-card-default.json --baseline=../artifacts/performance/space-default-baseline.json --candidate=../artifacts/performance/space-default-candidate.json --output=../artifacts/performance/space-default-comparison.json
|
|
;;
|
|
space-glow)
|
|
npm run benchmark:compare -- --budgets=demo/performance/budgets-large-space-card-glow.json --baseline=../artifacts/performance/space-glow-baseline.json --candidate=../artifacts/performance/space-glow-candidate.json --output=../artifacts/performance/space-glow-comparison.json
|
|
;;
|
|
led-strips)
|
|
echo "led-strips-v1: absolute limits and counters already enforced by the runner (budgets-led-strips.json)"
|
|
;;
|
|
esac
|
|
|
|
- name: Upload full performance report
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: full-performance-${{ matrix.profile }}
|
|
path: artifacts/performance
|