-
Notifications
You must be signed in to change notification settings - Fork 202
297 lines (263 loc) · 11.1 KB
/
Copy pathe2e-tests.yml
File metadata and controls
297 lines (263 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
name: E2E Tests
on:
push:
branches: [ main, master ]
paths:
# Only run on code changes, not documentation
- 'src/**'
- 'tests/**'
- 'pyproject.toml'
- 'Dockerfile'
- 'homeassistant-addon/**'
- '.github/workflows/e2e-tests.yml'
workflow_dispatch:
# Limit GITHUB_TOKEN permissions to minimum required
permissions:
contents: read
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.8.0"
# 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:
# Comprehensive E2E validation for main branch
e2e-tests:
name: E2E Tests
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
submodules: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
with:
cache-binary: true
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
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@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
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 3 workers..."
uv run pytest tests/src/e2e/ \
-n3 \
--dist loadscope \
--tb=short \
-v
echo "✅ Full E2E test suite passed"
env:
HAMCP_ENV_FILE: "tests/.env.test"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# In-process MCP server backend (#1527): the SAME full E2E suite, but the
# server-under-test is the in-process MCP server (ha_mcp_tools entry) running inside
# each worker's testcontainer (E2E_BACKEND=embedded), driven over its ingress
# webhook. Runs in parallel with the container lane above (a separate job — it
# does not extend that job's runtime). Matrixed x64 + arm64 to mirror the PR
# lane (pr.yml's e2e-validation-embedded). -n2/-n3 (vs the container lane's -n3):
# each worker additionally builds a ha-mcp wheel and its container preinstalls
# the fastmcp dependency tree before HA boots, so a lower worker count keeps peak
# memory + concurrent-pip pressure in check (arm gets +1). The larger timeout
# covers that per-worker preinstall window.
e2e-tests-embedded:
name: E2E Tests (Embedded Server, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest # Linux x64
- ubuntu-24.04-arm # Linux ARM64
include:
- os: ubuntu-latest
pytest_workers: 2
- os: ubuntu-24.04-arm
pytest_workers: 3
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
submodules: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
with:
cache-binary: true
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
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@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
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 (embedded backend)
run: |
echo "🚀 Running full E2E suite through the in-process MCP server (embedded backend) 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 (embedded backend) passed"
env:
E2E_BACKEND: "embedded"
HAMCP_ENV_FILE: "tests/.env.test"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Update-path e2e lane (#1783/#1785): mirrors pr.yml's e2e-validation-update-path
# on master push. Each run parametrizes over the component source — the released
# ``stable`` git tag (proves a new server release won't break existing installs)
# AND this checkout's working tree (proves the PR's own update machinery still
# works) — with the ha-mcp server installed from PyPI stable in both. The test
# then drives the in-process update that installs THIS commit's freshly built
# wheel over the running server (the real reload/purge/restart path) and asserts
# the server survives. A single ubuntu-latest lane runs both scenarios serially
# (no -n), not the parallel suite. fetch-depth 0 is REQUIRED — the default
# shallow checkout omits the ``stable`` tag the stable scenario checks out from.
e2e-tests-update-path:
name: E2E Tests (Update Path)
runs-on: ubuntu-latest
# 30m: each of the two scenarios (component@stable and component@working-tree)
# does a first bring-up that installs ha-mcp plus the full fastmcp dependency
# tree from PyPI stable before the in-process update swaps in the PR wheel. The
# two containers run sequentially in this one job (observed ~50s per scenario),
# so 30m still fits comfortably.
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
submodules: true
# fetch-depth 0: the update-path scenario checks the custom component
# out at the released ``stable`` git tag, which the default shallow
# checkout does not fetch.
fetch-depth: 0
- name: Verify stable tag is present
run: |
if ! git rev-parse --verify refs/tags/stable >/dev/null 2>&1; then
echo "::error::The 'stable' git tag is missing — the update-path lane checks the released component out from it. Ensure checkout uses fetch-depth: 0."
exit 1
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
with:
cache-binary: true
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
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@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
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 update-path e2e test
# E2E_BACKEND deliberately UNSET (container backend): the test drives its
# own dedicated container end to end and never touches the session
# backend, but conftest's autouse session fixture boots one regardless —
# the default container backend boots it cheaply, while
# E2E_BACKEND=embedded would add a wheel build + in-container preinstall
# of the whole fastmcp tree that nothing in this lane uses.
run: |
echo "🚀 Running the update-path e2e lane (released component + PyPI server → PR wheel)..."
uv run pytest tests/src/e2e/workflows/embedded/test_embedded_update_path.py \
-m update_path \
--tb=short \
-v
echo "✅ Update-path e2e lane passed"
env:
E2E_UPDATE_PATH: "1"
HAMCP_ENV_FILE: "tests/.env.test"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}