diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a520eec..4a99a68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,8 +15,8 @@ concurrency: # READ THIS BEFORE TRUSTING A GREEN TICK. # # What CI proves is strictly LESS than what `./verify.sh --full` proves on a -# real machine. Green here means: the code lints, the pure-Python tier passes, -# the packaging is well-formed, and the widgets build under GTK 4 / libadwaita. +# real machine. Green here means: the code lints, the packaging is well-formed, +# and the suite passes against a GNOME 50 userspace with a private session bus. # # It does NOT prove any of the following, all of which only the local canonical # check covers: @@ -27,61 +27,85 @@ concurrency: # * that the machine this was built on came out unchanged (F16). # A change that is green here and has not been through ./verify.sh --full is # unverified. See docs/testing.md. +# +# WHY THE WHOLE SUITE RUNS IN THE ARCH CONTAINER AND NOT ON THE UBUNTU RUNNER. +# It was split the other way until the v2 rebuild, and the unit half could not +# pass there for three separate reasons, none of them about the code: +# * with no PyGObject the page modules will not import, and the tests that +# read a setting through GioBackend have nothing to read it with; +# * with PyGObject but no session bus, every settings write is skipped with +# "your desktop session wasn't running" — 19 failures that say nothing; +# * with both, ubuntu-24.04's gsettings-desktop-schemas is GNOME 46, which +# has no `accent-color` key and no org.gnome.shell, so a further 14 fail +# for being a 2024 desktop rather than a 2026 one. +# The container is the machine gtheme targets. The ubuntu job keeps the two +# things that genuinely do not need a desktop: the linter and the packaging. jobs: - lint-and-unit: - name: ruff + unit tests + lint-and-packaging: + name: ruff + packaging runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - # F10: core/ may import Gio/GLib (a guard test forbids Gtk/Adw there), so - # the unit tier needs PyGObject with the GLib typelib. libglib2.0-bin - # provides glib-compile-schemas, which the settings-backend tests use to - # build a throwaway schema. The last three are what the packaging tests - # check the launcher entry, the store listing and the icons with — without - # them those tests skip themselves and prove nothing. - - name: Install GLib bindings and the packaging checkers + # These three are what the packaging tests check the launcher entry, the + # store listing and the icons with — without them those tests skip + # themselves and prove nothing. No PyGObject here on purpose: nothing in + # this job may need a desktop. + - name: Install the packaging checkers run: | sudo apt-get update - sudo apt-get install -y python3-gi gir1.2-glib-2.0 libglib2.0-bin \ - desktop-file-utils appstream librsvg2-bin + sudo apt-get install -y desktop-file-utils appstream librsvg2-bin - name: Install gtheme run: | - python -m venv --system-site-packages .venv + python -m venv .venv .venv/bin/python -m pip install --upgrade pip .venv/bin/python -m pip install -e '.[dev]' - name: ruff run: .venv/bin/ruff check . - - name: pytest - # Ubuntu runners ship libadwaita 1.5; gtheme targets 1.9. No Adw code - # may run here — that is the archlinux job's task. The unit tier - # includes tests/unit/test_packaging_*.py, which builds a real wheel - # and looks inside it. - run: .venv/bin/python -m pytest -q -m "not gtk and not sandbox" + - name: pytest — packaging only + # Builds a real wheel and looks inside it, validates the .desktop file + # and the metainfo, and checks what install.sh would put where. + run: | + .venv/bin/python -m pytest -q \ + tests/unit/test_packaging_wheel.py \ + tests/unit/test_packaging_desktop.py \ + tests/unit/test_packaging_install.py - gtk: - name: GTK/libadwaita tests (Arch container) + tests: + name: the suite (Arch container, GNOME 50) runs-on: ubuntu-latest container: archlinux:latest steps: + # The GNOME lines are not decoration. Without them ten tests fail for the + # container rather than for the code: the schemas the pages read + # (org.gnome.shell, org.gnome.mutter, the settings-daemon plugins), the + # GnomeDesktop-4.0 typelib the thumbnailer needs, and the wallpaper + # catalogue under /usr/share/gnome-background-properties. - name: Install system packages run: | pacman -Syu --noconfirm \ python python-gobject gtk4 libadwaita glib2 dconf libsoup3 \ + gsettings-desktop-schemas gnome-desktop-4 gnome-settings-daemon \ + mutter gnome-shell gnome-backgrounds \ + desktop-file-utils appstream \ python-pip python-hatchling dbus git - uses: actions/checkout@v4 - name: Install gtheme run: | python -m venv --system-site-packages .venv .venv/bin/python -m pip install -e '.[dev]' - - name: pytest -m gtk + - name: pytest # gtk4-broadwayd is an offscreen GDK backend: no X server, no # compositor, no Xvfb needed. dbus-run-session gives the tests a - # private session bus so nothing reaches a real one. + # private session bus so nothing reaches a real one — and so that the + # transaction tests have a session to write into at all. + # + # -m "not sandbox" is both tiers at once: the sandbox tier is the only + # one this file refuses to run, and pyproject already deselects it. run: | gtk4-broadwayd :5 & sleep 2 GDK_BACKEND=broadway BROADWAY_DISPLAY=:5 \ - dbus-run-session -- .venv/bin/python -m pytest -q -m gtk + dbus-run-session -- .venv/bin/python -m pytest -q -m "not sandbox" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 68a2170..b9bd60d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,36 @@ permissions: contents: read jobs: + # Nothing ships that the suite has not passed on, and the suite needs a + # GNOME 50 userspace and a session bus to mean anything — see the long note + # at the top of ci.yml. This is that job, and the wheel waits for it. + tests: + name: the suite (Arch container, GNOME 50) + runs-on: ubuntu-latest + container: archlinux:latest + steps: + - name: Install system packages + run: | + pacman -Syu --noconfirm \ + python python-gobject gtk4 libadwaita glib2 dconf libsoup3 \ + gsettings-desktop-schemas gnome-desktop-4 gnome-settings-daemon \ + mutter gnome-shell gnome-backgrounds \ + desktop-file-utils appstream \ + python-pip python-hatchling dbus git + - uses: actions/checkout@v4 + - name: Install gtheme + run: | + python -m venv --system-site-packages .venv + .venv/bin/python -m pip install -e '.[dev]' + - name: Test before shipping + run: | + gtk4-broadwayd :5 & + sleep 2 + GDK_BACKEND=broadway BROADWAY_DISPLAY=:5 \ + dbus-run-session -- .venv/bin/python -m pytest -q -m "not sandbox" + build: + needs: tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -26,19 +55,12 @@ jobs: with: python-version: "3.13" - # Same reason as ci.yml: core/ imports Gio, so the unit tier needs the - # GLib bindings even though no window is ever opened here. - - name: Install GLib bindings + # The wheel is pure Python; only the launcher entry and the store listing + # need checking here, and neither needs a desktop. + - name: Install the packaging checkers run: | sudo apt-get update - sudo apt-get install -y python3-gi gir1.2-glib-2.0 libglib2.0-bin \ - desktop-file-utils appstream - - - name: Test before shipping - run: | - python -m venv --system-site-packages .venv - .venv/bin/python -m pip install -e '.[dev]' - .venv/bin/python -m pytest -q -m "not gtk and not sandbox" + sudo apt-get install -y desktop-file-utils appstream - name: Check the launcher entry and the store listing run: | @@ -47,6 +69,8 @@ jobs: - name: Build the wheel run: | + python -m venv .venv + .venv/bin/python -m pip install -e '.[dev]' .venv/bin/python -m pip install build .venv/bin/python -m build --wheel diff --git a/tests/unit/test_applyrunner.py b/tests/unit/test_applyrunner.py index 7c58e33..840a9cc 100644 --- a/tests/unit/test_applyrunner.py +++ b/tests/unit/test_applyrunner.py @@ -19,6 +19,11 @@ pytestmark = pytest.mark.gtk +# A marker deselects after collection, so ``-m "not gtk"`` cannot save a +# machine with no PyGObject from the module-scope import below: without this +# line the whole run aborts on a collection error instead of skipping. +pytest.importorskip("gi", reason="PyGObject is needed for these widgets") + from gtheme.ui.applyrunner import ApplyRunner # noqa: E402 diff --git a/tests/unit/test_window.py b/tests/unit/test_window.py index e1bd59d..56c1c24 100644 --- a/tests/unit/test_window.py +++ b/tests/unit/test_window.py @@ -30,6 +30,11 @@ pytestmark = pytest.mark.gtk +# A marker deselects after collection, so ``-m "not gtk"`` cannot save a +# machine with no PyGObject from the module-scope import below: without this +# line the whole run aborts on a collection error instead of skipping. +pytest.importorskip("gi", reason="PyGObject is needed for these widgets") + from gi.repository import Adw, Gtk # noqa: E402 from gtheme import window as window_module # noqa: E402