-
Notifications
You must be signed in to change notification settings - Fork 2
412 lines (367 loc) · 17.8 KB
/
Copy pathdocker.yml
File metadata and controls
412 lines (367 loc) · 17.8 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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
name: AutoControl Docker CI
on:
push:
branches: [ "dev", "main" ]
paths:
- "docker/**"
- "je_auto_control/**"
- "pyproject.toml"
- ".github/workflows/docker.yml"
pull_request:
branches: [ "dev", "main" ]
paths:
- "docker/**"
- "je_auto_control/**"
- "pyproject.toml"
- ".github/workflows/docker.yml"
permissions:
contents: read
jobs:
build-image:
name: Build AutoControl container
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/setup-buildx-action@v3 # NOSONAR githubactions:S7637
- name: Build image (no push)
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/build-push-action@v5 # NOSONAR githubactions:S7637
with:
context: .
file: docker/Dockerfile
tags: autocontrol:ci
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Image size
run: docker image inspect autocontrol:ci --format='size={{.Size}} bytes'
headless-tests:
name: Headless pytest inside the image
needs: build-image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/setup-buildx-action@v3 # NOSONAR githubactions:S7637
- name: Rebuild image (cached)
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/build-push-action@v5 # NOSONAR githubactions:S7637
with:
context: .
file: docker/Dockerfile
tags: autocontrol:ci
load: true
cache-from: type=gha
# Mount the repo so pytest can read tests + write the artifact.
- name: Run headless tests under Xvfb
run: |
docker run --rm \
--user root \
-v "$PWD:/work" -w /work \
--entrypoint /bin/sh \
autocontrol:ci -c "
pip install --no-cache-dir -r dev_requirements.txt &&
xvfb-run -a -s '-screen 0 1280x800x24' \
python -m pytest -q --tb=short
"
- name: Smoke test the entrypoint (rest mode)
run: |
# Run without --rm so a quick crash leaves the container in
# place for ``docker logs`` to inspect afterwards. Final
# ``docker rm -f`` cleans up regardless of exit state.
docker run -d --name ac-rest -p 9939:9939 \
-e AC_TOKEN=ci-token autocontrol:ci rest
ok=0
for attempt in 1 2 3 4 5 6 7 8 9 10; do
if curl -fsS -H "Authorization: Bearer ci-token" \
http://127.0.0.1:9939/health; then
echo "REST API is up"
ok=1
break
fi
sleep 2
done
echo "::group::docker logs ac-rest"
docker logs ac-rest || true
echo "::endgroup::"
echo "::group::docker inspect (state)"
docker inspect --format '{{json .State}}' ac-rest || true
echo "::endgroup::"
docker rm -f ac-rest >/dev/null 2>&1 || true
if [ "$ok" -ne 1 ]; then
echo "REST health probe never succeeded" >&2
exit 1
fi
wayland-verification:
name: Wayland backend against a real compositor
needs: build-image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/setup-buildx-action@v3 # NOSONAR githubactions:S7637
- name: Build the Wayland verification image
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/build-push-action@v5 # NOSONAR githubactions:S7637
with:
context: .
file: docker/Dockerfile.wayland
tags: autocontrol-wayland:ci
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
# Two halves, both of which mocks structurally cannot cover.
#
# Capture: sway's headless backend needs no GPU, no seat and no display,
# so a plain ubuntu runner can host a genuine wlroots session. grim's
# argv and -g geometry, wlr-randr's undocumented output format, and the
# whole screenshot -> screen_grabber -> capture -> grab_image chain are
# checked against pixels the compositor actually painted. Twice: once
# with the outputs side by side from the origin, and once with the
# left-hand one at x=-1280, which is the layout of any desktop with a
# monitor left of the primary. The whole-screen capture then starts at
# a negative coordinate, and a size, a crop or a located hit that
# assumes (0, 0) is wrong by the width of that monitor.
#
# libei: every entry point the ctypes binding names is resolved against
# the real libei.so — a misspelled symbol passes every mock and fails
# only on a user's machine — and the fail-closed chain is driven end to
# end. It also re-checks whether ei_unref still segfaults upstream, so
# the workaround in LibeiBackend._teardown gets removed once it can be.
#
# The container exits with the number of failed checks.
- name: Verify against headless sway and the real libei
run: docker run --rm autocontrol-wayland:ci
eis-verification:
name: libei sender against a real EIS server
needs: build-image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/setup-buildx-action@v3 # NOSONAR githubactions:S7637
- name: Build the EIS verification image
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/build-push-action@v5 # NOSONAR githubactions:S7637
with:
context: .
file: docker/Dockerfile.eis
tags: autocontrol-eis:ci
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
# The other half of the input path, and the half no mock can reach: a
# wrong capability value, a mis-marshalled variadic bind or a wrong
# scroll unit is *accepted* by a fake symbol table and only rejected by
# something that speaks the protocol. libeis is that something — the
# server side of libei's own protocol, packaged by Debian — so
# docker/eis_server.py runs a real EIS implementation on a Unix socket
# and records what AutoControl's real sender does to it. No compositor
# and no desktop session are involved.
#
# It also re-checks whether ei_unref is still safe on a live context,
# which is what lets _teardown release instead of leaking.
#
# The container exits with the number of failed checks.
- name: Verify the libei sender against libeis
run: docker run --rm autocontrol-eis:ci
portal-verification:
name: RemoteDesktop portal handshake against a real liboeffis
needs: build-image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/setup-buildx-action@v3 # NOSONAR githubactions:S7637
- name: Build the portal verification image
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/build-push-action@v5 # NOSONAR githubactions:S7637
with:
context: .
file: docker/Dockerfile.portal
tags: autocontrol-portal:ci
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
# How a client gets an EIS socket on GNOME and KDE: not a path on disk
# but a file descriptor handed over D-Bus at the end of the
# org.freedesktop.portal.RemoteDesktop dance. That was recorded as
# needing a GNOME VM because xdg-desktop-portal-wlr has no RemoteDesktop
# interface — but the portal is a D-Bus interface, not a compositor
# feature, so docker/portal_server.py owns the well-known name on a
# private session bus and answers the four calls for real.
#
# Its ConnectToEIS hands back a live connection to the same real libeis
# server the eis job uses, so the whole chain is checked at once: the
# call order and predicted request paths, the device mask the user would
# be consenting to, that the descriptor carries a real EI session, and
# that input emitted through it is recorded by an independent
# implementation.
#
# And every way a portal says no — a dismissed dialog, a dialog left
# open, a withheld descriptor, a closed session, a portal too old to
# have ConnectToEIS, no portal at all — has to come back as a refusal on
# this project's own clock rather than a hang or a silent downgrade.
#
# The container exits with the number of failed checks.
- name: Verify the portal handshake against liboeffis
run: docker run --rm autocontrol-portal:ci
x11-verification:
name: X11 backend against a real X server
needs: build-image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/setup-buildx-action@v3 # NOSONAR githubactions:S7637
- name: Build the X11 verification image
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/build-push-action@v5 # NOSONAR githubactions:S7637
with:
context: .
file: docker/Dockerfile.x11
tags: autocontrol-x11:ci
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
# Wayland ended up with five jobs that read back what reached a real
# peer. X11 — the older and more widely deployed of the two Linux
# paths — had none: every X11 assertion in the suite is made against a
# mock of python-Xlib, so nothing had confirmed that an injected event
# reaches a client at all.
#
# Ground truth deliberately comes from other codebases than the one
# under test. xev is a real X client that prints every event delivered
# to its window, so a click is read back the way the ydotool job reads
# its events off /dev/input/eventN — including `synthetic NO`, which is
# what separates real server input from XSendEvent traffic that
# toolkits discard. ImageMagick's `import` is an independent grabber,
# in the role grim plays for Wayland, against a root window painted two
# asymmetric colours so a wrong rectangle cannot look right.
#
# It runs twice: one monitor over the whole screen, then two RANDR
# monitors side by side. There is deliberately no negative-origin pass
# — on X11 the root window is the union of every monitor and always
# starts at (0, 0), so the Wayland job's second layout has no analogue
# here. That is a protocol difference, not an untested case.
#
# The container exits with the number of failed checks.
- name: Verify the X11 backend against a real X server
run: docker run --rm autocontrol-x11:ci
seat-verification:
name: ydotool absolute move against a seat that consumes it
needs: build-image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/setup-buildx-action@v3 # NOSONAR githubactions:S7637
- name: Build the seat verification image
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/build-push-action@v5 # NOSONAR githubactions:S7637
with:
context: .
file: docker/Dockerfile.seat
tags: autocontrol-seat:ci
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Load the uinput and evdev kernel modules
run: |
sudo modprobe uinput
sudo modprobe evdev || true
test -e /dev/uinput || {
echo "::error::/dev/uinput is missing after modprobe; this runner's"
echo "::error::kernel cannot host the seat verification."
exit 1
}
# The join between the two images above, and the one every earlier note
# in this file said needed a VM. The wayland job runs a compositor that
# consumes no input; the ydotool job reads ydotool's events off the
# kernel with no compositor. Neither can say where the cursor ends up.
#
# wlroots can: WLR_BACKENDS=headless,libinput keeps the outputs virtual
# while running the real libinput backend, libseat's builtin backend
# opens the device without logind, and SEATD_VTBOUND=0 stops it
# reaching for a VT no container owns. ydotoold's device is then an
# ordinary seat device, and grim -c draws the cursor into a screenshot.
#
# That settles what --absolute is absolute *to* — the top-left of the
# output layout, not layout (0, 0), which is the translation
# linux_wayland/mouse.py now applies — and what pointer acceleration
# does to it, which is double the distance asked for under libinput's
# default profile. It runs over the same two layouts as the wayland
# job, and the negative-origin one is where an untranslated request
# lands on the wrong monitor entirely.
#
# The container exits with the number of failed checks.
- name: Verify the absolute move against a real seat
run: |
docker run --rm --device /dev/uinput --device-cgroup-rule 'c 13:* rmw' autocontrol-seat:ci
ydotool-verification:
name: ydotool argv against a real uinput device
needs: build-image
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/setup-buildx-action@v3 # NOSONAR githubactions:S7637
- name: Build the ydotool verification image
# nosemgrep: yaml.github-actions.security.third-party-action-not-pinned-to-commit-sha.third-party-action-not-pinned-to-commit-sha
uses: docker/build-push-action@v5 # NOSONAR githubactions:S7637
with:
context: .
file: docker/Dockerfile.ydotool
tags: autocontrol-ydotool:ci
load: true
cache-from: type=gha
cache-to: type=gha,mode=max
# uinput is what ydotoold writes into; evdev is the handler that turns
# the device it creates into the /dev/input/eventN this verification
# reads back. Both ship with the runner's kernel as modules. The
# explicit check is here so a kernel without them fails saying so,
# rather than the container reporting an empty device list.
- name: Load the uinput and evdev kernel modules
run: |
sudo modprobe uinput
sudo modprobe evdev || true
test -e /dev/uinput || {
echo "::error::/dev/uinput is missing after modprobe; this runner's"
echo "::error::kernel cannot host the ydotool verification."
exit 1
}
ls -l /dev/uinput
# The half neither other image can reach. sway's headless backend
# consumes no libinput devices, so an injected event has nowhere to
# arrive there — but arriving is not what is being checked. ydotoold
# creates an ordinary uinput device and the kernel publishes it as an
# evdev node, so reading that node returns the exact input_event structs
# ydotool wrote, with no compositor and no seat in the picture.
#
# That settles the click bitmasks, the split press / release edges drag
# depends on, what --absolute really puts on the wire, and the wheel
# signs this project had assumed from the kernel's REL_WHEEL convention
# and never measured. The last check drives the backend's own functions
# rather than a hand-written argv, so the two halves meet.
#
# --device covers /dev/uinput, which exists before the container starts.
# The input node does not — ydotoold creates it afterwards — so the
# cgroup rule grants character major 13 and nothing else, which is much
# narrower than --privileged.
#
# The container exits with the number of failed checks.
- name: Verify the ydotool argv against the kernel
run: |
docker run --rm \
--device /dev/uinput \
--device-cgroup-rule 'c 13:* rmw' \
autocontrol-ydotool:ci