-
Notifications
You must be signed in to change notification settings - Fork 0
179 lines (166 loc) · 8.53 KB
/
Copy pathci.yml
File metadata and controls
179 lines (166 loc) · 8.53 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
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
# CLAUDE.md has a LENGTH BUDGET, and this is what makes it real. It grew to
# 2254 lines once, at which point sessions stopped reading it to the end and
# started missing the rules near the bottom — which is how "never run the
# tests against the live session" came to be broken twice. Detail belongs in
# docs/<area>.md, a code comment, or ideas.csv; the starting point stays
# short enough to be read in full.
docs-budget:
name: CLAUDE.md stays readable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check the length budget
run: |
BUDGET=400
N=$(wc -l < CLAUDE.md)
echo "CLAUDE.md is $N lines (budget $BUDGET)"
if [ "$N" -gt "$BUDGET" ]; then
echo "::error file=CLAUDE.md::CLAUDE.md is $N lines, over the $BUDGET-line budget."
echo "It is the file every session reads FULLY before doing anything."
echo "Move the detail to docs/<area>.md, to a comment at the code it"
echo "describes, or to ideas.csv — and never add a changelog entry."
exit 1
fi
# Unit tests — GTK4 needs a real compositor even for headless widget
# construction. Weston's headless backend provides a Wayland socket
# with no screen/GPU required.
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y \
python3 python3-gi python3-gi-cairo \
python3-numpy \
gir1.2-gtk-4.0 gir1.2-adw-1 \
gir1.2-gtksource-5 \
libgtk-4-1 libadwaita-1-0 \
libgtksourceview-5-0 \
fonts-dejavu-core \
weston
# pytest is NOT optional here: conftest.py's autouse fixture is what
# resets settings.json between tests, and running the suite through
# plain unittest silently loses it (see the Run tests step).
pip install pymupdf pytest pytest-timeout
- name: Start headless Wayland compositor
run: |
mkdir -p /tmp/xrt
chmod 700 /tmp/xrt
XDG_RUNTIME_DIR=/tmp/xrt \
weston --backend=headless --socket=wayland-ci --idle-time=0 \
>/tmp/weston-ci.log 2>&1 &
for i in $(seq 10); do
[ -S /tmp/xrt/wayland-ci ] && break
sleep 0.5
done
test -S /tmp/xrt/wayland-ci || { echo "weston failed to start"; exit 1; }
echo "XDG_RUNTIME_DIR=/tmp/xrt" >> "$GITHUB_ENV"
echo "WAYLAND_DISPLAY=wayland-ci" >> "$GITHUB_ENV"
# Through PYTEST, exactly as run_tests.sh does, and never `python3
# test_pdfeditor.py`. conftest.py carries an autouse fixture that deletes
# settings.json after every test, and the plain unittest runner does not
# load conftest at all — so `Bindings.save()` (which writes on every
# rebind) leaked one test's button table into every window built after
# it, and a saved pen setting leaked into "nothing saved" tests. That
# made CI fail on 3–4 tests that pass locally, with the failing SET
# moving around as the order shifted. No `-m` filter: CI runs every
# tier, which is what run_tests.sh --full means.
- name: Run tests
env:
GDK_BACKEND: wayland
# a throwaway config home: Bindings.save() persists on every rebind,
# so the suite must never write the settings.json of whoever runs it
XDG_CONFIG_HOME: /tmp/sidemark-ci-config
SIDEMARK_TEST: "1"
# conftest.py refuses a bare pytest when a display is present, so the
# suite can never run against somebody's real desktop. CI IS a bare
# pytest and it DOES have a display — the isolated headless Weston
# started above — which is the exact case the guard exists to allow,
# so it says so deliberately rather than hiding its compositor.
SIDEMARK_ALLOW_BARE_PYTEST: "1"
# The runner has no GPU, so GSK's default renderer runs on llvmpipe
# through EGL — and the log is full of "libEGL warning: failed to get
# driver name", "ZINK: vkCreateInstance failed" and "failed to create
# dri2 screen" before the process dies with "libwayland: error in
# client communication". The cairo renderer takes that whole stack out
# of the picture; the suite asserts on widget state, never on pixels,
# so nothing here needs the GPU path.
GSK_RENDERER: cairo
# -v, not -q: the runner's process has died mid-suite ("libwayland:
# error in client communication" — a hard crash in the GTK/Mesa
# software stack), which kills it before pytest prints anything. With
# one test name per line, the LAST line names the test it died on.
# --timeout: a GPU-less runner does not just crash, it also HANGS —
# a run sat at 85% for 17 minutes and would have burned the job's
# whole budget. 120 s is ~300x the slowest test here (~410 ms), so it
# can only fire on a genuine stall.
#
# `signal`, NOT `thread`. The stall is inside app.run(), i.e. below
# Python in GTK's C code, and the thread method cannot interrupt that —
# it dumps stacks and kills the process, so the run STILL ends with no
# summary and the other failure's traceback is lost with it. SIGALRM
# lands while the GLib loop has the GIL released at its poll, raises in
# the main thread, and lets the suite carry on and report.
# a BLOCK scalar, not a backslash continuation: YAML folds a plain
# scalar's lines with a space and leaves the backslash in, so the shell
# saw an argument literally called " --timeout=120" and pytest exited 4
# before collecting a single test
run: |
python3 -m pytest test_pdfeditor.py -v -rf --tb=short \
--timeout=120 --timeout-method=signal
# The compositor has died mid-run before ("Error flushing display: Broken
# pipe"), which kills the process before any summary is printed — the run
# then reports failures with no tracebacks at all. Its log is the only
# evidence of that, so keep it.
- name: Compositor log (on failure)
if: failure()
run: cat /tmp/weston-ci.log || true
# Install script — verifies files land in the right places and uninstall cleans up.
# Ubuntu is covered here; Arch and Fedora are covered by install.yml.
install-script:
name: install.sh (Ubuntu)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y \
python3 python3-gi python3-gi-cairo \
python3-numpy \
gir1.2-gtk-4.0 gir1.2-adw-1 \
gir1.2-gtksource-5 \
libgtk-4-1 libadwaita-1-0 \
libgtksourceview-5-0 \
librsvg2-bin
pip install pymupdf
- name: Run install.sh
run: GDK_BACKEND=offscreen ./install.sh -y
- name: Verify installed files
run: |
set -e
test -f "$HOME/.local/share/sidemark/sidemark.py" && echo "✓ sidemark.py"
test -x "$HOME/.local/bin/sidemark" && echo "✓ wrapper"
test -f "$HOME/.local/share/applications/de.hspitz.sidemark.desktop" && echo "✓ .desktop"
test -f "$HOME/.local/share/icons/hicolor/scalable/apps/de.hspitz.sidemark.svg" && echo "✓ icon SVG"
test -f "$HOME/.local/share/icons/hicolor/48x48/apps/de.hspitz.sidemark.png" && echo "✓ icon 48px"
test -f "$HOME/.local/share/icons/hicolor/256x256/apps/de.hspitz.sidemark.png" && echo "✓ icon 256px"
- name: Run install.sh --uninstall
run: ./install.sh --uninstall
- name: Verify uninstall cleaned up
run: |
set -e
test ! -f "$HOME/.local/share/sidemark/sidemark.py" && echo "✓ app dir gone"
test ! -x "$HOME/.local/bin/sidemark" && echo "✓ wrapper gone"
test ! -f "$HOME/.local/share/applications/de.hspitz.sidemark.desktop" && echo "✓ .desktop gone"
test ! -f "$HOME/.local/share/icons/hicolor/scalable/apps/de.hspitz.sidemark.svg" && echo "✓ icon gone"