diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 02dcd49..70afd8f 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -2,9 +2,10 @@ name: Conformance on: push: - branches: [main] + branches: [main, v4.5] pull_request: - branches: [main] + types: [opened, ready_for_review, reopened, synchronize] + branches: [main, v4.5] jobs: plugin-test: @@ -19,20 +20,26 @@ jobs: - uses: actions/checkout@v5 with: repository: fruwehq/eve + ref: ${{ (github.base_ref == 'v4.5' || github.head_ref == 'v4.5' || github.ref_name == 'v4.5') && 'v4.5' || 'main' }} path: eve - uses: actions/setup-python@v6 with: python-version: "3.12" - - run: pip install jsonschema pyyaml + - run: pip install ./eve pytest==9.0.3 + + - name: Validate manifest environment contracts + run: python tests/test-manifest-env-contract - name: Run plugin-test against every package run: | set -e - for manifest in */eve-plugin.yaml; do - plugin="$(dirname "$manifest")" - echo "::group::plugin-test $plugin" - eve/scripts/plugin-test "$plugin" + git ls-files '*eve-plugin.yaml' | while IFS= read -r manifest; do + echo "::group::plugin-test $manifest" + eve/scripts/plugin-test "$manifest" echo "::endgroup::" done + + - name: Launcher parity tests + run: python -m pytest -q tests diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ee1885 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pyc +.pytest_cache/ +__pycache__/ diff --git a/bundles/desktop-gnome-headless/eve-plugin.yaml b/bundles/desktop-gnome-headless/eve-plugin.yaml new file mode 100644 index 0000000..69fa3cb --- /dev/null +++ b/bundles/desktop-gnome-headless/eve-plugin.yaml @@ -0,0 +1,5 @@ +api_version: eve.plugin/v1 +kind: bundle +id: desktop-gnome-headless +display_name: GNOME Desktop (Headless) +members: [gnome-desktop-headless, rdp] diff --git a/bundles/desktop-gnome-mac/eve-plugin.yaml b/bundles/desktop-gnome-mac/eve-plugin.yaml new file mode 100644 index 0000000..6b526e6 --- /dev/null +++ b/bundles/desktop-gnome-mac/eve-plugin.yaml @@ -0,0 +1,5 @@ +api_version: eve.plugin/v1 +kind: bundle +id: desktop-gnome-mac +display_name: GNOME Desktop (macOS themed) +members: [gnome-desktop, macos-desktop-theme, rdp] diff --git a/bundles/desktop-gnome/eve-plugin.yaml b/bundles/desktop-gnome/eve-plugin.yaml new file mode 100644 index 0000000..c323595 --- /dev/null +++ b/bundles/desktop-gnome/eve-plugin.yaml @@ -0,0 +1,5 @@ +api_version: eve.plugin/v1 +kind: bundle +id: desktop-gnome +display_name: GNOME Desktop +members: [gnome-desktop, rdp] diff --git a/bundles/desktop-kde-headless/eve-plugin.yaml b/bundles/desktop-kde-headless/eve-plugin.yaml new file mode 100644 index 0000000..2aa7067 --- /dev/null +++ b/bundles/desktop-kde-headless/eve-plugin.yaml @@ -0,0 +1,5 @@ +api_version: eve.plugin/v1 +kind: bundle +id: desktop-kde-headless +display_name: KDE Plasma Desktop (Headless) +members: [kde-desktop-headless, rdp] diff --git a/bundles/desktop-kde/eve-plugin.yaml b/bundles/desktop-kde/eve-plugin.yaml new file mode 100644 index 0000000..0670140 --- /dev/null +++ b/bundles/desktop-kde/eve-plugin.yaml @@ -0,0 +1,5 @@ +api_version: eve.plugin/v1 +kind: bundle +id: desktop-kde +display_name: KDE Plasma Desktop +members: [kde-desktop, rdp] diff --git a/bundles/desktop-streaming/eve-plugin.yaml b/bundles/desktop-streaming/eve-plugin.yaml new file mode 100644 index 0000000..93ae65e --- /dev/null +++ b/bundles/desktop-streaming/eve-plugin.yaml @@ -0,0 +1,5 @@ +api_version: eve.plugin/v1 +kind: bundle +id: desktop-streaming +display_name: Desktop Streaming +members: [sunshine, rustdesk, vnc, rdp, waypipe] diff --git a/bundles/desktop-xfce-headless/eve-plugin.yaml b/bundles/desktop-xfce-headless/eve-plugin.yaml new file mode 100644 index 0000000..e31df52 --- /dev/null +++ b/bundles/desktop-xfce-headless/eve-plugin.yaml @@ -0,0 +1,5 @@ +api_version: eve.plugin/v1 +kind: bundle +id: desktop-xfce-headless +display_name: XFCE Desktop (Headless) +members: [xfce-desktop-headless, rdp] diff --git a/bundles/desktop-xfce/eve-plugin.yaml b/bundles/desktop-xfce/eve-plugin.yaml new file mode 100644 index 0000000..e6b89e4 --- /dev/null +++ b/bundles/desktop-xfce/eve-plugin.yaml @@ -0,0 +1,5 @@ +api_version: eve.plugin/v1 +kind: bundle +id: desktop-xfce +display_name: XFCE Desktop +members: [xfce-desktop, rdp] diff --git a/bundles/remote-xpra/eve-plugin.yaml b/bundles/remote-xpra/eve-plugin.yaml new file mode 100644 index 0000000..398c981 --- /dev/null +++ b/bundles/remote-xpra/eve-plugin.yaml @@ -0,0 +1,5 @@ +api_version: eve.plugin/v1 +kind: bundle +id: remote-xpra +display_name: Remote Xpra +members: [xpra] diff --git a/gnome-desktop-headless/eve-plugin.yaml b/gnome-desktop-headless/eve-plugin.yaml index 038abae..86b0546 100644 --- a/gnome-desktop-headless/eve-plugin.yaml +++ b/gnome-desktop-headless/eve-plugin.yaml @@ -3,26 +3,47 @@ kind: package id: gnome-desktop-headless display_name: GNOME Desktop Headless commands: - install: {exec: scripts/package-plugin, args: [gnome-desktop-headless, install]} - status: {exec: scripts/package-plugin, args: [gnome-desktop-headless, status]} - down: {exec: scripts/package-plugin, args: [gnome-desktop-headless, down]} -supports: {os_families: [ubuntu]} -conflicts_with: [xfce-desktop, xfce-desktop-headless, gnome-desktop, kde-desktop, kde-desktop-headless] + install: + exec: scripts/package-plugin + args: + - gnome-desktop-headless + - install + status: + exec: scripts/package-plugin + args: + - gnome-desktop-headless + - status + down: + exec: scripts/package-plugin + args: + - gnome-desktop-headless + - down +supports: + os_families: + - ubuntu +conflicts_with: +- xfce-desktop +- xfce-desktop-headless +- gnome-desktop +- kde-desktop +- kde-desktop-headless compatibility: - - platform: ubuntu - desktop: GNOME Headless - session: Wayland - status: supported - notes: Installs GNOME with GDM autologin and persistent Wayland virtual monitor setup. -down: {destructive: true} -env: [VIRTUAL_MONITOR_WIDTH, VIRTUAL_MONITOR_HEIGHT] +- platform: ubuntu + desktop: GNOME Headless + session: Wayland + status: supported + notes: Installs GNOME with GDM autologin and persistent Wayland virtual monitor + setup. +down: + destructive: true +env: [] desktop: - name: "GNOME Headless" - session: "Wayland" + name: GNOME Headless + session: Wayland headless: true install: ubuntu: - steps: [base.sh, timezone.sh, provision/ubuntu/gnome-desktop-headless.sh] -bundles: - - id: desktop-gnome-headless - includes: [gnome-desktop-headless, rdp] + steps: + - base.sh + - timezone.sh + - provision/ubuntu/gnome-desktop-headless.sh diff --git a/gnome-desktop/eve-plugin.yaml b/gnome-desktop/eve-plugin.yaml index 18f4631..351c9f3 100644 --- a/gnome-desktop/eve-plugin.yaml +++ b/gnome-desktop/eve-plugin.yaml @@ -3,29 +3,47 @@ kind: package id: gnome-desktop display_name: GNOME Desktop commands: - install: {exec: scripts/package-plugin, args: [gnome-desktop, install]} - status: {exec: scripts/package-plugin, args: [gnome-desktop, status]} - down: {exec: scripts/package-plugin, args: [gnome-desktop, down]} -supports: {os_families: [ubuntu]} -conflicts_with: [xfce-desktop, xfce-desktop-headless, gnome-desktop-headless, kde-desktop, kde-desktop-headless] + install: + exec: scripts/package-plugin + args: + - gnome-desktop + - install + status: + exec: scripts/package-plugin + args: + - gnome-desktop + - status + down: + exec: scripts/package-plugin + args: + - gnome-desktop + - down +supports: + os_families: + - ubuntu +conflicts_with: +- xfce-desktop +- xfce-desktop-headless +- gnome-desktop-headless +- kde-desktop +- kde-desktop-headless compatibility: - - platform: ubuntu - desktop: GNOME - session: Wayland - status: supported - notes: Installs GNOME with GDM autologin for the local graphical session. -down: {destructive: true} +- platform: ubuntu + desktop: GNOME + session: Wayland + status: supported + notes: Installs GNOME with GDM autologin for the local graphical session. +down: + destructive: true env: [] -provides: [session:wayland] +provides: +- session:wayland desktop: - name: "GNOME" - session: "Wayland" + name: GNOME + session: Wayland install: ubuntu: - steps: [base.sh, timezone.sh, provision/ubuntu/gnome-desktop.sh] -bundles: - - id: desktop-gnome - includes: [gnome-desktop, rdp] - - - id: desktop-gnome-mac - includes: [gnome-desktop, macos-desktop-theme, rdp] + steps: + - base.sh + - timezone.sh + - provision/ubuntu/gnome-desktop.sh diff --git a/kde-desktop-headless/eve-plugin.yaml b/kde-desktop-headless/eve-plugin.yaml index 79570c8..d539b4f 100644 --- a/kde-desktop-headless/eve-plugin.yaml +++ b/kde-desktop-headless/eve-plugin.yaml @@ -3,26 +3,49 @@ kind: package id: kde-desktop-headless display_name: KDE Plasma Desktop Headless commands: - install: {exec: scripts/package-plugin, args: [kde-desktop-headless, install]} - status: {exec: scripts/package-plugin, args: [kde-desktop-headless, status]} - down: {exec: scripts/package-plugin, args: [kde-desktop-headless, down]} -supports: {os_families: [ubuntu], arches: [amd64]} -conflicts_with: [xfce-desktop, xfce-desktop-headless, gnome-desktop, gnome-desktop-headless, kde-desktop] + install: + exec: scripts/package-plugin + args: + - kde-desktop-headless + - install + status: + exec: scripts/package-plugin + args: + - kde-desktop-headless + - status + down: + exec: scripts/package-plugin + args: + - kde-desktop-headless + - down +supports: + os_families: + - ubuntu + arches: + - amd64 +conflicts_with: +- xfce-desktop +- xfce-desktop-headless +- gnome-desktop +- gnome-desktop-headless +- kde-desktop compatibility: - - platform: ubuntu - desktop: KDE Plasma Headless - session: Wayland - status: supported - notes: Installs KDE Plasma with SDDM autologin and disables lock/power management for unattended krdp. -down: {destructive: true} +- platform: ubuntu + desktop: KDE Plasma Headless + session: Wayland + status: supported + notes: Installs KDE Plasma with SDDM autologin and disables lock/power management + for unattended krdp. +down: + destructive: true env: [] desktop: - name: "KDE Plasma Headless" - session: "Wayland" + name: KDE Plasma Headless + session: Wayland headless: true install: ubuntu: - steps: [base.sh, timezone.sh, provision/ubuntu/kde-desktop-headless.sh] -bundles: - - id: desktop-kde-headless - includes: [kde-desktop-headless, rdp] + steps: + - base.sh + - timezone.sh + - provision/ubuntu/kde-desktop-headless.sh diff --git a/kde-desktop/eve-plugin.yaml b/kde-desktop/eve-plugin.yaml index bc5e0e8..bd529a6 100644 --- a/kde-desktop/eve-plugin.yaml +++ b/kde-desktop/eve-plugin.yaml @@ -3,25 +3,47 @@ kind: package id: kde-desktop display_name: KDE Plasma Desktop commands: - install: {exec: scripts/package-plugin, args: [kde-desktop, install]} - status: {exec: scripts/package-plugin, args: [kde-desktop, status]} - down: {exec: scripts/package-plugin, args: [kde-desktop, down]} -supports: {os_families: [ubuntu], arches: [amd64]} -conflicts_with: [xfce-desktop, xfce-desktop-headless, gnome-desktop, gnome-desktop-headless, kde-desktop-headless] + install: + exec: scripts/package-plugin + args: + - kde-desktop + - install + status: + exec: scripts/package-plugin + args: + - kde-desktop + - status + down: + exec: scripts/package-plugin + args: + - kde-desktop + - down +supports: + os_families: + - ubuntu + arches: + - amd64 +conflicts_with: +- xfce-desktop +- xfce-desktop-headless +- gnome-desktop +- gnome-desktop-headless +- kde-desktop-headless compatibility: - - platform: ubuntu - desktop: KDE Plasma - session: Wayland - status: supported - notes: Installs KDE Plasma with SDDM autologin; RDP uses krdp. -down: {destructive: true} +- platform: ubuntu + desktop: KDE Plasma + session: Wayland + status: supported + notes: Installs KDE Plasma with SDDM autologin; RDP uses krdp. +down: + destructive: true env: [] desktop: - name: "KDE Plasma" - session: "Wayland" + name: KDE Plasma + session: Wayland install: ubuntu: - steps: [base.sh, timezone.sh, provision/ubuntu/kde-desktop.sh] -bundles: - - id: desktop-kde - includes: [kde-desktop, rdp] + steps: + - base.sh + - timezone.sh + - provision/ubuntu/kde-desktop.sh diff --git a/macos-desktop-theme/commands/ubuntu/down b/macos-desktop-theme/commands/ubuntu/down index f90025b..508d957 100755 --- a/macos-desktop-theme/commands/ubuntu/down +++ b/macos-desktop-theme/commands/ubuntu/down @@ -1,4 +1,6 @@ #!/usr/bin/env sh set -eu +# Variables in the command payload expand on the guest. +# shellcheck disable=SC2016 exec ./scripts/package-down-command macos-desktop-theme 'sudo rm -f "$EVE_HUMAN_HOME/.config/autostart/eve-gnome-macos-theme.desktop" "$EVE_HUMAN_HOME/.local/bin/eve-gnome-macos-theme"; sudo DEBIAN_FRONTEND=noninteractive apt-get remove -y gnome-shell-extension-ubuntu-dock papirus-icon-theme || true' diff --git a/macos-desktop-theme/commands/ubuntu/status b/macos-desktop-theme/commands/ubuntu/status index e7872a1..0dd7fbf 100755 --- a/macos-desktop-theme/commands/ubuntu/status +++ b/macos-desktop-theme/commands/ubuntu/status @@ -1,4 +1,6 @@ #!/usr/bin/env sh set -eu +# Variables in the command payload expand on the guest. +# shellcheck disable=SC2016 exec ./scripts/package-status-command macos-desktop-theme 'if [ -f "$HOME/.local/bin/eve-gnome-macos-theme" ]; then echo installed; else echo missing; fi' diff --git a/nomachine/commands/nomachine-info b/nomachine/commands/nomachine-info index 4986a14..6847ae9 100755 --- a/nomachine/commands/nomachine-info +++ b/nomachine/commands/nomachine-info @@ -9,7 +9,7 @@ def main() -> int: ip = os.environ.get("EVE_REMOTE_IP", "(no IP)") user = os.environ.get("VM_USER_NAME", "") or "the instance user" print(f"Host: {ip}") - print(f"Port: 4000 (NX)") + print("Port: 4000 (NX)") print(f"User: {user}") print("Auth: the instance user's system password") return 0 diff --git a/rdp/commands/__pycache__/_client.cpython-314.pyc b/rdp/commands/__pycache__/_client.cpython-314.pyc deleted file mode 100644 index 5b07928..0000000 Binary files a/rdp/commands/__pycache__/_client.cpython-314.pyc and /dev/null differ diff --git a/rdp/commands/ubuntu/status b/rdp/commands/ubuntu/status index a69fc0d..b483652 100755 --- a/rdp/commands/ubuntu/status +++ b/rdp/commands/ubuntu/status @@ -1,4 +1,6 @@ #!/usr/bin/env sh set -eu +# Variables in the command payload expand on the guest. +# shellcheck disable=SC2016 exec ./scripts/package-status-command rdp 'if systemctl is-active --quiet gnome-remote-desktop.service 2>/dev/null || command -v xrdp >/dev/null 2>&1 || dpkg-query -W -f="${Status}" xrdp 2>/dev/null | grep -q "install ok installed"; then echo installed; else echo missing; fi' diff --git a/rdp/commands/verify b/rdp/commands/verify index 1f7c8aa..6286e57 100755 --- a/rdp/commands/verify +++ b/rdp/commands/verify @@ -4,7 +4,6 @@ from __future__ import annotations import os import subprocess -import sys ROOT = os.environ.get("EVE_REMOTE_ROOT", ".") INST = os.environ.get("EVE_REMOTE_INSTANCE", "") diff --git a/rdp/eve-plugin.yaml b/rdp/eve-plugin.yaml index 0375604..c0130f3 100644 --- a/rdp/eve-plugin.yaml +++ b/rdp/eve-plugin.yaml @@ -13,8 +13,10 @@ config_schema: type: string env_var: RDP_GATE_USER description: "Username for the RDP gateway login on GNOME desktops." + secrets: gate_password: type: string + env_var: RDP_GATE_PASSWORD description: "Password for the RDP gateway login on GNOME desktops." compatibility_enforced: true compatibility: @@ -54,7 +56,7 @@ compatibility: status: supported notes: Uses krdp with lock and power management disabled for unattended sessions. down: {destructive: true} -env: [RDP_GATE_USER, RDP_GATE_PASSWORD] +env: [EPHEMERAL_DISPLAY_RESOLUTION, RDP_GATE_PASSWORD, RDP_GATE_USER] actions: - id: rdp label: RDP diff --git a/rdp/provision/ubuntu/rdp.sh b/rdp/provision/ubuntu/rdp.sh index 8585ba6..59a4625 100755 --- a/rdp/provision/ubuntu/rdp.sh +++ b/rdp/provision/ubuntu/rdp.sh @@ -71,8 +71,15 @@ configure_gnome_rdp() { human_run grdctl rdp enable if [ "$headless" = "headless" ]; then - local width="${VIRTUAL_MONITOR_WIDTH:-1920}" - local height="${VIRTUAL_MONITOR_HEIGHT:-1080}" + local resolution="$EPHEMERAL_DISPLAY_RESOLUTION" + local width="${resolution%x*}" + local height="${resolution#*x}" + case "$width:$height" in + *[!0-9:]*|:*|*:) + log "ERROR: display resolution must use the WxH format: $resolution" + return 1 + ;; + esac if grdctl_supports "set-virtual-monitor"; then log "### rdp: configuring GNOME virtual monitor ${width}x${height}" human_run grdctl rdp set-virtual-monitor "${width}x${height}" || true diff --git a/rustdesk/commands/__pycache__/_client.cpython-314.pyc b/rustdesk/commands/__pycache__/_client.cpython-314.pyc deleted file mode 100644 index 20411e6..0000000 Binary files a/rustdesk/commands/__pycache__/_client.cpython-314.pyc and /dev/null differ diff --git a/rustdesk/commands/ubuntu/status b/rustdesk/commands/ubuntu/status index 05e01de..1d7bb62 100755 --- a/rustdesk/commands/ubuntu/status +++ b/rustdesk/commands/ubuntu/status @@ -1,4 +1,6 @@ #!/usr/bin/env sh set -eu +# Variables in the command payload expand on the guest. +# shellcheck disable=SC2016 exec ./scripts/package-status-command rustdesk 'if command -v rustdesk >/dev/null 2>&1 || dpkg-query -W -f="${Status}" rustdesk 2>/dev/null | grep -q "install ok installed"; then echo installed; else echo missing; fi' diff --git a/rustdesk/commands/verify b/rustdesk/commands/verify index 7934478..6605676 100755 --- a/rustdesk/commands/verify +++ b/rustdesk/commands/verify @@ -5,7 +5,6 @@ from __future__ import annotations import os import re import subprocess -import sys ROOT = os.environ.get("EVE_REMOTE_ROOT", ".") INST = os.environ.get("EVE_REMOTE_INSTANCE", "") diff --git a/rustdesk/eve-plugin.yaml b/rustdesk/eve-plugin.yaml index c739548..44f655b 100644 --- a/rustdesk/eve-plugin.yaml +++ b/rustdesk/eve-plugin.yaml @@ -9,13 +9,19 @@ commands: supports: {os_families: [ubuntu]} config_schema: config: - password: - type: string - description: "RustDesk autonomic/one-time password." server: type: host env_var: RUSTDESK_SERVER description: "RustDesk relay/ID server address. Leave unset to use the public servers." + secrets: + key: + type: string + env_var: RUSTDESK_KEY + description: "RustDesk server public key." + password: + type: string + env_var: RUSTDESK_PASSWORD + description: "RustDesk permanent unattended-access password." compatibility_enforced: true compatibility: - platform: windows @@ -54,7 +60,7 @@ compatibility: status: unsupported notes: RustDesk unattended Wayland sharing is not a supported KDE path. down: {destructive: true} -env: [RUSTDESK_SERVER, RUSTDESK_KEY, RUSTDESK_PASSWORD] +env: [RUSTDESK_KEY, RUSTDESK_PASSWORD, RUSTDESK_SERVER] actions: - id: rustdesk label: RustDesk diff --git a/steam/commands/ubuntu/status b/steam/commands/ubuntu/status index 3bd0b2c..b46f1a2 100755 --- a/steam/commands/ubuntu/status +++ b/steam/commands/ubuntu/status @@ -1,4 +1,6 @@ #!/usr/bin/env sh set -eu +# Variables in the command payload expand on the guest. +# shellcheck disable=SC2016 exec ./scripts/package-status-command steam 'if command -v steam >/dev/null 2>&1 || dpkg-query -W -f="${Status}" steam steam-installer 2>/dev/null | grep -q "install ok installed"; then echo installed; else echo missing; fi' diff --git a/steam/eve-plugin.yaml b/steam/eve-plugin.yaml index afb1b02..4cd1956 100644 --- a/steam/eve-plugin.yaml +++ b/steam/eve-plugin.yaml @@ -3,15 +3,32 @@ kind: package id: steam display_name: Steam commands: - install: {exec: scripts/package-plugin, args: [steam, install]} - status: {exec: scripts/package-plugin, args: [steam, status]} - down: {exec: scripts/package-plugin, args: [steam, down]} -supports: {os_families: [ubuntu], arches: [amd64]} -down: {destructive: true} + install: + exec: scripts/package-plugin + args: + - steam + - install + status: + exec: scripts/package-plugin + args: + - steam + - status + down: + exec: scripts/package-plugin + args: + - steam + - down +supports: + os_families: + - ubuntu + arches: + - amd64 +down: + destructive: true env: [] install: ubuntu: - steps: [base.sh, timezone.sh, provision/ubuntu/steam.sh] -bundles: - - id: gaming-streaming - includes: [sunshine, steam, rustdesk] + steps: + - base.sh + - timezone.sh + - provision/ubuntu/steam.sh diff --git a/sunshine/commands/__pycache__/_client.cpython-314.pyc b/sunshine/commands/__pycache__/_client.cpython-314.pyc deleted file mode 100644 index 621f347..0000000 Binary files a/sunshine/commands/__pycache__/_client.cpython-314.pyc and /dev/null differ diff --git a/sunshine/commands/ubuntu/status b/sunshine/commands/ubuntu/status index 5cb02ae..a8e16d2 100755 --- a/sunshine/commands/ubuntu/status +++ b/sunshine/commands/ubuntu/status @@ -1,4 +1,6 @@ #!/usr/bin/env sh set -eu +# Variables in the command payload expand on the guest. +# shellcheck disable=SC2016 exec ./scripts/package-status-command sunshine 'if command -v sunshine >/dev/null 2>&1 || dpkg-query -W -f="${Status}" sunshine 2>/dev/null | grep -q "install ok installed"; then echo installed; else echo missing; fi' diff --git a/sunshine/commands/verify b/sunshine/commands/verify index 91ae288..74c9771 100755 --- a/sunshine/commands/verify +++ b/sunshine/commands/verify @@ -4,7 +4,6 @@ from __future__ import annotations import os import subprocess -import sys ROOT = os.environ.get("EVE_REMOTE_ROOT", ".") INST = os.environ.get("EVE_REMOTE_INSTANCE", "") diff --git a/sunshine/eve-plugin.yaml b/sunshine/eve-plugin.yaml index 7553c42..72181ea 100644 --- a/sunshine/eve-plugin.yaml +++ b/sunshine/eve-plugin.yaml @@ -3,102 +3,149 @@ kind: package id: sunshine display_name: Sunshine commands: - install: {exec: scripts/package-plugin, args: [sunshine, install]} - status: {exec: scripts/package-plugin, args: [sunshine, status]} - down: {exec: scripts/package-plugin, args: [sunshine, down]} -supports: {os_families: [ubuntu]} + install: + exec: scripts/package-plugin + args: + - sunshine + - install + status: + exec: scripts/package-plugin + args: + - sunshine + - status + down: + exec: scripts/package-plugin + args: + - sunshine + - down +supports: + os_families: + - ubuntu config_schema: config: max_bitrate_kbps: type: integer env_var: SUNSHINE_MAX_BITRATE_KBPS - description: "Maximum Sunshine host streaming bitrate, in kilobits per second." - password: - type: string - env_var: EPHEMERAL_SUNSHINE_PASSWORD - description: "Password for the Sunshine web UI / Moonlight pairing flow." - version: - type: string - env_var: SUNSHINE_VERSION - description: "Sunshine version to install (pinned)." + description: Maximum Sunshine host streaming bitrate, in kilobits per second. moonlight_bitrate_kbps: type: integer env_var: EPHEMERAL_MOONLIGHT_BITRATE_KBPS - description: "Moonlight client streaming bitrate, in kilobits per second." + description: Moonlight client streaming bitrate, in kilobits per second. moonlight_display_mode: type: enum - enum: [fullscreen, borderless, windowed] + enum: + - borderless + - fullscreen + - windowed env_var: EPHEMERAL_MOONLIGHT_DISPLAY_MODE - description: "Moonlight client window mode." + description: Moonlight client window mode. moonlight_video_codec: type: enum - enum: [auto, "H.264", HEVC, AV1] + enum: + - AV1 + - H.264 + - HEVC + - auto env_var: EPHEMERAL_MOONLIGHT_VIDEO_CODEC - description: "Preferred Moonlight client video codec." + description: Preferred Moonlight client video codec. moonlight_video_decoder: type: enum - enum: [auto, hardware, software] + enum: + - auto + - hardware + - software env_var: EPHEMERAL_MOONLIGHT_VIDEO_DECODER - description: "Moonlight client video decoder selection." + description: Moonlight client video decoder selection. + version: + type: string + env_var: SUNSHINE_VERSION + description: Sunshine version to install (pinned). + secrets: + password: + type: string + env_var: EPHEMERAL_SUNSHINE_PASSWORD + description: Password for the Sunshine web UI / Moonlight pairing flow. compatibility_enforced: true compatibility: - - platform: windows - desktop: Windows - session: Native - status: supported - notes: Uses Virtual Display Driver as the intended single Sunshine display. - - platform: ubuntu - desktop: XFCE - session: X11 - status: wip - notes: Current Linux path uses X11 capture; ultrawide 5120x1440 still needs live validation. - - platform: ubuntu - desktop: XFCE Headless - session: X11 - status: wip - notes: Current Linux path uses X11 capture; ultrawide 5120x1440 still needs live validation. - - platform: ubuntu - desktop: GNOME - session: Wayland - status: wip - notes: Wayland capture is not the stable default path yet. -down: {destructive: true} -env: [EPHEMERAL_DISPLAY_FPS, EPHEMERAL_DISPLAY_RESOLUTION, EPHEMERAL_SUNSHINE_PASSWORD, SUNSHINE_VERSION, SUNSHINE_MAX_BITRATE_KBPS] +- platform: windows + desktop: Windows + session: Native + status: supported + notes: Uses Virtual Display Driver as the intended single Sunshine display. +- platform: ubuntu + desktop: XFCE + session: X11 + status: wip + notes: Current Linux path uses X11 capture; ultrawide 5120x1440 still needs live + validation. +- platform: ubuntu + desktop: XFCE Headless + session: X11 + status: wip + notes: Current Linux path uses X11 capture; ultrawide 5120x1440 still needs live + validation. +- platform: ubuntu + desktop: GNOME + session: Wayland + status: wip + notes: Wayland capture is not the stable default path yet. +down: + destructive: true +env: +- EPHEMERAL_DISPLAY_FPS +- EPHEMERAL_DISPLAY_RESOLUTION +- EPHEMERAL_MOONLIGHT_BITRATE_KBPS +- EPHEMERAL_MOONLIGHT_DISPLAY_MODE +- EPHEMERAL_MOONLIGHT_VIDEO_CODEC +- EPHEMERAL_MOONLIGHT_VIDEO_DECODER +- EPHEMERAL_SUNSHINE_PASSWORD +- SUNSHINE_MAX_BITRATE_KBPS +- SUNSHINE_VERSION actions: - - id: sunshine - label: Sunshine - target: sunshine.open - exec: commands/sunshine-open - - id: sunshine-wait - label: Wait for Sunshine - target: sunshine.wait - exec: commands/sunshine-wait - - id: pair-moonlight - label: Pair Moonlight - target: moonlight.pair - exec: commands/moonlight-pair - wait_for: sunshine.wait - - id: moonlight - label: Moonlight - target: moonlight.open - exec: commands/moonlight-open - wait_for: sunshine.wait - - id: verify - label: Verify - target: sunshine.verify - exec: commands/verify +- id: sunshine + label: Sunshine + target: sunshine.open + exec: commands/sunshine-open +- id: sunshine-wait + label: Wait for Sunshine + target: sunshine.wait + exec: commands/sunshine-wait +- id: pair-moonlight + label: Pair Moonlight + target: moonlight.pair + exec: commands/moonlight-pair + wait_for: sunshine.wait +- id: moonlight + label: Moonlight + target: moonlight.open + exec: commands/moonlight-open + wait_for: sunshine.wait +- id: verify + label: Verify + target: sunshine.verify + exec: commands/verify vagrant: port_forwards: - - {guest: 47984, host: 47984} - - {guest: 47989, host: 47989} - - {guest: 47990, host: 47990} - - {guest: 48010, host: 48010} - - {guest: 47998, host: 47998, protocol: udp} - - {guest: 47999, host: 47999, protocol: udp} - - {guest: 48000, host: 48000, protocol: udp} + - guest: 47984 + host: 47984 + - guest: 47989 + host: 47989 + - guest: 47990 + host: 47990 + - guest: 48010 + host: 48010 + - guest: 47998 + host: 47998 + protocol: udp + - guest: 47999 + host: 47999 + protocol: udp + - guest: 48000 + host: 48000 + protocol: udp install: ubuntu: - steps: [base.sh, timezone.sh, provision/ubuntu/sunshine.sh] -bundles: - - id: desktop-streaming - includes: [sunshine, rustdesk, vnc, rdp, waypipe] + steps: + - base.sh + - timezone.sh + - provision/ubuntu/sunshine.sh diff --git a/tests/__pycache__/conftest.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/conftest.cpython-314-pytest-9.0.3.pyc deleted file mode 100644 index 727cf6a..0000000 Binary files a/tests/__pycache__/conftest.cpython-314-pytest-9.0.3.pyc and /dev/null differ diff --git a/tests/__pycache__/test_rdp_launcher.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_rdp_launcher.cpython-314-pytest-9.0.3.pyc deleted file mode 100644 index f751ddd..0000000 Binary files a/tests/__pycache__/test_rdp_launcher.cpython-314-pytest-9.0.3.pyc and /dev/null differ diff --git a/tests/__pycache__/test_rustdesk_launcher.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_rustdesk_launcher.cpython-314-pytest-9.0.3.pyc deleted file mode 100644 index 928bcf6..0000000 Binary files a/tests/__pycache__/test_rustdesk_launcher.cpython-314-pytest-9.0.3.pyc and /dev/null differ diff --git a/tests/__pycache__/test_sunshine_launcher.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_sunshine_launcher.cpython-314-pytest-9.0.3.pyc deleted file mode 100644 index f42e468..0000000 Binary files a/tests/__pycache__/test_sunshine_launcher.cpython-314-pytest-9.0.3.pyc and /dev/null differ diff --git a/tests/__pycache__/test_thinlinc_launcher.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_thinlinc_launcher.cpython-314-pytest-9.0.3.pyc deleted file mode 100644 index 9b1f74f..0000000 Binary files a/tests/__pycache__/test_thinlinc_launcher.cpython-314-pytest-9.0.3.pyc and /dev/null differ diff --git a/tests/__pycache__/test_vnc_launcher.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_vnc_launcher.cpython-314-pytest-9.0.3.pyc deleted file mode 100644 index 4e83d57..0000000 Binary files a/tests/__pycache__/test_vnc_launcher.cpython-314-pytest-9.0.3.pyc and /dev/null differ diff --git a/tests/__pycache__/test_waypipe_launcher.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_waypipe_launcher.cpython-314-pytest-9.0.3.pyc deleted file mode 100644 index 3b3cd14..0000000 Binary files a/tests/__pycache__/test_waypipe_launcher.cpython-314-pytest-9.0.3.pyc and /dev/null differ diff --git a/tests/__pycache__/test_xpra_launcher.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_xpra_launcher.cpython-314-pytest-9.0.3.pyc deleted file mode 100644 index ea6d7fb..0000000 Binary files a/tests/__pycache__/test_xpra_launcher.cpython-314-pytest-9.0.3.pyc and /dev/null differ diff --git a/tests/test-manifest-env-contract b/tests/test-manifest-env-contract new file mode 100644 index 0000000..2d6b371 --- /dev/null +++ b/tests/test-manifest-env-contract @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +"""Validate package config/secret mappings against the declared env surface.""" + +from pathlib import Path + +import yaml + +ROOT = Path(__file__).resolve().parents[1] + +for path in sorted(ROOT.glob("*/eve-plugin.yaml")): + plugin = yaml.safe_load(path.read_text(encoding="utf-8")) + declared = plugin.get("env") or [] + assert declared == sorted(set(declared)), f"{path}: env must be sorted and unique" + mapped = { + str(name) + for section in ("config", "secrets") + for spec in ((plugin.get("config_schema") or {}).get(section) or {}).values() + if isinstance(spec, dict) and spec.get("env_var") + for name in ( + spec["env_var"] if isinstance(spec["env_var"], list) else [spec["env_var"]] + ) + } + missing = sorted(mapped - set(declared)) + assert not missing, f"{path}: env omits config_schema mappings: {missing}" + +print("manifest environment contracts passed") diff --git a/tests/test_rustdesk_launcher.py b/tests/test_rustdesk_launcher.py index e55b7dc..f7041e2 100644 --- a/tests/test_rustdesk_launcher.py +++ b/tests/test_rustdesk_launcher.py @@ -1,7 +1,6 @@ """Argv parity for the rustdesk launcher builders (v4.4 §8).""" from __future__ import annotations -import os import pytest diff --git a/tests/test_thinlinc_launcher.py b/tests/test_thinlinc_launcher.py index d9b515a..d56bdc7 100644 --- a/tests/test_thinlinc_launcher.py +++ b/tests/test_thinlinc_launcher.py @@ -1,7 +1,6 @@ """Argv parity for the thinlinc launcher builders (v4.4 §8).""" from __future__ import annotations -import os import pytest diff --git a/tests/test_vnc_launcher.py b/tests/test_vnc_launcher.py index 413fb3d..eec3875 100644 --- a/tests/test_vnc_launcher.py +++ b/tests/test_vnc_launcher.py @@ -1,7 +1,6 @@ """Argv parity for the vnc launcher builders (v4.4 §8).""" from __future__ import annotations -import os import pytest diff --git a/thinlinc/commands/__pycache__/_client.cpython-314.pyc b/thinlinc/commands/__pycache__/_client.cpython-314.pyc deleted file mode 100644 index c69a7be..0000000 Binary files a/thinlinc/commands/__pycache__/_client.cpython-314.pyc and /dev/null differ diff --git a/thinlinc/commands/thinlinc-client b/thinlinc/commands/thinlinc-client index 541f940..b479b91 100755 --- a/thinlinc/commands/thinlinc-client +++ b/thinlinc/commands/thinlinc-client @@ -4,7 +4,6 @@ from __future__ import annotations import os import platform -import subprocess import sys from pathlib import Path diff --git a/thinlinc/commands/verify b/thinlinc/commands/verify index 68442a5..0f7ad45 100755 --- a/thinlinc/commands/verify +++ b/thinlinc/commands/verify @@ -4,7 +4,6 @@ from __future__ import annotations import os import subprocess -import sys ROOT = os.environ.get("EVE_REMOTE_ROOT", ".") INST = os.environ.get("EVE_REMOTE_INSTANCE", "") diff --git a/thinlinc/eve-plugin.yaml b/thinlinc/eve-plugin.yaml index 7dd7eb6..e873ff3 100644 --- a/thinlinc/eve-plugin.yaml +++ b/thinlinc/eve-plugin.yaml @@ -54,7 +54,7 @@ compatibility: status: unsupported notes: ThinLinc serves X11 sessions, not the local GNOME Wayland compositor. down: {destructive: true} -env: [THINLINC_ACCEPT_EULA, THINLINC_SERVER_BUNDLE_PATH, THINLINC_SERVER_BUNDLE_URL, THINLINC_WEBACCESS_PORT] +env: [THINLINC_ACCEPT_EULA, THINLINC_AGENT_HOSTNAME, THINLINC_SERVER_BUNDLE_PATH, THINLINC_SERVER_BUNDLE_URL, THINLINC_WEBACCESS_PORT] actions: - id: web label: ThinLinc Web diff --git a/thinlinc/provision/ubuntu/thinlinc.sh b/thinlinc/provision/ubuntu/thinlinc.sh index 9e5c965..0551639 100644 --- a/thinlinc/provision/ubuntu/thinlinc.sh +++ b/thinlinc/provision/ubuntu/thinlinc.sh @@ -239,7 +239,7 @@ setup-selinux=no EOF log "### thinlinc: running tl-setup non-interactively" -DISPLAY= sudo /opt/thinlinc/sbin/tl-setup -a "$answer_template" +DISPLAY='' sudo /opt/thinlinc/sbin/tl-setup -a "$answer_template" configure_agent_hostname diff --git a/vnc/commands/__pycache__/_client.cpython-314.pyc b/vnc/commands/__pycache__/_client.cpython-314.pyc deleted file mode 100644 index c1402eb..0000000 Binary files a/vnc/commands/__pycache__/_client.cpython-314.pyc and /dev/null differ diff --git a/vnc/commands/verify b/vnc/commands/verify index ea929b8..33fe955 100755 --- a/vnc/commands/verify +++ b/vnc/commands/verify @@ -4,7 +4,6 @@ from __future__ import annotations import os import subprocess -import sys ROOT = os.environ.get("EVE_REMOTE_ROOT", ".") INST = os.environ.get("EVE_REMOTE_INSTANCE", "") diff --git a/waypipe/commands/__pycache__/_client.cpython-314.pyc b/waypipe/commands/__pycache__/_client.cpython-314.pyc deleted file mode 100644 index 515431a..0000000 Binary files a/waypipe/commands/__pycache__/_client.cpython-314.pyc and /dev/null differ diff --git a/xfce-desktop-headless/eve-plugin.yaml b/xfce-desktop-headless/eve-plugin.yaml index f1d0a41..5014b1a 100644 --- a/xfce-desktop-headless/eve-plugin.yaml +++ b/xfce-desktop-headless/eve-plugin.yaml @@ -3,26 +3,46 @@ kind: package id: xfce-desktop-headless display_name: XFCE Desktop Headless commands: - install: {exec: scripts/package-plugin, args: [xfce-desktop-headless, install]} - status: {exec: scripts/package-plugin, args: [xfce-desktop-headless, status]} - down: {exec: scripts/package-plugin, args: [xfce-desktop-headless, down]} -supports: {os_families: [ubuntu]} -conflicts_with: [xfce-desktop, gnome-desktop, gnome-desktop-headless, kde-desktop, kde-desktop-headless] + install: + exec: scripts/package-plugin + args: + - xfce-desktop-headless + - install + status: + exec: scripts/package-plugin + args: + - xfce-desktop-headless + - status + down: + exec: scripts/package-plugin + args: + - xfce-desktop-headless + - down +supports: + os_families: + - ubuntu +conflicts_with: +- xfce-desktop +- gnome-desktop +- gnome-desktop-headless +- kde-desktop +- kde-desktop-headless compatibility: - - platform: ubuntu - desktop: XFCE Headless - session: X11 - status: supported - notes: Installs XFCE for xrdp without a local display manager. -down: {destructive: true} +- platform: ubuntu + desktop: XFCE Headless + session: X11 + status: supported + notes: Installs XFCE for xrdp without a local display manager. +down: + destructive: true env: [] desktop: - name: "XFCE Headless" - session: "X11" + name: XFCE Headless + session: X11 headless: true install: ubuntu: - steps: [base.sh, timezone.sh, provision/ubuntu/xfce-desktop-headless.sh] -bundles: - - id: desktop-xfce-headless - includes: [xfce-desktop-headless, rdp] + steps: + - base.sh + - timezone.sh + - provision/ubuntu/xfce-desktop-headless.sh diff --git a/xfce-desktop/eve-plugin.yaml b/xfce-desktop/eve-plugin.yaml index a72789a..1e967e1 100644 --- a/xfce-desktop/eve-plugin.yaml +++ b/xfce-desktop/eve-plugin.yaml @@ -3,25 +3,45 @@ kind: package id: xfce-desktop display_name: XFCE Desktop commands: - install: {exec: scripts/package-plugin, args: [xfce-desktop, install]} - status: {exec: scripts/package-plugin, args: [xfce-desktop, status]} - down: {exec: scripts/package-plugin, args: [xfce-desktop, down]} -supports: {os_families: [ubuntu]} -conflicts_with: [xfce-desktop-headless, gnome-desktop, gnome-desktop-headless, kde-desktop, kde-desktop-headless] + install: + exec: scripts/package-plugin + args: + - xfce-desktop + - install + status: + exec: scripts/package-plugin + args: + - xfce-desktop + - status + down: + exec: scripts/package-plugin + args: + - xfce-desktop + - down +supports: + os_families: + - ubuntu +conflicts_with: +- xfce-desktop-headless +- gnome-desktop +- gnome-desktop-headless +- kde-desktop +- kde-desktop-headless compatibility: - - platform: ubuntu - desktop: XFCE - session: X11 - status: supported - notes: Installs XFCE with LightDM autologin for a local graphical X11 session. -down: {destructive: true} +- platform: ubuntu + desktop: XFCE + session: X11 + status: supported + notes: Installs XFCE with LightDM autologin for a local graphical X11 session. +down: + destructive: true env: [] desktop: - name: "XFCE" - session: "X11" + name: XFCE + session: X11 install: ubuntu: - steps: [base.sh, timezone.sh, provision/ubuntu/xfce-desktop.sh] -bundles: - - id: desktop-xfce - includes: [xfce-desktop, rdp] + steps: + - base.sh + - timezone.sh + - provision/ubuntu/xfce-desktop.sh diff --git a/xpra/commands/__pycache__/_client.cpython-314.pyc b/xpra/commands/__pycache__/_client.cpython-314.pyc deleted file mode 100644 index 829c5a0..0000000 Binary files a/xpra/commands/__pycache__/_client.cpython-314.pyc and /dev/null differ diff --git a/xpra/commands/xpra-launch b/xpra/commands/xpra-launch index 62f1ab1..e3227f8 100755 --- a/xpra/commands/xpra-launch +++ b/xpra/commands/xpra-launch @@ -174,7 +174,7 @@ def main() -> int: print(f"xpra: remote 'xpra start :{display_num}' failed", file=sys.stderr) print(f" Is xpra installed? Run: eve instance provision --instance {instance}", file=sys.stderr) return 1 - print(f"[xpra] server running. Attach via the TUI package action.") + print("[xpra] server running. Attach via the TUI package action.") return 0 if action == "stop": diff --git a/xpra/eve-plugin.yaml b/xpra/eve-plugin.yaml index 73bc96c..b39ef21 100644 --- a/xpra/eve-plugin.yaml +++ b/xpra/eve-plugin.yaml @@ -3,42 +3,60 @@ kind: package id: xpra display_name: Xpra commands: - install: {exec: scripts/package-plugin, args: [xpra, install]} - status: {exec: scripts/package-plugin, args: [xpra, status]} - down: {exec: scripts/package-plugin, args: [xpra, down]} + install: + exec: scripts/package-plugin + args: + - xpra + - install + status: + exec: scripts/package-plugin + args: + - xpra + - status + down: + exec: scripts/package-plugin + args: + - xpra + - down supports: - os_families: [ubuntu] - arches: [amd64] - ubuntu_versions: ["22.04", "24.04"] + os_families: + - ubuntu + arches: + - amd64 + ubuntu_versions: + - '22.04' + - '24.04' compatibility_enforced: true compatibility: - - platform: ubuntu - desktop: XFCE - session: X11 - status: legacy - notes: Limited to Ubuntu 22.04/24.04; not an Ubuntu 26 path. - - platform: windows - desktop: Windows - session: Native - status: wip - notes: Available as an experimental package only. -down: {destructive: true} +- platform: ubuntu + desktop: XFCE + session: X11 + status: legacy + notes: Limited to Ubuntu 22.04/24.04; not an Ubuntu 26 path. +- platform: windows + desktop: Windows + session: Native + status: wip + notes: Available as an experimental package only. +down: + destructive: true env: [] actions: - - id: xpra-desktop - label: Xpra Desktop - target: xpra.desktop - exec: commands/xpra-launch - - id: xpra-attach - label: Xpra Attach - target: xpra.attach - exec: commands/xpra-launch +- id: xpra-desktop + label: Xpra Desktop + target: xpra.desktop + exec: commands/xpra-launch +- id: xpra-attach + label: Xpra Attach + target: xpra.attach + exec: commands/xpra-launch vagrant: port_forwards: - - {guest: 14500, host: 14500} + - guest: 14500 + host: 14500 install: ubuntu: - steps: [base.sh, timezone.sh, provision/ubuntu/xpra.sh] -bundles: - - id: remote-xpra - includes: [xpra] + steps: + - base.sh + - timezone.sh + - provision/ubuntu/xpra.sh