#!/usr/bin/env bash # SPDX-License-Identifier: LicenseRef-Proprietary set -euo pipefail version='0.1.0-preview.20261027.7' base="https://muzz.in/gravitas/downloads/$version" selection=desktop action=install appearance=yes duo_top_head= die() { printf 'Gravitas: %s\n' "$*" >&2; exit 1; } usage() { cat <<'HELP' Gravitas development preview — Ubuntu 24.04 and 26.04, Intel/AMD 64-bit bash install.sh Install desktop, all 14 apps, login and boot themes bash install.sh --desktop-only Keep your current login manager and boot theme bash install.sh --duo-top-head 0 Enable Duo boot with a calibrated top-panel index (0 or 1) bash install.sh --app command Install only Command (Wayland terminal) bash install.sh --app reap Install only Reap (task manager) bash install.sh --app type Install only Type (text and Markdown editor) bash install.sh --app capture Install only Capture (screen recorder) bash install.sh --app drill Install only Drill (file browser) bash install.sh --app image Install only Image (image viewer and editor) bash install.sh --app documents Install only Documents (PDF reader) bash install.sh --app calculator Install only Calculator (scientific calculator) bash install.sh --app clocks Install only Clocks (clocks and timers) bash install.sh --app listen Install only Listen (music player) bash install.sh --app video Install only Video (local video player) bash install.sh --app storage Install only Storage (disk usage) bash install.sh --app backups Install only Backups (local snapshots and restore) bash install.sh --app software Install only Software (PackageKit browser) bash install.sh --check Check compatibility without making changes bash install.sh --uninstall Remove the Gravitas desktop packages bash install.sh --app command --uninstall The full install selects the Gravitas SDDM login screen and Plymouth/GRUB boot themes for the next reboot. It saves your previous settings for removal. Ubuntu remains installed and available in the login-screen session menu. Installation never switches sessions or restarts the display manager. Re-run the latest installer to update. Removal preserves your personal settings and leaves Ubuntu dependencies and separately installed Gravitas apps installed. Remove each app with --app NAME --uninstall. Standalone apps use your Wayland session; the Gravitas compositor is not required. The small shared theme package is included with every standalone app. Duo is off by default. It requires exactly two displays and explicit calibration; indices are not connector names and can change between boots or driver updates. Disable it later with: sudo gravitas-appearance duo --disable HELP } while (($#)); do case "$1" in --app) (($# >= 2)) || die '--app needs an app name' case "$2" in command|reap|type|capture|drill|image|documents|calculator|clocks|listen|video|storage|backups|software) ;; *) die "Unknown app: $2. Use --help to list available apps." ;; esac selection=$2 shift 2 ;; --duo-top-head) (($# >= 2)) || die '--duo-top-head needs a calibrated index (0 or 1)' [[ $2 == 0 || $2 == 1 ]] || die '--duo-top-head must be 0 or 1' duo_top_head=$2 shift 2 ;; --check) action=check; shift ;; --desktop-only) appearance=no; shift ;; --uninstall) action=uninstall; shift ;; -h|--help) usage; exit 0 ;; *) die "Unknown option: $1 (use --help)" ;; esac done if [[ -n $duo_top_head && ($selection != desktop || $appearance != yes || $action == uninstall) ]]; then die '--duo-top-head requires a full desktop install with boot themes.' fi [[ -r /etc/os-release ]] || die 'Cannot identify this operating system.' # shellcheck disable=SC1091 source /etc/os-release [[ ${ID:-} == ubuntu && (${VERSION_ID:-} == 24.04 || ${VERSION_ID:-} == 26.04) ]] || die 'This preview supports Ubuntu 24.04 and 26.04 only.' [[ $(dpkg --print-architecture) == amd64 && $(uname -m) == x86_64 ]] || die 'This preview needs an Intel/AMD 64-bit (amd64) system.' command -v apt-get >/dev/null || die 'apt-get is required.' if [[ $selection == desktop && $action != uninstall ]]; then [[ -e /etc/X11/default-display-manager ]] || die 'Install an Ubuntu desktop and display manager first, or use --app command.' if [[ $appearance == yes ]]; then if [[ ! -f /boot/grub/grub.cfg ]] || ! compgen -G '/boot/initrd.img-*' >/dev/null; then die 'Boot themes require GRUB and an existing initramfs. Use --desktop-only to keep your boot/login setup.' fi fi fi if [[ $action == check ]]; then printf 'Compatible: Ubuntu %s amd64, %s preview %s. No changes made.\n' "$VERSION_ID" "$selection" "$version" exit 0 fi as_root() { if ((EUID == 0)); then "$@"; else sudo -- "$@"; fi } if ((EUID != 0)); then command -v sudo >/dev/null || die 'sudo is required; alternatively run the downloaded script as root.'; fi if [[ $action == uninstall ]]; then if [[ $selection != desktop ]]; then # apt would otherwise remove the desktop too, since it depends on these apps. if dpkg-query -W -f='${Status}' gravitas-desktop 2>/dev/null | grep -qx 'install ok installed'; then die "The Gravitas desktop requires this app. Run --uninstall first to remove the desktop, then --app $selection --uninstall." fi candidates=("gravitas-$selection") else if command -v gravitas-appearance >/dev/null; then as_root gravitas-appearance restore; fi candidates=(gravitas-desktop gravitas-wallpapers gravitas-xwayland gravitas-appearance-qt5 gravitas-appearance-qt6) fi installed=() for package in "${candidates[@]}"; do if dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -qx 'install ok installed'; then installed+=("$package") fi done if ((${#installed[@]})); then as_root apt-get remove -y "${installed[@]}"; fi printf 'Removed. Your settings and Ubuntu desktop were preserved.\n' exit 0 fi if [[ $selection != desktop ]] && dpkg-query -W -f='${Status}' gravitas-desktop 2>/dev/null | grep -qx 'install ok installed'; then desktop_version=$(dpkg-query -W -f='${Version}' gravitas-desktop) [[ $desktop_version == "$version" ]] || die 'Update the full Gravitas desktop together before updating an individual app: run this installer without --app (or with --desktop-only).' fi printf 'Installing Gravitas %s preview %s. apt will request administrator permission.\n' "$selection" "$version" if ! command -v curl >/dev/null || [[ ! -s /etc/ssl/certs/ca-certificates.crt ]]; then as_root apt-get update as_root apt-get install -y --no-install-recommends curl ca-certificates fi umask 077 download_dir=$(mktemp -d /tmp/gravitas-install.XXXXXXXX) trap 'rm -rf -- "$download_dir"' EXIT packages=() download() { local name=$1 expected=$2 architecture=${3:-amd64} file file="$download_dir/${name}_${version}_${architecture}.deb" curl --fail --show-error --silent --location --proto '=https' --proto-redir '=https' \ --connect-timeout 20 --max-time 600 --retry 2 "$base/${name}_${version}_${architecture}.deb" -o "$file" printf '%s %s\n' "$expected" "$file" | sha256sum --check --status || die "Checksum mismatch for $name; no Gravitas packages were installed." packages+=("$file") } download gravitas-themes '8a32564acf1ced5a0c9cee2fcb80a3c80ffb5a3756e4cbe5fc7add9a13869b4e' all if [[ $selection == command || $selection == desktop ]]; then download gravitas-command '0d6dceeb5331c9013c13e14ead6b9b346a5970e6e0aa5cd2394efd8aad6098b5' fi if [[ $selection == reap || $selection == desktop ]]; then download gravitas-reap 'b5768c133bb2ae4858431cee75fc058b75ef903d254563b6757dcc59a9e5bd0e' fi if [[ $selection == type || $selection == desktop ]]; then download gravitas-type '04758c0d96d54cfd24268649b8f575e18e5931efd770a9c72bd3d18dfdf9ee3e' fi if [[ $selection == capture || $selection == desktop ]]; then download gravitas-capture '1c4a537db2b0732e611ff3724cd1349e34d807a0c6d72536fd30d0de70466c4b' fi if [[ $selection == drill || $selection == desktop ]]; then download gravitas-drill '220a741c3b7d4ee15b3751e5dc406b0663149a080ff53a86dcaf6e7358b9375a' fi if [[ $selection == image || $selection == desktop ]]; then download gravitas-image '905a9b5ddb7f1c26d2ac6f816bb102b26e4b81d55fd1b7d3eb90fccd67d3785b' fi if [[ $selection == documents || $selection == desktop ]]; then download gravitas-documents '3ed01b7cb65c684b6b0f09ba1bed5e52e8ba940d58d366ed3eb93e95ac3dbd01' fi if [[ $selection == calculator || $selection == desktop ]]; then download gravitas-calculator 'c6f0af4595ce11e9025e75a5641a0d8cc59064b990e4955ba479e6597e7f9d86' fi if [[ $selection == clocks || $selection == desktop ]]; then download gravitas-clocks 'e3f72d914f06464c14270583cdde2a17e4d512f377bd3395c197befa353c1a78' fi if [[ $selection == listen || $selection == desktop ]]; then download gravitas-listen '8e26383ee7d867fc31eb915ec21b2db870d79a93157ecbbf780c8033057e4ee1' fi if [[ $selection == video || $selection == desktop ]]; then download gravitas-video '25e7dc29325dbc00062ac00515b720836a60112095b7120bd7b0accab43d648f' fi if [[ $selection == storage || $selection == desktop ]]; then download gravitas-storage '941f1dae17ae3a020c17b86dc01c55e7472285e14aa1b0bff4ed7103412478c0' fi if [[ $selection == backups || $selection == desktop ]]; then download gravitas-backups '688d9ce93ca2e7abc75db8a6a83604b728104ac56deb4dac4f7283453072307c' fi if [[ $selection == software || $selection == desktop ]]; then download gravitas-software '3c7ae135daee1207d78f025c0cfc8e33f1f9fbc92824be022a5d74441a2e904d' fi if [[ $selection == desktop ]]; then download gravitas-wallpapers '37b79ebd743e762965add26dc22343033cff4d4beeaca924d04292faad9cfad2' all download gravitas-xwayland '6e92cc2b9fe70515be51c95b8fdcd721b8846f79ad89b4d1012794161a9e98ed' download gravitas-desktop '9a18c4ed58ae7c714d914ff12f37898a0580ddb2086d31ec4d0f06c244956f70' if [[ $appearance == yes ]]; then if [[ $VERSION_ID == 24.04 ]]; then download gravitas-appearance-qt5 '0291ab9f9869270ad7350eff1b98918fca8d07b924d9a90b70d32a074108ce63' else download gravitas-appearance-qt6 '3aaa922575ebeb45ab8c739b2660250ba69322c0041b91ee98ae1ca71f2b6db4' fi curl --fail --show-error --silent --location --proto '=https' --proto-redir '=https' \ --connect-timeout 20 --max-time 600 --retry 2 "$base/system-appearance.py" -o "$download_dir/system-appearance.py" printf '%s %s\n' 'd9f941ae1a80b1be4f98f18ad0747940a23765bd6282fdacf0670899f4ee1612' "$download_dir/system-appearance.py" | sha256sum --check --status || die 'Checksum mismatch for the appearance helper; no Gravitas packages were installed.' fi fi # All payloads must verify before apt is allowed to install any of them. as_root apt-get update if [[ $selection == desktop && $appearance == yes ]]; then if ! command -v python3 >/dev/null; then as_root env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends python3; fi as_root python3 "$download_dir/system-appearance.py" prepare fi apt_options=(--no-install-recommends) if [[ $selection != desktop ]]; then apt_options+=(--no-remove); fi if ! as_root env DEBIAN_FRONTEND=noninteractive apt-get install -y "${apt_options[@]}" "${packages[@]}"; then if [[ $selection == desktop && $appearance == yes ]]; then as_root python3 "$download_dir/system-appearance.py" restore; fi die 'Package installation failed. Resolve the apt error above before retrying.' fi if [[ $selection == desktop ]]; then if [[ $appearance == yes ]]; then as_root gravitas-appearance apply if [[ -n $duo_top_head ]]; then as_root gravitas-appearance duo --top-head "$duo_top_head"; fi printf 'Save your work and reboot to use the new login and boot themes.\n' fi cat <<'DONE' Gravitas is installed. Save your work, log out, and select Gravitas at the login screen. Super opens apps; Super+Enter opens Command; Super+L locks; Super+Shift+E logs out. Your previous desktop remains in the login menu. DONE elif [[ $selection == command ]]; then printf 'Command is installed. Open Command from your app menu in a Wayland session, or run litty.\n' elif [[ $selection == reap ]]; then printf 'Reap is installed. Open Reap from your app menu in a Wayland session, or run gravitas-reap.\n' elif [[ $selection == capture ]]; then printf 'Capture is installed. Open Capture from your app menu, or run gravitas-capture.\n' else printf '%s is installed. Open it from your app menu in a Wayland session, or run gravitas-%s.\n' "${selection^}" "$selection" fi