Skip to content

Commit 38701a2

Browse files
committed
Merge branch 'dev' into date-card
2 parents 56a9f49 + 527319a commit 38701a2

360 files changed

Lines changed: 23963 additions & 6779 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/ha-frontend-review/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ When creating a pull request, use `.github/PULL_REQUEST_TEMPLATE.md` as the body
2222

2323
- `yarn lint` passes when practical for the scope.
2424
- `yarn test` or focused relevant tests are green when practical for the scope.
25-
- Tests are added or updated for new data processing and utilities where applicable.
25+
- Each test added by the change protects real logic, not the look of a component.
2626
- User-facing text is localized and follows `ha-frontend-user-facing-text` guidance.
2727
- Components handle loading, error, unavailable, and missing-entity states.
2828
- Entity existence is checked before property access.

.agents/skills/ha-frontend-testing/SKILL.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ Do not pass `--help`, `--background`, or `--modern` to `script/build_frontend`;
4949

5050
Managed app, demo, gallery, and E2E app workflows share one lifetime lock, so only one build or development server can run at a time.
5151

52-
## Unit And Utility Tests
52+
## When To Add Tests
5353

54-
- Add or update Vitest tests for data processing, utility code, and behavior that can be tested without a browser.
55-
- Mock WebSocket connections and API calls at boundaries.
56-
- Cover loading, error, unavailable, and missing-entity states where relevant.
57-
- Test accessibility-sensitive behavior when it can be asserted without brittle DOM internals.
54+
- Write tests for code that computes something: data processing, utility functions, config validation, and what happens when the user interacts with a component.
55+
- Do not write tests that check what a component looks like: its text, CSS classes, styles, or slots. Do not write tests that check the default value of an option.
56+
- A component that only takes data from contexts and helpers and puts it in a template does not need a test.
57+
- If you are not sure a test is useful, describe the test and what it would catch, and let the user decide.
58+
- Tests never talk to a real Home Assistant. Replace `callWS`, `callApi`, and the connection with fakes.
5859

5960
## Dev Servers
6061

.browserslistrc

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,52 @@
11
[modern]
22
# Modern builds target recent browsers supporting the latest features to minimize transpilation, polyfills, etc.
33
# It is served to browsers meeting the following requirements:
4-
# - released in the last year + current alpha/beta versions
5-
# - Firefox extended support release (ESR)
6-
# - with global utilization at or above 0.5%
4+
# - released in the last 2 years + current alpha/beta versions
75
# - exclude dead browsers (no security maintenance for 2+ years)
8-
# - exclude KaiOS, QQ, and UC browsers due to lack of sufficient feature support data
6+
# - exclude QQ, and UC browsers due to lack of sufficient feature support data
97
unreleased versions
10-
last 1 year
11-
Firefox ESR
12-
>= 0.5%
8+
last 2 years
139
not dead
14-
not KaiOS > 0
1510
not QQAndroid > 0
1611
not UCAndroid > 0
1712

1813
[legacy]
19-
# Legacy builds are served when modern requirements are not met and support browsers:
20-
# - released in the last 7 years + current alpha/beta versionss
21-
# - with global utilization at or above 0.05%
22-
# - exclude dead browsers (no security maintenance for 2+ years)
23-
# - exclude Opera Mini which does not support web sockets
24-
unreleased versions
25-
last 7 years
26-
>= 0.05%
27-
not dead
28-
not op_mini all
14+
# Legacy builds are served when modern requirements are not met.
15+
# Floors are pinned explicitly (not usage-based) so the support policy is
16+
# deliberate and does not drift with global usage statistics, which do not
17+
# represent old tablets and wall displays used as Home Assistant dashboards:
18+
# - iOS/Safari >= 12: iPad Air 1 / mini 2 / mini 3 (last supported iOS).
19+
# Older iPads (iPad 2/3/mini 1 on iOS 9.3, iPad 4 on iOS 10.3) cannot run
20+
# the app: their engines lack custom elements, shadow DOM, and/or CSS grid.
21+
# - Chrome >= 59: Fire OS 5 tablets (Fire 7/HD 8/HD 10 through ~2017) have
22+
# their system WebView pinned at Chromium 59 and commonly run Fully Kiosk;
23+
# also covers old kiosk browsers and no-longer-updating webviews above it.
24+
# - Edge >= 79: all Chromium-based Edge. Costs nothing (above the Chrome
25+
# floor); mainly catches Edge 109 on Windows 7/8.1 and update-frozen
26+
# enterprise installs, whose engines can run the legacy build fine.
27+
# - Firefox >= 94: the zero-cost floor, not a chased population — pinning it
28+
# adds no babel transforms, core-js modules, or Lightning CSS prefixes to
29+
# the output. Mozilla-supported Firefox (incl. current ESR) always matches
30+
# [modern]; Firefox on old OSes is not supported (use Chrome 109 instead).
31+
# If Firefox ever becomes the pin forcing extra output, raise it first.
32+
# - Samsung >= 9: Samsung Internet on old Galaxy tablets
33+
Chrome >= 59
34+
ChromeAndroid >= 59
35+
Edge >= 79
36+
Firefox >= 94
37+
FirefoxAndroid >= 94
38+
iOS >= 12
39+
Safari >= 12
40+
Samsung >= 9
2941

3042
[legacy-sw]
31-
# Same as legacy plus supports service workers
32-
unreleased versions
33-
last 7 years
34-
>= 0.05% and supports serviceworkers
35-
not dead
36-
not op_mini all
43+
# Same as legacy, restricted to browsers that support service workers
44+
# (currently resolves to the same set; guards the service worker build if the legacy floor ever drops below them)
45+
Chrome >= 59 and supports serviceworkers
46+
ChromeAndroid >= 59 and supports serviceworkers
47+
Edge >= 79 and supports serviceworkers
48+
Firefox >= 94 and supports serviceworkers
49+
FirefoxAndroid >= 94 and supports serviceworkers
50+
iOS >= 12 and supports serviceworkers
51+
Safari >= 12 and supports serviceworkers
52+
Samsung >= 9 and supports serviceworkers

.github/actions/build/action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ inputs:
1111
is-test:
1212
description: Set IS_TEST for the build (skips source maps and compression)
1313
default: "false"
14+
rspack-cache:
15+
description: rspack persistent cache mode ("readwrite", "readonly", or "" to disable)
16+
default: ""
1417

1518
runs:
1619
using: composite
@@ -21,3 +24,4 @@ runs:
2124
env:
2225
GITHUB_TOKEN: ${{ inputs.github-token }}
2326
IS_TEST: ${{ inputs.is-test }}
27+
RSPACK_CACHE: ${{ inputs.rspack-cache }}

.github/workflows/ci.yaml

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,12 @@ jobs:
9797
run: yarn run test
9898
build:
9999
name: Build frontend
100-
needs:
101-
- prepare-dependencies
102-
- lint
103-
- test
100+
# Runs alongside lint and test rather than after them: the build only needs
101+
# the dependency tree, and with the rspack cache it is no longer expensive
102+
# enough to be worth serialising behind the other checks. The
103+
# cancel-on-failure job below stops the run as soon as a check fails, so a
104+
# broken pull request does not finish building.
105+
needs: prepare-dependencies
104106
runs-on: ubuntu-latest
105107
steps:
106108
- name: Check out files from GitHub
@@ -111,12 +113,26 @@ jobs:
111113
uses: ./.github/actions/setup
112114
with:
113115
node-modules-cache: true
116+
# Read-only reuse of the rspack cache written by the nightly (see
117+
# nightly.yaml). rspack itself decides what is still valid (version +
118+
# buildDependencies + node_modules snapshot), so the GHA key just restores
119+
# the latest nightly cache; no fingerprint, and no save step (CI never
120+
# writes the shared cache).
121+
- name: Restore rspack cache
122+
continue-on-error: true
123+
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
124+
with:
125+
path: .rspack-cache
126+
key: rspack-cache-${{ runner.os }}-${{ github.run_id }}
127+
restore-keys: |
128+
rspack-cache-${{ runner.os }}-
114129
- name: Build Application
115130
uses: ./.github/actions/build
116131
with:
117132
target: build-app
118133
github-token: ${{ secrets.GITHUB_TOKEN }}
119134
is-test: true
135+
rspack-cache: readonly
120136
- name: Upload bundle stats
121137
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
122138
with:
@@ -132,3 +148,54 @@ jobs:
132148
path: hass_frontend/
133149
if-no-files-found: error
134150
retention-days: 7
151+
152+
# Now that the checks run in parallel, a failing lint or test no longer stops
153+
# the build from finishing on its own, so this watches them and cancels the
154+
# whole run on the first failure.
155+
#
156+
# It is a separate job on purpose. Cancelling needs `actions: write`, and the
157+
# other jobs check out the pull request and run its build scripts — handing
158+
# them that scope would give PR-controlled code (or a compromised dependency)
159+
# write access to Actions. This job never checks out the repository, so the
160+
# elevated token stays away from PR code. It also cannot be a job that
161+
# `needs` the checks: that would only start once they have all finished, which
162+
# is exactly too late to cancel anything.
163+
cancel-on-failure:
164+
name: Cancel run on failure
165+
needs: prepare-dependencies
166+
runs-on: ubuntu-latest
167+
permissions:
168+
actions: write
169+
timeout-minutes: 30
170+
steps:
171+
- name: Cancel the run when a check fails
172+
env:
173+
GH_TOKEN: ${{ github.token }}
174+
REPO: ${{ github.repository }}
175+
RUN_ID: ${{ github.run_id }}
176+
run: |
177+
watched='^(Lint and check format|Run tests|Build frontend)$'
178+
while :; do
179+
jobs=$(gh api "repos/$REPO/actions/runs/$RUN_ID/jobs?per_page=100" \
180+
--paginate --jq '.jobs[] | [.name, .status, (.conclusion // "")] | @tsv' \
181+
2>/dev/null || true)
182+
183+
failed=$(printf '%s\n' "$jobs" | awk -F'\t' -v w="$watched" \
184+
'$1 ~ w && ($3 == "failure" || $3 == "timed_out") { print $1 }')
185+
if [ -n "$failed" ]; then
186+
echo "Cancelling the run, these checks failed:"
187+
printf '%s\n' "$failed"
188+
gh run cancel "$RUN_ID" --repo "$REPO" || true
189+
exit 0
190+
fi
191+
192+
found=$(printf '%s\n' "$jobs" | awk -F'\t' -v w="$watched" '$1 ~ w' | wc -l)
193+
running=$(printf '%s\n' "$jobs" | awk -F'\t' -v w="$watched" \
194+
'$1 ~ w && $2 != "completed" { print $1 }')
195+
if [ "$found" -ge 3 ] && [ -z "$running" ]; then
196+
echo "All checks finished without failure"
197+
exit 0
198+
fi
199+
200+
sleep 15
201+
done

.github/workflows/codeql-analysis.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ jobs:
3232
persist-credentials: false
3333

3434
- name: Initialize CodeQL
35-
uses: github/codeql-action/init@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
35+
uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
3636
with:
3737
languages: javascript-typescript
3838
build-mode: none
3939

4040
- name: Perform CodeQL Analysis
41-
uses: github/codeql-action/analyze@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
41+
uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
4242
with:
4343
category: "/language:javascript-typescript"

.github/workflows/e2e.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ jobs:
137137
with:
138138
target: build-gallery
139139
github-token: ${{ secrets.GITHUB_TOKEN }}
140+
is-test: true
140141

141142
- name: Upload gallery build
142143
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

.github/workflows/nightly.yaml

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ jobs:
3838
run: ./script/translations_download
3939
env:
4040
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
41+
# The wheel only builds the app (build-app), which does not merge
42+
# backend translations. Skipping the whole-project backend export (as
43+
# the release does) keeps this off the build's critical path; the full
44+
# translations artifact is produced in parallel by the job below.
45+
SKIP_BACKEND_TRANSLATIONS: "1"
4146

4247
- name: Bump version
4348
run: script/version_bump.js nightly
@@ -53,9 +58,24 @@ jobs:
5358
restore-keys: |
5459
compress-cache-${{ runner.os }}-
5560
61+
# The nightly writes the rspack persistent cache; CI reads it read-only
62+
# (see ci.yaml). rspack invalidates internally (version + buildDependencies
63+
# + node_modules snapshot), so the cache rolls forward daily and a single
64+
# dependency bump keeps most of it warm instead of dropping the lineage.
65+
- name: Restore rspack cache
66+
id: rspack-cache
67+
continue-on-error: true
68+
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
69+
with:
70+
path: .rspack-cache
71+
key: rspack-cache-${{ runner.os }}-${{ github.run_id }}-${{ github.run_attempt }}
72+
restore-keys: |
73+
rspack-cache-${{ runner.os }}-
74+
5675
- name: Build nightly Python wheels
5776
env:
5877
COMPRESS_CACHE_DIR: ${{ github.workspace }}/.compress-cache
78+
RSPACK_CACHE: readwrite
5979
run: |
6080
pip install build
6181
yarn install
@@ -64,7 +84,7 @@ jobs:
6484
rm -rf dist home_assistant_frontend.egg-info
6585
python3 -m build
6686
67-
# Not gated on the restore step: a transient restore failure (it is
87+
# Not gated on the restore steps: a transient restore failure (they are
6888
# continue-on-error) must not stop us persisting a freshly built cache.
6989
- name: Save compression cache
7090
if: success()
@@ -74,8 +94,13 @@ jobs:
7494
path: .compress-cache
7595
key: compress-cache-${{ runner.os }}-${{ github.run_id }}-${{ github.run_attempt }}
7696

77-
- name: Archive translations
78-
run: tar -czvf translations.tar.gz translations
97+
- name: Save rspack cache
98+
if: success()
99+
continue-on-error: true
100+
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
101+
with:
102+
path: .rspack-cache
103+
key: rspack-cache-${{ runner.os }}-${{ github.run_id }}-${{ github.run_attempt }}
79104

80105
- name: Upload build artifacts
81106
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
@@ -84,6 +109,31 @@ jobs:
84109
path: dist/home_assistant_frontend*.whl
85110
if-no-files-found: error
86111

112+
# The full translations (including the slow backend/core export) are only
113+
# needed for the uploaded artifact, not the wheel, so they are downloaded in
114+
# parallel here instead of blocking the build above.
115+
translations:
116+
name: Translations
117+
runs-on: ubuntu-latest
118+
steps:
119+
- name: Checkout the repository
120+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
121+
with:
122+
persist-credentials: false
123+
124+
- name: Setup Node and install
125+
uses: ./.github/actions/setup
126+
with:
127+
immutable: false
128+
129+
- name: Download translations
130+
run: ./script/translations_download
131+
env:
132+
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
133+
134+
- name: Archive translations
135+
run: tar -czvf translations.tar.gz translations
136+
87137
- name: Upload translations
88138
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
89139
with:
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Sync device class constants
2+
3+
# Mirrors the device class constants for Home Assistant Core's into the
4+
# build-time default in src/data/devce_classes.ts and opens a PR
5+
# when it drifts. Reads homeassistant/generated/device_classes.json from core.
6+
7+
on:
8+
workflow_dispatch:
9+
schedule:
10+
- cron: "0 4 * * *" # Daily, 04:00 UTC
11+
12+
permissions:
13+
contents: read
14+
15+
jobs:
16+
sync:
17+
name: Sync
18+
runs-on: ubuntu-latest
19+
permissions:
20+
contents: write
21+
pull-requests: write
22+
steps:
23+
- name: Checkout the repository
24+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
25+
with:
26+
persist-credentials: false
27+
28+
- name: Setup Node and install
29+
uses: ./.github/actions/setup
30+
31+
- name: Regenerate device class constants
32+
run: ./script/gen_device_classes
33+
34+
- name: Format
35+
run: yarn prettier --write src/data/sensor_numeric_device_classes.ts
36+
37+
- name: Create pull request
38+
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
39+
with:
40+
branch: chore/sync-numeric-device-classes
41+
commit-message: Update numeric sensor device classes
42+
title: Update numeric sensor device classes
43+
body: |
44+
Regenerated `SENSOR_NUMERIC_DEVICE_CLASSES` from Home Assistant Core's
45+
`SensorDeviceClass`.
46+
47+
Automated by `.github/workflows/sync-numeric-device-classes.yaml`.

0 commit comments

Comments
 (0)