diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6ccca48..c2d3f83 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -71,6 +71,57 @@ jobs: DEBIAN_SECURITY_MIRROR=${{ env.DEBIAN_SECURITY_MIRROR }} push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + build-desktop: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - stack: desktop + version: kde-focal + permissions: + contents: read + packages: write + env: + STACK: ${{ matrix.stack }} + VERSION: ${{ matrix.version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ matrix.stack }} + tags: | + type=raw,value=${{ matrix.version }} + type=raw,value=${{ matrix.version }}-${{ github.ref_name }},enable=${{ startsWith(github.ref, 'refs/heads/') && github.ref != 'refs/heads/main' }} + type=ref,event=tag + type=raw,value=${{ matrix.version }}-${{ github.ref_name }},enable=${{ startsWith(github.ref, 'refs/tags/') }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + if: github.event_name == 'push' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build (no push on PR) + uses: docker/build-push-action@v5 + with: + context: . + file: docker/${{ matrix.stack }}/${{ matrix.version }}/Dockerfile + platforms: linux/amd64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + build-stacks: needs: build-base runs-on: ubuntu-latest diff --git a/docker/desktop/kde-focal/Dockerfile b/docker/desktop/kde-focal/Dockerfile new file mode 100644 index 0000000..efee5fa --- /dev/null +++ b/docker/desktop/kde-focal/Dockerfile @@ -0,0 +1,96 @@ +FROM ubuntu:focal + +ARG S6_VER="2.0.0.1" +ARG NO_VNC_VER="1.1.0" +ARG WEB_SOCK_VER="0.9.0" + +RUN mkdir /_install + +## S6 Overlay +ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_VER}/s6-overlay-amd64.tar.gz /_install +RUN tar xzf /_install/s6-overlay-amd64.tar.gz -C / --exclude="./bin" && \ + tar xzf /_install/s6-overlay-amd64.tar.gz -C /usr ./bin +ENTRYPOINT ["/init"] + + +## KDE +ENV DEBIAN_FRONTEND=noninteractive +RUN apt update && \ + apt upgrade -y && \ + apt install -y plasma-desktop +RUN apt install -y dbus-x11 + + +## Other Applications +RUN apt install -y dolphin konsole screen + +## Google Chrome +RUN apt install -y wget gnupg2 && \ + wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add - && \ + echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list && \ + apt update && \ + apt install -y google-chrome-stable && \ + sed -i 's|Exec=/usr/bin/google-chrome-stable|Exec=/usr/bin/google-chrome-stable --no-sandbox|g' /usr/share/applications/google-chrome.desktop && \ + update-alternatives --set x-www-browser /usr/bin/google-chrome-stable && \ + update-alternatives --set gnome-www-browser /usr/bin/google-chrome-stable + + +## VNC +RUN apt install -y tigervnc-standalone-server tigervnc-xorg-extension +ENV DISPLAY=:0 \ + SCR_WIDTH=1920 \ + SCR_HEIGHT=1080 +EXPOSE 5900 + + +## NOVNC +ADD https://github.com/novnc/noVNC/archive/v${NO_VNC_VER}.zip /_install +ADD https://github.com/novnc/websockify/archive/v${WEB_SOCK_VER}.zip /_install +RUN cd /_install && \ + apt install -y unzip python3 python3-pip nginx gettext-base vim && \ + ln -s /usr/bin/python3 /usr/bin/python && \ + pip3 install numpy && \ + unzip v${NO_VNC_VER}.zip && \ + unzip v${WEB_SOCK_VER}.zip && \ + mv noVNC-${NO_VNC_VER} /novnc && \ + mv websockify-${WEB_SOCK_VER} /novnc/utils/websockify +EXPOSE 8080 +ENV PATH_PREFIX=/ \ + VNC_RESIZE=scale \ + RECON_DELAY=250 \ + PAGE_TITLE=KDE + + +## Browser Harness (for AI agent browser control) +RUN apt install -y git curl && \ + curl -LsSf https://astral.sh/uv/install.sh | sh && \ + export PATH="/root/.local/bin:$PATH" && \ + git clone https://github.com/browser-use/browser-harness /opt/browser-harness && \ + cd /opt/browser-harness && \ + uv tool install -e . && \ + ln -sf /root/.local/bin/browser-harness /usr/local/bin/browser-harness +ENV BU_CDP_URL=http://127.0.0.1:9222 \ + PATH="/root/.local/bin:${PATH}" + + +## USER +ENV PGID=0 \ + PUID=0 \ + ROOT_PASSWORD=password \ + HOME=/config +RUN useradd -U -u 6006 -d "$HOME" kid && \ + usermod -G users kid +WORKDIR / + + +## All Dependencies Satisfied +# RUN yes | unminimize +COPY docker/desktop/kde-focal/_root / +COPY docker/desktop/kde-focal/focal/root / + + +# Cleanup +RUN apt autoremove -y && \ + apt clean && \ + rm -r /_install + diff --git a/docker/desktop/kde-focal/_root/etc/cont-init.d/00--init-user.sh b/docker/desktop/kde-focal/_root/etc/cont-init.d/00--init-user.sh new file mode 100755 index 0000000..8dbf236 --- /dev/null +++ b/docker/desktop/kde-focal/_root/etc/cont-init.d/00--init-user.sh @@ -0,0 +1,24 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +groupmod -o -g "$PGID" kid +usermod -o -u "$PUID" kid +echo "kid:$ROOT_PASSWORD" | chpasswd +echo "root:$ROOT_PASSWORD" | chpasswd + + +# Link /root -> $HOME +# for compatibility reasons +if [[ "$PGID" -eq 0 ]] && [[ "$PUID" -eq 0 ]] +then + if [[ ! -e "$HOME" ]] + then + ln -s /root "$HOME" + fi +else + mkdir -p "$HOME" +fi + +chown kid:kid "$HOME" diff --git a/docker/desktop/kde-focal/_root/etc/cont-init.d/00-desktop.sh b/docker/desktop/kde-focal/_root/etc/cont-init.d/00-desktop.sh new file mode 100755 index 0000000..05ac87a --- /dev/null +++ b/docker/desktop/kde-focal/_root/etc/cont-init.d/00-desktop.sh @@ -0,0 +1,8 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +s6-setuidgid kid mkdir -p "$HOME/Desktop" +mkdir -p /misc/desktop +mv /misc/desktop/* "$HOME/Desktop" diff --git a/docker/desktop/kde-focal/_root/etc/cont-init.d/00-envsubst.sh b/docker/desktop/kde-focal/_root/etc/cont-init.d/00-envsubst.sh new file mode 100755 index 0000000..b76c2ab --- /dev/null +++ b/docker/desktop/kde-focal/_root/etc/cont-init.d/00-envsubst.sh @@ -0,0 +1,18 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +if [ "$PATH_PREFIX" = "/" ] +then + export SOCKET_PREFIX="" +else + export SOCKET_PREFIX="$PATH_PREFIX" +fi + +TEMP="$(envsubst '${PATH_PREFIX},${SOCKET_PREFIX}' < /etc/nginx/nginx.conf)" +echo "$TEMP" > /etc/nginx/nginx.conf + +export PAGE_PREFIX="${SOCKET_PREFIX:1}/" +TEMP="$(envsubst '${PAGE_PREFIX},${PAGE_TITLE},${RECON_DELAY},${VNC_RESIZE}' < /novnc/index.html)" +echo "$TEMP" > /novnc/index.html diff --git a/docker/desktop/kde-focal/_root/etc/nginx/nginx.conf b/docker/desktop/kde-focal/_root/etc/nginx/nginx.conf new file mode 100644 index 0000000..13c5f9b --- /dev/null +++ b/docker/desktop/kde-focal/_root/etc/nginx/nginx.conf @@ -0,0 +1,33 @@ +daemon off; + +events { } + +http { + + gzip on; + include /etc/nginx/mime.types; + default_type application/octet-stream; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_read_timeout 61s; + proxy_buffering off; + + upstream vnc_proxy { + server 127.0.0.1:6080; + } + + server { + listen 8080; + location ${SOCKET_PREFIX}/websockify { + proxy_pass http://vnc_proxy/; + } + location ${PATH_PREFIX} { + alias /novnc/; + index index.html; + try_files $uri $uri/ =404; + } + } + +} diff --git a/docker/desktop/kde-focal/_root/etc/services.d/chrome/run b/docker/desktop/kde-focal/_root/etc/services.d/chrome/run new file mode 100755 index 0000000..f1ef45b --- /dev/null +++ b/docker/desktop/kde-focal/_root/etc/services.d/chrome/run @@ -0,0 +1,16 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +s6-svwait /var/run/s6/services/kde + +sleep 3 + +exec s6-setuidgid kid /usr/bin/google-chrome-stable \ + --no-sandbox \ + --remote-debugging-port=9222 \ + --user-data-dir=/config/.chrome-debug-profile \ + --no-first-run \ + --window-position=0,0 \ + --window-size=1920,1080 diff --git a/docker/desktop/kde-focal/_root/etc/services.d/nginx/run b/docker/desktop/kde-focal/_root/etc/services.d/nginx/run new file mode 100755 index 0000000..e842ef7 --- /dev/null +++ b/docker/desktop/kde-focal/_root/etc/services.d/nginx/run @@ -0,0 +1,10 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +s6-svwait /var/run/s6/services/novnc + + +exec /usr/sbin/nginx + diff --git a/docker/desktop/kde-focal/_root/etc/services.d/novnc/run b/docker/desktop/kde-focal/_root/etc/services.d/novnc/run new file mode 100755 index 0000000..9f9f81f --- /dev/null +++ b/docker/desktop/kde-focal/_root/etc/services.d/novnc/run @@ -0,0 +1,10 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +s6-svwait /var/run/s6/services/tigervnc + + +exec s6-setuidgid kid /novnc/utils/launch.sh + diff --git a/docker/desktop/kde-focal/_root/etc/services.d/tigervnc/run b/docker/desktop/kde-focal/_root/etc/services.d/tigervnc/run new file mode 100755 index 0000000..01e7632 --- /dev/null +++ b/docker/desktop/kde-focal/_root/etc/services.d/tigervnc/run @@ -0,0 +1,7 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +exec s6-setuidgid kid /usr/bin/Xvnc "$DISPLAY" -geometry "$SCR_WIDTH"x"$SCR_HEIGHT" -depth 24 -SecurityTypes none -AlwaysShared + diff --git a/docker/desktop/kde-focal/_root/etc/services.d/vncconfig/run b/docker/desktop/kde-focal/_root/etc/services.d/vncconfig/run new file mode 100755 index 0000000..d4f4d29 --- /dev/null +++ b/docker/desktop/kde-focal/_root/etc/services.d/vncconfig/run @@ -0,0 +1,10 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +s6-svwait /var/run/s6/services/tigervnc + + +exec s6-setuidgid kid vncconfig -nowin + diff --git a/docker/desktop/kde-focal/_root/novnc/index.html b/docker/desktop/kde-focal/_root/novnc/index.html new file mode 100644 index 0000000..47d4dd7 --- /dev/null +++ b/docker/desktop/kde-focal/_root/novnc/index.html @@ -0,0 +1,26 @@ + + + + + + + ${PAGE_TITLE} + + + + + + + diff --git a/docker/desktop/kde-focal/focal/root/etc/cont-init.d/01-desktop.sh b/docker/desktop/kde-focal/focal/root/etc/cont-init.d/01-desktop.sh new file mode 100755 index 0000000..c806b2c --- /dev/null +++ b/docker/desktop/kde-focal/focal/root/etc/cont-init.d/01-desktop.sh @@ -0,0 +1,7 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +cat /usr/share/applications/org.kde.dolphin.desktop > "$HOME/Desktop/dolphin.desktop" +cat /usr/share/applications/org.kde.konsole.desktop > "$HOME/Desktop/konsole.desktop" diff --git a/docker/desktop/kde-focal/focal/root/etc/cont-init.d/01-nolock.sh b/docker/desktop/kde-focal/focal/root/etc/cont-init.d/01-nolock.sh new file mode 100755 index 0000000..eb92bed --- /dev/null +++ b/docker/desktop/kde-focal/focal/root/etc/cont-init.d/01-nolock.sh @@ -0,0 +1,7 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +s6-setuidgid kid mkdir -p "$HOME/.config" +s6-setuidgid kid mv /misc/kscreenlockerrc "$HOME/.config/kscreenlockerrc" diff --git a/docker/desktop/kde-focal/focal/root/etc/services.d/kde/run b/docker/desktop/kde-focal/focal/root/etc/services.d/kde/run new file mode 100755 index 0000000..fd4501c --- /dev/null +++ b/docker/desktop/kde-focal/focal/root/etc/services.d/kde/run @@ -0,0 +1,9 @@ +#!/usr/bin/with-contenv bash + +set -eu +set -o pipefail + +s6-svwait /var/run/s6/services/tigervnc + + +exec s6-setuidgid kid /usr/bin/startplasma-x11 diff --git a/docker/desktop/kde-focal/focal/root/misc/kscreenlockerrc b/docker/desktop/kde-focal/focal/root/misc/kscreenlockerrc new file mode 100644 index 0000000..c578cbc --- /dev/null +++ b/docker/desktop/kde-focal/focal/root/misc/kscreenlockerrc @@ -0,0 +1,6 @@ +[$Version] +update_info=kscreenlocker.upd:0.1-autolock + +[Daemon] +Autolock=false +LockOnResume=false diff --git a/docker/desktop/kde-focal/focal/root/usr/lib/x86_64-linux-gnu/libexec/kf5/start_kdeinit_wrapper b/docker/desktop/kde-focal/focal/root/usr/lib/x86_64-linux-gnu/libexec/kf5/start_kdeinit_wrapper new file mode 100755 index 0000000..3c52bc3 --- /dev/null +++ b/docker/desktop/kde-focal/focal/root/usr/lib/x86_64-linux-gnu/libexec/kf5/start_kdeinit_wrapper @@ -0,0 +1,3 @@ +#!/usr/bin/with-contenv bash + +echo FAKE start_kdeinit_wrapper FAKE