diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..549a195 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +* +!data/ diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml new file mode 100644 index 0000000..3adff45 --- /dev/null +++ b/.github/workflows/common.yml @@ -0,0 +1,101 @@ +concurrency: ${{ github.workflow }}/${{ github.ref }} + +on: + workflow_dispatch: + inputs: + push: + description: "Push built image" + default: true + required: false + type: boolean + + workflow_call: + inputs: + push: + description: "Push built image" + default: true + required: false + type: boolean + +jobs: + build-test: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + include: + - image: fedora-39 + dockerfile: fedora.dockerfile + + - image: fedora-40 + dockerfile: fedora.dockerfile + + - image: debian-12 + dockerfile: debian.dockerfile + + - image: debian-13 + dockerfile: debian.dockerfile + + - image: ubuntu-24.04 + dockerfile: debian.dockerfile + + - image: archlinux + dockerfile: archlinux.dockerfile + + - image: opensuse-tumbleweed + dockerfile: suse.dockerfile + + - image: opensuse-leap-15.6 + dockerfile: suse.dockerfile + + timeout-minutes: 15 + env: + IMAGE: ghcr.io/${{ github.repository }}/${{ matrix.image }} + + steps: + - uses: actions/checkout@v4 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - id: docker_meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE }} + + - id: docker_build + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ matrix.dockerfile }} + build-args: | + base_image=${{ matrix.image }} + pull: true + push: ${{ inputs.push }} + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + annotations: ${{ steps.docker_meta.outputs.annotations }} + cache-from: | + ${{ steps.docker_meta.outputs.tags }} + ${{ env.IMAGE }}:master + cache-to: type=inline + + - run: podman pull docker-daemon:${{ steps.docker_build.outputs.imageid }} | tee .podman-image-id + + - id: gnome-session-x11 + name: Test gnome-session-x11 + run: test/test-x11.sh $(cat .podman-image-id) + + - id: gnome-session-wayland + name: Test gnome-session-wayland + run: test/test-wayland.sh $(cat .podman-image-id) + + - name: Upload screenshot + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.image }}-screenshots + path: test/*.png + if: ${{ always() }} diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 0000000..a092bd2 --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,23 @@ +name: master + +on: + push: + branches: + - master + paths-ignore: + - renovate.json + - README.md + - LICENSE + +permissions: + packages: write + +jobs: + ci: + uses: ./.github/workflows/common.yml + tag: + needs: ci + uses: ./.github/workflows/tag.yml + permissions: + contents: write + actions: write diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..18ee3b3 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,15 @@ +name: pr + +on: + pull_request: + paths-ignore: + - renovate.json + - README.md + - LICENSE + +jobs: + ci: + if: ${{ github.event.pull_request.base.repo.node_id != github.event.pull_request.head.repo.node_id }} + uses: ./.github/workflows/common.yml + with: + push: false diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 0000000..099b1ce --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,20 @@ +name: push + +on: + workflow_dispatch: + push: + branches-ignore: + - master + tags: + - "*" + paths-ignore: + - renovate.json + - README.md + - LICENSE + +permissions: + packages: write + +jobs: + ci: + uses: ./.github/workflows/common.yml diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml new file mode 100644 index 0000000..8f6eac6 --- /dev/null +++ b/.github/workflows/tag.yml @@ -0,0 +1,20 @@ +on: + workflow_call: + workflow_dispatch: + +jobs: + tag: + runs-on: ubuntu-latest + steps: + - id: tag + uses: ddterm/autotag@2023.11.18.1 + + - uses: actions/github-script@v7 + with: + script: | + github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'push.yml', + ref: '${{ steps.tag.outputs.ref }}', + }) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e708454 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2021 Aleksandr Mezin +Copyright (c) 2021 Simon Schneegans + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/archlinux.dockerfile b/archlinux.dockerfile new file mode 100644 index 0000000..af70597 --- /dev/null +++ b/archlinux.dockerfile @@ -0,0 +1,25 @@ +FROM docker.io/library/archlinux:latest@sha256:649f22ffe44950a2fbdd7b4f3ad7eaf1ae017d60360f857ba1b07902121824d4 + +RUN pacman -Rdd --noconfirm dbus-broker-units \ + && pacman -Syu --noconfirm \ + dbus-daemon-units \ + gnome-shell \ + vte3 \ + vte4 \ + xorg-server-xvfb \ + xorg-xinit \ + mesa \ + packagekit \ + gdm \ + wl-clipboard \ + libhandy \ + && pacman -Scc --noconfirm + +COPY data / + +RUN systemctl mask systemd-oomd low-memory-monitor rtkit-daemon udisks2 gdm && \ + useradd -m -U -G users,adm gnomeshell && \ + truncate --size 0 /etc/machine-id && \ + dconf update + +CMD [ "/sbin/init" ] diff --git a/data/etc/systemd/journald.conf b/data/etc/systemd/journald.conf new file mode 100644 index 0000000..c6e48ce --- /dev/null +++ b/data/etc/systemd/journald.conf @@ -0,0 +1,31 @@ +[Journal] +Storage=volatile +Compress=no +Seal=no +#SplitMode=uid +#SyncIntervalSec=5m +#RateLimitIntervalSec=30s +#RateLimitBurst=10000 +#SystemMaxUse= +#SystemKeepFree= +#SystemMaxFileSize= +#SystemMaxFiles=100 +#RuntimeMaxUse= +#RuntimeKeepFree= +#RuntimeMaxFileSize= +#RuntimeMaxFiles=100 +#MaxRetentionSec= +#MaxFileSec=1month +ForwardToSyslog=no +ForwardToKMsg=no +ForwardToConsole=yes +ForwardToWall=no +TTYPath=/dev/console +#MaxLevelStore=debug +#MaxLevelSyslog=debug +#MaxLevelKMsg=notice +MaxLevelConsole=debug +#MaxLevelWall=emerg +#LineMax=48K +ReadKMsg=no +Audit=no diff --git a/data/etc/systemd/system/systemd-logind.service.d/override.conf b/data/etc/systemd/system/systemd-logind.service.d/override.conf new file mode 100644 index 0000000..b51859a --- /dev/null +++ b/data/etc/systemd/system/systemd-logind.service.d/override.conf @@ -0,0 +1,3 @@ +# Required to make systemd-logind work +[Service] +ProtectHostname=no diff --git a/data/etc/systemd/system/upower.service.d/override.conf b/data/etc/systemd/system/upower.service.d/override.conf new file mode 100644 index 0000000..6b4d3e0 --- /dev/null +++ b/data/etc/systemd/system/upower.service.d/override.conf @@ -0,0 +1,2 @@ +[Service] +PrivateUsers=no diff --git a/debian.dockerfile b/debian.dockerfile new file mode 100644 index 0000000..62bbf9e --- /dev/null +++ b/debian.dockerfile @@ -0,0 +1,30 @@ +ARG base_image=ubuntu-24.04 + +FROM docker.io/library/debian:12@sha256:aadf411dc9ed5199bc7dab48b3e6ce18f8bbee4f170127f5ff1b75cd8035eb36 AS debian-12 +FROM docker.io/library/debian:trixie@sha256:0c75aed52e3a564e27c3aaba51b0a7e59ac21dd09abcb25f2fe0b9b37c8f7e01 AS debian-13 +FROM docker.io/library/ubuntu:24.04@sha256:8a37d68f4f73ebf3d4efafbcf66379bf3728902a8038616808f04e34a9ab63ee AS ubuntu-24.04 + +FROM ${base_image} + +RUN apt-get update -y && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + gnome-session \ + gjs \ + dbus-user-session \ + gdm3 \ + gir1.2-vte-2.91 \ + gir1.2-vte-3.91 \ + xvfb \ + packagekit \ + gir1.2-packagekitglib-1.0 \ + wl-clipboard \ + gir1.2-handy-1 + +COPY data / + +RUN systemctl mask systemd-oomd low-memory-monitor rtkit-daemon udisks2 gdm3 && \ + useradd -m -U -G users,adm gnomeshell && \ + truncate --size 0 /etc/machine-id && \ + dconf update + +CMD [ "/sbin/init" ] diff --git a/fedora.dockerfile b/fedora.dockerfile new file mode 100644 index 0000000..4f294b3 --- /dev/null +++ b/fedora.dockerfile @@ -0,0 +1,31 @@ +ARG base_image=fedora-40 + +FROM docker.io/library/fedora:39@sha256:2922a1237abbb7f8517018e4f5d7a82a618f6ec09f386799e8595f9e1c39f021 AS fedora-39 +FROM docker.io/library/fedora:40@sha256:5ce8497aeea599bf6b54ab3979133923d82aaa4f6ca5ced1812611b197c79eb0 AS fedora-40 + +FROM ${base_image} + +RUN dnf install -y --nodocs --setopt install_weak_deps=False \ + gnome-session-xsession \ + gnome-extensions-app \ + gjs \ + gdm \ + vte291 \ + vte291-gtk4 \ + xorg-x11-server-Xvfb \ + mesa-dri-drivers \ + wl-clipboard \ + PackageKit \ + PackageKit-glib \ + libhandy \ + && dnf clean all -y + +COPY data / + +RUN systemctl mask systemd-oomd low-memory-monitor rtkit-daemon udisks2 gdm && \ + adduser -m -U -G users,adm gnomeshell && \ + chmod u+rw /etc/shadow && \ + truncate --size 0 /etc/machine-id && \ + dconf update + +CMD [ "/sbin/init" ] diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..a54b838 --- /dev/null +++ b/renovate.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":maintainLockFilesWeekly", + "npm:unpublishSafe" + ], + "packageRules": [ + { + "matchCategories": [ + "docker" + ], + "pinDigests": true + }, + { + "matchPackagePatterns": [ + "opensuse/tumbleweed$" + ], + "extends": [ + "schedule:automergeWeekly" + ] + }, + { + "excludePackagePatterns": [ + "opensuse/tumbleweed$" + ], + "extends": [ + "schedule:automergeDaily" + ] + } + ], + "dockerfile": { + "fileMatch": [ + "(^|/|\\.)dockerfile$" + ], + "major": { + "enabled": false + }, + "minor": { + "enabled": false + }, + "patch": { + "enabled": false + }, + "groupName": "Base images" + }, + "automerge": true, + "customManagers": [ + { + "customType": "regex", + "fileMatch": [ + "^\\.github/workflows/.+\\.ya?ml" + ], + "matchStrings": [ + "#\\s*renovate:\\s+datasource=(?[^\\s]+)\\s+depName=(?[^\\s]+)(\\s+(lookupName|packageName)=(?[^\\s]+))?(\\s+versioning=(?[^\\s]+))?\\n\\s*\\w+\\s*:\\s*[\"']?(?[^\\s\"']+)[\"']?(\\s|$)" + ] + } + ] +} diff --git a/suse.dockerfile b/suse.dockerfile new file mode 100644 index 0000000..af16271 --- /dev/null +++ b/suse.dockerfile @@ -0,0 +1,31 @@ +ARG base_image=opensuse-tumbleweed + +FROM docker.io/opensuse/tumbleweed:latest@sha256:cef34da5c8f8c68d551054f1e1d3d5c5d3adf01684e925db6cccd448342954f9 AS opensuse-tumbleweed +FROM docker.io/opensuse/leap:15.6@sha256:b92aba5f8413624d1a4b671dff4858e454fcbe5e38dff1880cc48241750c2e8e AS opensuse-leap-15.6 + +FROM ${base_image} + +RUN zypper --non-interactive install --no-recommends \ + systemd-sysvinit \ + xorg-x11-server-Xvfb \ + gjs \ + gdm \ + gnome-session-wayland \ + gnome-extensions \ + gtk3-metatheme-adwaita \ + typelib-1_0-Vte-2.91 \ + typelib-1_0-Vte-3_91 \ + PackageKit \ + typelib-1_0-PackageKitGlib-1_0 \ + typelib-1_0-Handy-1_0 \ + wl-clipboard \ + && zypper clean --all + +COPY data / + +RUN systemctl mask systemd-oomd low-memory-monitor rtkit-daemon udisks2 gdm && \ + useradd -m -U -G users gnomeshell && \ + truncate --size 0 /etc/machine-id && \ + dconf update + +CMD [ "/sbin/init" ] diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..b44ef38 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +/*.png diff --git a/test/test-wayland.sh b/test/test-wayland.sh new file mode 100755 index 0000000..e8aa444 --- /dev/null +++ b/test/test-wayland.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +if [[ $# != 1 ]]; then + echo "Usage: $0 image-name" >&2 + exit 1 +fi + +SCRIPT_DIR=$(CDPATH="" cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) + +function shutdown { + podman exec "$CID" systemctl list-units --failed || true + podman rm -f "$CID" +} + +SHARED_DIR="$(mktemp -d)" + +ENV_VARS=( + "XDG_RUNTIME_DIR=${SHARED_DIR}/runtime" + "XDG_CONFIG_HOME=${SHARED_DIR}/config" + "XDG_CACHE_HOME=${SHARED_DIR}/cache" + "XDG_STATE_HOME=${SHARED_DIR}/state" + "DBUS_SESSION_BUS_ADDRESS=unix:path=${SHARED_DIR}/runtime/bus" + "NO_AT_BRIDGE=1" + "GTK_A11Y=none" +) + +mkdir -p "${SHARED_DIR}/runtime" "${SHARED_DIR}/config" "${SHARED_DIR}/cache" "${SHARED_DIR}/state" + +set -ex + +CAPS="SYS_ADMIN,SYS_NICE,SYS_PTRACE,SETPCAP,NET_RAW,NET_BIND_SERVICE,IPC_LOCK" +CID="$(podman create --log-driver=none --tty --cap-add="$CAPS" --security-opt=label=disable --security-opt=apparmor=unconfined --user=0 --userns=keep-id:uid=1000,gid=1000 -v "$SHARED_DIR:$SHARED_DIR" "$1")" + +trap shutdown EXIT + +podman start --attach --sig-proxy=false "$CID" & +podman wait --condition=running "$CID" +podman exec "$CID" busctl --watch-bind=true status +podman exec "$CID" systemctl is-system-running --wait + +podman exec --user=1000 "${ENV_VARS[@]/#/--env=}" "$CID" dbus-daemon --session --nopidfile --nosyslog --fork "--address=unix:path=${SHARED_DIR}/runtime/bus" +env "${ENV_VARS[@]}" dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.Peer.Ping + +podman exec --user=1000 "${ENV_VARS[@]/#/--env=}" "$CID" gnome-shell --wayland --headless --sm-disable --unsafe-mode --virtual-monitor 1600x960 & + +while ! env "${ENV_VARS[@]}" dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep '"org.gnome.Shell.Screenshot"' +do + sleep 1 +done + +while env "${ENV_VARS[@]}" dbus-send --session --print-reply --dest=org.gnome.Shell /org/gnome/Shell org.gnome.Shell.Eval 'string:Main.layoutManager._startingUp' | grep 'string "true"' +do + sleep 1 +done + +env "${ENV_VARS[@]}" dbus-send --session --print-reply --dest=org.gnome.Shell.Screenshot /org/gnome/Shell/Screenshot org.gnome.Shell.Screenshot.Screenshot 'boolean:true' 'boolean:false' "string:${SHARED_DIR}/screenshot-wayland.png" +cp "${SHARED_DIR}/screenshot-wayland.png" "${SCRIPT_DIR}/" diff --git a/test/test-x11.sh b/test/test-x11.sh new file mode 100755 index 0000000..6267151 --- /dev/null +++ b/test/test-x11.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +if [[ $# != 1 ]]; then + echo "Usage: $0 image-name" >&2 + exit 1 +fi + +SCRIPT_DIR=$(CDPATH="" cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) + +function shutdown { + podman exec "$CID" systemctl list-units --failed || true + podman rm -f "$CID" +} + +SHARED_DIR="$(mktemp -d)" + +ENV_VARS=( + "XDG_RUNTIME_DIR=${SHARED_DIR}/runtime" + "XDG_CONFIG_HOME=${SHARED_DIR}/config" + "XDG_CACHE_HOME=${SHARED_DIR}/cache" + "XDG_STATE_HOME=${SHARED_DIR}/state" + "DBUS_SESSION_BUS_ADDRESS=unix:path=${SHARED_DIR}/runtime/bus" + "NO_AT_BRIDGE=1" + "GTK_A11Y=none" + "DISPLAY=:99" +) + +mkdir -p "${SHARED_DIR}/runtime" "${SHARED_DIR}/config" "${SHARED_DIR}/cache" "${SHARED_DIR}/state" + +set -ex + +CAPS="SYS_ADMIN,SYS_NICE,SYS_PTRACE,SETPCAP,NET_RAW,NET_BIND_SERVICE,IPC_LOCK" +CID="$(podman create --log-driver=none --tty --cap-add="$CAPS" --security-opt=label=disable --security-opt=apparmor=unconfined --user=0 --userns=keep-id:uid=1000,gid=1000 -v "$SHARED_DIR:$SHARED_DIR" "$1")" + +trap shutdown EXIT + +podman start --attach --sig-proxy=false "$CID" & +podman wait --condition=running "$CID" +podman exec "$CID" busctl --watch-bind=true status +podman exec "$CID" systemctl is-system-running --wait + +podman exec --user=1000 "${ENV_VARS[@]/#/--env=}" "$CID" dbus-daemon --session --nopidfile --nosyslog --fork "--address=unix:path=${SHARED_DIR}/runtime/bus" +env "${ENV_VARS[@]}" dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.Peer.Ping + +mkfifo "${SHARED_DIR}/display_pipe" +podman exec --user=1000 "${ENV_VARS[@]/#/--env=}" "$CID" bash -c "Xvfb -screen 0 1600x960x24 -nolisten tcp -displayfd 3 :99 3>'${SHARED_DIR}/display_pipe'" & + +read -r DISPLAY_NUMBER <"${SHARED_DIR}/display_pipe" +test ":$DISPLAY_NUMBER" = ":99" + +podman exec --user=1000 "${ENV_VARS[@]/#/--env=}" "$CID" gnome-shell --x11 --sm-disable --unsafe-mode & + +while ! env "${ENV_VARS[@]}" dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep '"org.gnome.Shell.Screenshot"' +do + sleep 1 +done + +while env "${ENV_VARS[@]}" dbus-send --session --print-reply --dest=org.gnome.Shell /org/gnome/Shell org.gnome.Shell.Eval 'string:Main.layoutManager._startingUp' | grep 'string "true"' +do + sleep 1 +done + +env "${ENV_VARS[@]}" dbus-send --session --print-reply --dest=org.gnome.Shell.Screenshot /org/gnome/Shell/Screenshot org.gnome.Shell.Screenshot.Screenshot 'boolean:true' 'boolean:false' "string:${SHARED_DIR}/screenshot-x11.png" +cp "${SHARED_DIR}/screenshot-x11.png" "${SCRIPT_DIR}/"