Skip to content

Commit 8954429

Browse files
committed
Harden release integration gates
1 parent 95f8bb5 commit 8954429

11 files changed

Lines changed: 422 additions & 21 deletions

.github/workflows/ci.yml

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ on:
1111
description: Run the experimental Flatpak PipeWire routing smoke test
1212
type: boolean
1313
default: false
14+
live_ui_runtime_smoke:
15+
description: Run the live GTK/AT-SPI/PipeWire smoke test
16+
type: boolean
17+
default: false
1418
smoke_only:
1519
description: Skip ordinary CI work and run only the requested experimental smoke job
1620
type: boolean
@@ -131,7 +135,7 @@ jobs:
131135
README.md|pyproject.toml|MANIFEST.in|src/*|tests/*|data/*|extensions/*)
132136
test=true
133137
;;
134-
tools/check_gnome_shell_extension.py|tools/pack_gnome_shell_extension.sh|tools/prepare_release.py|tools/release_gates.py|tools/release_preflight.py|tools/release_runtime_gate.py|tools/release_status.py)
138+
tools/check_gnome_shell_extension.py|tools/pack_gnome_shell_extension.sh|tools/prepare_release.py|tools/release_gates.py|tools/release_preflight.py|tools/release_runtime_gate.py|tools/release_status.py|tools/run_live_ui_runtime_smoke_ci.sh)
135139
test=true
136140
;;
137141
esac
@@ -479,3 +483,55 @@ jobs:
479483
MINI_EQ_FLATPAK_INSTALL_REF: ${{ inputs.flatpak_runtime_install_ref }}
480484
MINI_EQ_FLATPAK_EXPECT_VERSION: ${{ inputs.flatpak_runtime_expected_version }}
481485
run: tools/run_flatpak_runtime_smoke_ci.sh
486+
487+
live-ui-runtime-smoke:
488+
needs: changes
489+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.live_ui_runtime_smoke }}
490+
runs-on: ubuntu-24.04
491+
timeout-minutes: 90
492+
493+
steps:
494+
- name: Require change detection
495+
if: ${{ needs.changes.result != 'success' }}
496+
run: exit 1
497+
498+
- name: Check out repository
499+
uses: actions/checkout@v6
500+
501+
- name: Install live UI runtime dependencies
502+
run: |
503+
sudo apt-get update
504+
sudo apt-get install -y \
505+
appstream \
506+
at-spi2-core \
507+
build-essential \
508+
dbus-user-session \
509+
desktop-file-utils \
510+
gir1.2-adw-1 \
511+
gir1.2-gtk-4.0 \
512+
git \
513+
gnome-shell \
514+
gobject-introspection \
515+
libebur128-1 \
516+
libgirepository1.0-dev \
517+
libglib2.0-dev \
518+
libpipewire-0.3-dev \
519+
libspa-0.2-modules \
520+
meson \
521+
ninja-build \
522+
pipewire \
523+
pipewire-bin \
524+
pkg-config \
525+
python3 \
526+
python3-cairo \
527+
python3-dev \
528+
python3-gi \
529+
python3-numpy \
530+
python3-pip \
531+
python3-pyatspi \
532+
python3-setuptools \
533+
python3-venv \
534+
wireplumber
535+
536+
- name: Build source environment and run live UI smoke
537+
run: tools/run_live_ui_runtime_smoke_ci.sh

.github/workflows/release.yml

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ on:
4747
required: true
4848
type: boolean
4949
default: false
50+
force_live_ui_runtime_smoke:
51+
description: Run the live GTK/AT-SPI/PipeWire smoke even when app-sensitive files did not change.
52+
required: true
53+
type: boolean
54+
default: false
5055

5156
permissions:
5257
contents: read
@@ -139,12 +144,55 @@ jobs:
139144
fi
140145
141146
python3 tools/release_runtime_gate.py \
147+
--scope flatpak \
142148
--tag "$TAG_NAME" \
143149
--release-requested "$release_requested" \
144150
--force "$FORCE_FLATPAK_RUNTIME_SMOKE" \
145151
--github-output "$GITHUB_OUTPUT" \
146152
--github-summary "$GITHUB_STEP_SUMMARY"
147153
154+
live-ui-risk:
155+
name: Detect live UI smoke gate
156+
needs: validate-inputs
157+
runs-on: ubuntu-24.04
158+
159+
outputs:
160+
required: ${{ steps.detect.outputs.required }}
161+
base_tag: ${{ steps.detect.outputs.base_tag }}
162+
163+
steps:
164+
- name: Check out repository
165+
uses: actions/checkout@v6
166+
with:
167+
ref: ${{ inputs.target }}
168+
fetch-depth: 0
169+
170+
- name: Detect app and UI runtime-sensitive changes
171+
id: detect
172+
env:
173+
CREATE_GITHUB_RELEASE: ${{ inputs.create_github_release }}
174+
DRY_RUN: ${{ inputs.dry_run }}
175+
FORCE_LIVE_UI_RUNTIME_SMOKE: ${{ inputs.force_live_ui_runtime_smoke }}
176+
PUBLISH_PYPI: ${{ inputs.publish_pypi }}
177+
PUBLISH_TESTPYPI: ${{ inputs.publish_testpypi }}
178+
TAG_NAME: ${{ inputs.tag_name }}
179+
run: |
180+
set -euo pipefail
181+
182+
release_requested=false
183+
if { [ "$CREATE_GITHUB_RELEASE" = "true" ] || [ "$PUBLISH_TESTPYPI" = "true" ] || [ "$PUBLISH_PYPI" = "true" ]; } \
184+
&& [ "$DRY_RUN" = "false" ]; then
185+
release_requested=true
186+
fi
187+
188+
python3 tools/release_runtime_gate.py \
189+
--scope live-ui \
190+
--tag "$TAG_NAME" \
191+
--release-requested "$release_requested" \
192+
--force "$FORCE_LIVE_UI_RUNTIME_SMOKE" \
193+
--github-output "$GITHUB_OUTPUT" \
194+
--github-summary "$GITHUB_STEP_SUMMARY"
195+
148196
flatpak-runtime-smoke:
149197
name: Flatpak routing runtime smoke
150198
needs: runtime-risk
@@ -172,6 +220,57 @@ jobs:
172220
- name: Build and run Flatpak routing runtime smoke
173221
run: tools/run_flatpak_runtime_smoke_ci.sh
174222

223+
live-ui-runtime-smoke:
224+
name: Live UI runtime smoke
225+
needs: live-ui-risk
226+
if: ${{ needs['live-ui-risk'].outputs.required == 'true' }}
227+
runs-on: ubuntu-24.04
228+
timeout-minutes: 90
229+
230+
steps:
231+
- name: Check out repository
232+
uses: actions/checkout@v6
233+
with:
234+
ref: ${{ inputs.target }}
235+
236+
- name: Install live UI runtime dependencies
237+
run: |
238+
sudo apt-get update
239+
sudo apt-get install -y \
240+
appstream \
241+
at-spi2-core \
242+
build-essential \
243+
dbus-user-session \
244+
desktop-file-utils \
245+
gir1.2-adw-1 \
246+
gir1.2-gtk-4.0 \
247+
git \
248+
gnome-shell \
249+
gobject-introspection \
250+
libebur128-1 \
251+
libgirepository1.0-dev \
252+
libglib2.0-dev \
253+
libpipewire-0.3-dev \
254+
libspa-0.2-modules \
255+
meson \
256+
ninja-build \
257+
pipewire \
258+
pipewire-bin \
259+
pkg-config \
260+
python3 \
261+
python3-cairo \
262+
python3-dev \
263+
python3-gi \
264+
python3-numpy \
265+
python3-pip \
266+
python3-pyatspi \
267+
python3-setuptools \
268+
python3-venv \
269+
wireplumber
270+
271+
- name: Build source environment and run live UI smoke
272+
run: tools/run_live_ui_runtime_smoke_ci.sh
273+
175274
runtime-gate:
176275
name: Require runtime smoke gate
177276
needs:
@@ -199,6 +298,33 @@ jobs:
199298
echo "Flatpak routing runtime smoke was not required for this dispatch."
200299
fi
201300
301+
live-ui-gate:
302+
name: Require live UI smoke gate
303+
needs:
304+
- live-ui-risk
305+
- live-ui-runtime-smoke
306+
if: ${{ always() }}
307+
runs-on: ubuntu-24.04
308+
309+
steps:
310+
- name: Require live-ui-risk job
311+
if: ${{ needs['live-ui-risk'].result != 'success' }}
312+
run: exit 1
313+
314+
- name: Require live UI smoke
315+
if: ${{ needs['live-ui-risk'].outputs.required == 'true' && needs['live-ui-runtime-smoke'].result != 'success' }}
316+
run: |
317+
echo "::error::Live GTK/AT-SPI/PipeWire runtime smoke is required for this release and did not pass."
318+
exit 1
319+
320+
- name: Confirm live UI gate
321+
run: |
322+
if [ "${{ needs['live-ui-risk'].outputs.required }}" = "true" ]; then
323+
echo "Required live UI runtime smoke passed."
324+
else
325+
echo "Live UI runtime smoke was not required for this dispatch."
326+
fi
327+
202328
preflight:
203329
name: Release preflight
204330
needs: validate-inputs
@@ -252,6 +378,7 @@ jobs:
252378
needs:
253379
- validate-inputs
254380
- runtime-gate
381+
- live-ui-gate
255382
- preflight
256383
runs-on: ubuntu-24.04
257384

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ PipeWire/WirePlumber graph with synthetic playback and AT-SPI UI driving:
8787
MINI_EQ_RUN_LIVE_UI=1 .venv/bin/python -m pytest tests/test_mini_eq_live_ui_runtime.py -q
8888
```
8989

90+
The live UI smoke is opt-in locally because it needs nested GNOME Shell,
91+
AT-SPI, PipeWire, and WirePlumber, but it is a blocking `Release` workflow gate
92+
for app-sensitive publish dispatches.
93+
9094
For manual GNOME Shell extension testing against an installed Flatpak build,
9195
run the nested devkit smoke. It loads the extension from this checkout and
9296
launches the installed Flatpak app in the same isolated nested display and

docs/development.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ python3 tools/check_live_ui_runtime.py --timeout 35 --cycles 1
149149
MINI_EQ_RUN_LIVE_UI=1 python3 -m pytest tests/test_mini_eq_live_ui_runtime.py -q
150150
```
151151

152+
These integration smokes are intentionally separate from the default pytest
153+
suite because they require nested GNOME Shell, AT-SPI services, PipeWire, and
154+
WirePlumber. The release workflow promotes the live UI smoke to a blocking gate
155+
for app-sensitive publish dispatches.
156+
152157
## Local Flatpak Build
153158

154159
The Flatpak manifest uses the GNOME runtime. It does not ship a full PipeWire

docs/release.md

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ Run the narrowest gate that covers the release risk:
1414
- **Always:** version metadata, release preflight, GitHub release dry run or
1515
release workflow, TestPyPI install validation, PyPI publish, post-publish
1616
verification, then Flathub stable PR.
17+
- **When application code, UI code, or runtime-sensitive packaging changed:**
18+
live GTK/AT-SPI/PipeWire smoke. Public release workflow dispatches run this
19+
as a blocking job for app-sensitive changes before building publishable
20+
artifacts.
1721
- **When PipeWire, routing, analyzer, Flatpak permissions, runtime dependencies,
1822
or shutdown changed:** local Flatpak install, Flatpak runtime smoke, and
1923
interactive real-music testing before merge or release. Public release
@@ -73,6 +77,22 @@ on widget internals when a unit test can cover the state transition directly.
7377
When in doubt, add a small state-level unit test first, then one AT-SPI smoke
7478
assertion for the visible contract.
7579

80+
The automated release gates are confidence checks, not a proof that every user
81+
graph behaves correctly. Treat their claims narrowly:
82+
83+
- Unit and seam tests verify deterministic model, routing, metadata, and UI
84+
state transitions.
85+
- The release preflight verifies source cleanliness, package build/install,
86+
dependency importability, metadata, and the full default pytest suite.
87+
- The Flatpak routing smoke verifies the installed Flatpak can route and
88+
restore a synthetic stream in an isolated PipeWire/WirePlumber graph.
89+
- The live UI smoke verifies the real GTK app can be driven through AT-SPI
90+
while PipeWire routing, output following, monitor capture, preset reset, and
91+
shutdown are exercised.
92+
- Manual real-session audio testing is still required for changes that depend
93+
on host devices, real music playback, WirePlumber policy, or analyzer
94+
behavior users can perceive.
95+
7696
## Prepare Version
7797

7898
Set the release version once for the shell session:
@@ -192,21 +212,26 @@ run the app interactively with real music before release. Exercise
192212
enable/disable, output switching, preset changes, analyzer display, shutdown,
193213
and stream restoration against the actual desktop audio graph.
194214

195-
Before the manual real-music pass, run the opt-in live UI smoke. It starts a
196-
private PipeWire/WirePlumber graph, synthetic playback, nested headless GNOME
197-
Shell, the real Mini EQ GTK process, and AT-SPI UI controls:
215+
Before the manual real-music pass, run the live UI smoke. It starts a private
216+
PipeWire/WirePlumber graph, synthetic playback, nested headless GNOME Shell,
217+
the real Mini EQ GTK process, and AT-SPI UI controls:
198218

199219
```bash
200220
python3 tools/check_live_ui_runtime.py --timeout 35 --cycles 1
201221
MINI_EQ_RUN_LIVE_UI=1 python3 -m pytest tests/test_mini_eq_live_ui_runtime.py -q
202222
```
203223

224+
The `Release` workflow runs this live UI smoke as a blocking job for app and UI
225+
runtime-sensitive publish dispatches. The pytest wrapper remains opt-in for
226+
local development so ordinary unit test runs stay fast and do not require
227+
nested GNOME Shell or AT-SPI services.
228+
204229
There is an optional hosted Flatpak runtime smoke path in the `CI` workflow.
205230
Use it as extra signal or for smoke-harness work; keep the local runtime smoke
206231
as the release check when app/runtime routing behavior changed. The `Release`
207232
workflow also runs release preflight as a blocking job and has its own blocking
208-
copy of the Flatpak routing smoke gate for public TestPyPI, PyPI, and GitHub
209-
release dispatches.
233+
copy of the Flatpak routing smoke and live UI smoke gates for public TestPyPI,
234+
PyPI, and GitHub release dispatches.
210235

211236
## Package Channels
212237

tests/test_github_workflows.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ def test_release_workflow_blocks_publish_on_runtime_gate() -> None:
3232
assert "runtime-gate" in release_yml.partition("build:")[2].partition("runs-on:")[0]
3333

3434

35+
def test_release_workflow_blocks_publish_on_live_ui_gate() -> None:
36+
release_yml = workflow_text("release.yml")
37+
38+
assert "force_live_ui_runtime_smoke" in release_yml
39+
assert "--scope live-ui" in release_yml
40+
assert "live-ui-risk:" in release_yml
41+
assert "live-ui-runtime-smoke:" in release_yml
42+
assert "live-ui-gate:" in release_yml
43+
assert "tools/run_live_ui_runtime_smoke_ci.sh" in release_yml
44+
assert "needs['live-ui-runtime-smoke'].result != 'success'" in release_yml
45+
assert "live-ui-gate" in release_yml.partition("build:")[2].partition("runs-on:")[0]
46+
47+
3548
def test_release_workflow_blocks_publish_on_release_preflight() -> None:
3649
release_yml = workflow_text("release.yml")
3750

@@ -46,3 +59,5 @@ def test_ci_scope_treats_release_workflow_and_release_gate_tools_as_tested_chang
4659
assert ".github/workflows/*.yml)" in ci_yml
4760
assert "tools/release_gates.py" in ci_yml
4861
assert "tools/release_runtime_gate.py" in ci_yml
62+
assert "live_ui_runtime_smoke" in ci_yml
63+
assert "tools/run_live_ui_runtime_smoke_ci.sh" in ci_yml

0 commit comments

Comments
 (0)