Skip to content

Commit 7b2de30

Browse files
authored
feat: better platform support along with CI and docs (#250)
1 parent c23ec03 commit 7b2de30

22 files changed

Lines changed: 552 additions & 76 deletions

.github/workflows/ci.yml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ jobs:
100100
run: uv run pip-audit --skip-editable
101101

102102
selftest:
103-
name: Selftests (${{ matrix.python-version }})
104-
runs-on: ubuntu-latest
103+
name: Selftests (${{ matrix.os }} / ${{ matrix.python-version }})
104+
runs-on: ${{ matrix.os }}
105105
strategy:
106+
fail-fast: false
106107
matrix:
108+
os: [ubuntu-latest, macos-latest]
107109
python-version: ["3.10", "3.12"]
108110
steps:
109111
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
@@ -130,6 +132,20 @@ jobs:
130132
if: always()
131133
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
132134
with:
133-
name: selftest-results-py${{ matrix.python-version }}
135+
name: selftest-results-${{ matrix.os }}-py${{ matrix.python-version }}
134136
path: selftest-results.xml
135137

138+
selftests-status:
139+
name: Selftests Status
140+
if: always()
141+
needs: [selftest]
142+
runs-on: ubuntu-latest
143+
steps:
144+
- name: Check selftest matrix result
145+
run: |
146+
if [ "${{ needs.selftest.result }}" = "failure" ] || [ "${{ needs.selftest.result }}" = "cancelled" ]; then
147+
echo "Selftests failed or were cancelled"
148+
exit 1
149+
fi
150+
echo "Selftests passed or were skipped"
151+
Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
1-
name: RHEL Smoke
1+
name: Linux Smoke
22

33
on:
44
push:
55
branches: [main]
66
paths:
77
- 'docker/rhel*/**'
8-
- 'docker/rhel-smoke.py'
8+
- 'docker/opensuse-leap/**'
9+
- 'docker/playwright-smoke.py'
910
- 'scripts/rhel-smoke.sh'
11+
- 'scripts/opensuse-leap-smoke.sh'
1012
- 'justfile'
1113
- 'pyproject.toml'
1214
- 'uv.lock'
1315
- 'src/**'
1416
- 'selftests/**'
15-
- '.github/workflows/rhel-smoke.yml'
17+
- '.github/workflows/linux-smoke.yml'
1618
pull_request:
1719
paths:
1820
- 'docker/rhel*/**'
19-
- 'docker/rhel-smoke.py'
21+
- 'docker/opensuse-leap/**'
22+
- 'docker/playwright-smoke.py'
2023
- 'scripts/rhel-smoke.sh'
24+
- 'scripts/opensuse-leap-smoke.sh'
2125
- 'justfile'
2226
- 'pyproject.toml'
2327
- 'uv.lock'
2428
- 'src/**'
2529
- 'selftests/**'
26-
- '.github/workflows/rhel-smoke.yml'
30+
- '.github/workflows/linux-smoke.yml'
2731

2832
jobs:
2933
smoke:
@@ -32,7 +36,7 @@ jobs:
3236
strategy:
3337
fail-fast: false
3438
matrix:
35-
version: [rhel9, rhel10]
39+
version: [rhel9, rhel10, opensuse-leap]
3640
steps:
3741
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
3842
with:

.github/workflows/mac-smoke.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Mac Smoke
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'docker/playwright-smoke.py'
8+
- 'pyproject.toml'
9+
- 'uv.lock'
10+
- 'src/**'
11+
- '.github/workflows/mac-smoke.yml'
12+
pull_request:
13+
paths:
14+
- 'docker/playwright-smoke.py'
15+
- 'pyproject.toml'
16+
- 'uv.lock'
17+
- 'src/**'
18+
- '.github/workflows/mac-smoke.yml'
19+
20+
jobs:
21+
smoke:
22+
name: macos-latest
23+
runs-on: macos-latest
24+
steps:
25+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
26+
with:
27+
persist-credentials: false
28+
29+
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
30+
with:
31+
enable-cache: true
32+
33+
- name: Install dependencies
34+
run: uv sync --frozen
35+
36+
- name: vip install (Playwright Chromium)
37+
run: uv run vip install
38+
39+
- name: Run smoke
40+
run: uv run python docker/playwright-smoke.py

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ demo.md
1313
.claude/worktrees/
1414
.claude/settings.local.json
1515
.worktrees/
16-
docs/superpowers
1716

1817
# Website
1918
website/node_modules/

docker/opensuse-leap/Dockerfile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# openSUSE Leap headless Chromium smoke test image
2+
FROM opensuse/leap:15
3+
4+
# python312 is required by vip's pyproject.toml. procps provides ps.
5+
# gzip and tar are needed for the uv tarball extraction in the COPY --from below.
6+
RUN zypper -n install \
7+
python312 python312-pip procps gzip tar \
8+
&& zypper clean --all
9+
10+
# Install uv (matches version pinned in vip's main Dockerfiles)
11+
COPY --from=ghcr.io/astral-sh/uv:0.6.3 /uv /uvx /usr/local/bin/
12+
13+
WORKDIR /app
14+
COPY . .
15+
16+
# Sync vip's Python dependencies. Frozen lockfile reproduces CI exactly.
17+
RUN uv sync --frozen
18+
19+
# Install Chromium runtime libs (via zypper) and the Playwright browser bundle via vip.
20+
# Runs as root inside Docker, so vip install invokes zypper directly.
21+
RUN uv run vip install
22+
23+
CMD ["uv", "run", "python", "/app/docker/playwright-smoke.py"]

docker/playwright-smoke.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"""Headless Chromium smoke test usable on any vip-supported platform.
2+
3+
Mirrors microsoft/playwright#40312's manual test: launches Chromium
4+
headlessly, exercises basic page interaction and JS evaluation, and
5+
takes a screenshot. Exits non-zero on any failure so docker run /
6+
the GitHub Actions step propagates the result.
7+
"""
8+
9+
import platform as _stdplatform
10+
import re
11+
import sys
12+
from pathlib import Path
13+
14+
from playwright.sync_api import sync_playwright
15+
16+
17+
def _platform_label() -> str:
18+
"""Best-effort short label like 'rhel9', 'leap15', 'macos-14.5', or 'unknown'."""
19+
if sys.platform == "darwin":
20+
ver = _stdplatform.mac_ver()[0] or "unknown"
21+
return f"macos-{ver}"
22+
try:
23+
text = Path("/etc/os-release").read_text()
24+
except OSError:
25+
return "unknown"
26+
distro_id = ""
27+
version_major = ""
28+
m_id = re.search(r'^ID="?([^"\n]+)"?', text, re.MULTILINE)
29+
if m_id:
30+
distro_id = m_id.group(1).strip()
31+
m_ver = re.search(r'^VERSION_ID="?(\d+)', text, re.MULTILINE)
32+
if m_ver:
33+
version_major = m_ver.group(1)
34+
label = f"{distro_id}{version_major}".strip()
35+
return label or "unknown"
36+
37+
38+
def main() -> None:
39+
label = _platform_label()
40+
with sync_playwright() as p:
41+
browser = p.chromium.launch(headless=True, channel="chromium-headless-shell")
42+
page = browser.new_page()
43+
page.goto(
44+
"data:text/html,<title>vip smoke</title><h1 id=h>ok</h1><input id=i><div id=r></div>"
45+
)
46+
title = page.title()
47+
if title != "vip smoke":
48+
raise RuntimeError(f"unexpected title: {title!r}")
49+
page.fill("#i", f"hello from {label}")
50+
page.evaluate(
51+
"document.getElementById('r').textContent = document.getElementById('i').value"
52+
)
53+
text = page.text_content("#r")
54+
if text != f"hello from {label}":
55+
raise RuntimeError(f"unexpected DOM text: {text!r}")
56+
ua = page.evaluate("navigator.userAgent")
57+
if "HeadlessChrome" not in ua:
58+
raise RuntimeError(f"unexpected UA: {ua!r}")
59+
page.screenshot(path="/tmp/smoke.png")
60+
browser.close()
61+
print(f"PASS: {label} headless chromium smoke")
62+
63+
64+
if __name__ == "__main__":
65+
main()

docker/rhel-smoke.py

Lines changed: 0 additions & 54 deletions
This file was deleted.

docker/rhel10/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ RUN uv sync --frozen
2020
# Runs as root inside Docker, so vip install invokes dnf directly.
2121
RUN uv run vip install
2222

23-
CMD ["uv", "run", "python", "/app/docker/rhel-smoke.py"]
23+
CMD ["uv", "run", "python", "/app/docker/playwright-smoke.py"]

docker/rhel9/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ RUN uv sync --frozen
2020
# Runs as root inside Docker, so vip install invokes dnf directly.
2121
RUN uv run vip install
2222

23-
CMD ["uv", "run", "python", "/app/docker/rhel-smoke.py"]
23+
CMD ["uv", "run", "python", "/app/docker/playwright-smoke.py"]

justfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,7 @@ rhel9-smoke:
110110
# Build and run the RHEL 10 headless Chromium smoke test
111111
rhel10-smoke:
112112
./scripts/rhel-smoke.sh 10
113+
114+
# Build and run the openSUSE Leap headless Chromium smoke test
115+
opensuse-leap-smoke:
116+
./scripts/opensuse-leap-smoke.sh

0 commit comments

Comments
 (0)