From c88385f7fff375d55a1f325e10de49c1bcdc62d6 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:35:02 +0000 Subject: [PATCH] fix: Linux .run installers now bundle all icons/assets and install properly - Add scripts/linux-install.sh: full installer that copies binary, icons, Mesa libs to /opt/Water, creates .desktop entry, installs XDG icons, and creates /usr/local/bin/water symlink - Add scripts/linux-uninstall.sh: clean uninstaller - Update Makefile _bundle-linux-mesa target to: - Bundle ALL icons (logo.png, logo-only.png, vscode.png) into icons/ dir - Bundle install.sh into the .run archive - Validate all required assets exist before bundling (no silent fallbacks) - Verify bundled contents before creating .run - Run install.sh (not launcher) when .run is executed - Update water-launcher.sh with icons/ directory documentation - No || true on critical icon/asset bundling steps --- Makefile | 34 +++++-- scripts/linux-install.sh | 182 +++++++++++++++++++++++++++++++++++++ scripts/linux-uninstall.sh | 66 ++++++++++++++ scripts/water-launcher.sh | 6 +- 4 files changed, 280 insertions(+), 8 deletions(-) create mode 100755 scripts/linux-install.sh create mode 100755 scripts/linux-uninstall.sh diff --git a/Makefile b/Makefile index 53560ae..d2fde93 100644 --- a/Makefile +++ b/Makefile @@ -395,21 +395,34 @@ release-linux: deps-linux-static @$(MAKE) _bundle-linux-mesa ARCH=arm64 @echo "--> Linux release .run installers built (with Mesa software renderer fallback)" -# Internal target: bundle a Linux binary with Mesa software renderer libs -# and create a .run self-extracting installer via makeself +# Internal target: bundle a Linux binary with Mesa software renderer libs, +# all icons/assets, installer script, and create a .run self-extracting +# installer via makeself. The .run runs linux-install.sh which copies +# everything to /opt/Water, creates .desktop entries, and installs icons. _bundle-linux-mesa: $(eval BUNDLE_DIR := $(DIST_DIR)/$(BINARY)-linux-$(ARCH)) $(eval MULTIARCH := $(shell dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null)) - @mkdir -p $(BUNDLE_DIR)/bin $(BUNDLE_DIR)/lib/dri - @# Validate icon file exists + @mkdir -p $(BUNDLE_DIR)/bin $(BUNDLE_DIR)/lib/dri $(BUNDLE_DIR)/icons + @# Validate ALL required icon/asset files exist (fail hard — no silent fallbacks) @test -f $(APP_ICON) || { echo "ERROR: Icon file not found: $(APP_ICON)"; exit 1; } + @test -f resources/logo.png || { echo "ERROR: Asset not found: resources/logo.png"; exit 1; } + @test -f resources/logo-only.png || { echo "ERROR: Asset not found: resources/logo-only.png"; exit 1; } + @test -f resources/vscode.png || { echo "ERROR: Asset not found: resources/vscode.png"; exit 1; } + @test -f scripts/linux-install.sh || { echo "ERROR: Installer script not found: scripts/linux-install.sh"; exit 1; } + @test -f scripts/water-launcher.sh || { echo "ERROR: Launcher script not found: scripts/water-launcher.sh"; exit 1; } @# Copy the binary into the bundle @mv $(DIST_DIR)/$(BINARY)-linux-$(ARCH)-bin $(BUNDLE_DIR)/bin/$(BINARY) @# Copy the launcher script @cp scripts/water-launcher.sh $(BUNDLE_DIR)/$(BINARY) @chmod +x $(BUNDLE_DIR)/$(BINARY) - @# Copy the icon into the bundle - @cp $(APP_ICON) $(BUNDLE_DIR)/icon.png + @# Copy ALL icons and assets into the bundle + @cp resources/logo.png $(BUNDLE_DIR)/icons/logo.png + @cp resources/logo-only.png $(BUNDLE_DIR)/icons/logo-only.png + @cp resources/vscode.png $(BUNDLE_DIR)/icons/vscode.png + @echo " Bundled icons: logo.png, logo-only.png, vscode.png" + @# Copy the installer script into the bundle + @cp scripts/linux-install.sh $(BUNDLE_DIR)/install.sh + @chmod +x $(BUNDLE_DIR)/install.sh @# Bundle Mesa software rendering libraries @echo " Copying Mesa software renderer libraries (multiarch=$(MULTIARCH))..." @CANDIDATE_PATHS="/usr/lib /usr/lib64"; \ @@ -464,11 +477,18 @@ _bundle-linux-mesa: echo " WARN: $$drv not found on system (searched: $$SEARCH_PATHS)"; \ fi; \ done + @# Verify all bundled icons are present before creating the .run + @test -f $(BUNDLE_DIR)/icons/logo.png || { echo "ERROR: Bundled icon missing: icons/logo.png"; exit 1; } + @test -f $(BUNDLE_DIR)/icons/logo-only.png || { echo "ERROR: Bundled icon missing: icons/logo-only.png"; exit 1; } + @test -f $(BUNDLE_DIR)/icons/vscode.png || { echo "ERROR: Bundled icon missing: icons/vscode.png"; exit 1; } + @test -f $(BUNDLE_DIR)/install.sh || { echo "ERROR: Installer script missing from bundle"; exit 1; } + @echo " Bundle contents:" + @find $(BUNDLE_DIR) -type f | sort | sed 's/^/ /' @# Create the .run self-extracting installer via makeself @echo " Creating self-extracting installer $(BINARY)-linux-$(ARCH).run..." @command -v makeself >/dev/null 2>&1 || { echo "ERROR: makeself is not installed"; exit 1; } @makeself --nox11 $(BUNDLE_DIR) $(DIST_DIR)/$(BINARY)-linux-$(ARCH).run \ - "$(BINARY) Installer" ./$(BINARY) "$@" + "$(BINARY) Installer" ./install.sh @rm -rf $(BUNDLE_DIR) @echo " $(DIST_DIR)/$(BINARY)-linux-$(ARCH).run" diff --git a/scripts/linux-install.sh b/scripts/linux-install.sh new file mode 100755 index 0000000..a8e3709 --- /dev/null +++ b/scripts/linux-install.sh @@ -0,0 +1,182 @@ +#!/bin/bash +# ============================================================================== +# Water AI — Linux Installer Script +# ============================================================================== +# +# This script is executed by the makeself .run self-extracting installer. +# It installs the Water AI application with: +# - Binary and launcher in /opt/Water/ +# - Desktop entry in ~/.local/share/applications/ +# - Icons in ~/.local/share/icons/hicolor/ +# - Mesa software renderer fallback libraries +# +# Usage: This script is called automatically by the .run installer. +# It can also be run manually from the extracted directory. +# +# ============================================================================== + +set -euo pipefail + +APP_NAME="Water" +APP_ID="ai.water.app" +INSTALL_DIR="/opt/${APP_NAME}" +ICON_DIR="${HOME}/.local/share/icons/hicolor" +DESKTOP_DIR="${HOME}/.local/share/applications" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# --- Colors for output -------------------------------------------------------- +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +info() { echo -e "${GREEN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } + +# --- Validate extracted contents ---------------------------------------------- +info "Validating installer contents..." + +REQUIRED_FILES=( + "bin/${APP_NAME}" + "${APP_NAME}" + "icons/logo.png" + "icons/logo-only.png" + "icons/vscode.png" +) + +for f in "${REQUIRED_FILES[@]}"; do + if [ ! -f "${SCRIPT_DIR}/${f}" ]; then + error "Missing required file: ${f}" + error "The installer archive appears to be incomplete." + exit 1 + fi +done + +info "All required files present." + +# --- Install application ------------------------------------------------------ +info "Installing ${APP_NAME} to ${INSTALL_DIR}..." + +# Create install directory (needs sudo for /opt) +if [ -w "$(dirname "${INSTALL_DIR}")" ]; then + mkdir -p "${INSTALL_DIR}/bin" "${INSTALL_DIR}/lib/dri" "${INSTALL_DIR}/icons" +else + sudo mkdir -p "${INSTALL_DIR}/bin" "${INSTALL_DIR}/lib/dri" "${INSTALL_DIR}/icons" +fi + +# Copy binary +if [ -w "${INSTALL_DIR}" ]; then + cp "${SCRIPT_DIR}/bin/${APP_NAME}" "${INSTALL_DIR}/bin/${APP_NAME}" + chmod +x "${INSTALL_DIR}/bin/${APP_NAME}" +else + sudo cp "${SCRIPT_DIR}/bin/${APP_NAME}" "${INSTALL_DIR}/bin/${APP_NAME}" + sudo chmod +x "${INSTALL_DIR}/bin/${APP_NAME}" +fi + +# Copy launcher script +if [ -w "${INSTALL_DIR}" ]; then + cp "${SCRIPT_DIR}/${APP_NAME}" "${INSTALL_DIR}/${APP_NAME}" + chmod +x "${INSTALL_DIR}/${APP_NAME}" +else + sudo cp "${SCRIPT_DIR}/${APP_NAME}" "${INSTALL_DIR}/${APP_NAME}" + sudo chmod +x "${INSTALL_DIR}/${APP_NAME}" +fi + +# Copy icons +for icon in logo.png logo-only.png vscode.png; do + if [ -w "${INSTALL_DIR}" ]; then + cp "${SCRIPT_DIR}/icons/${icon}" "${INSTALL_DIR}/icons/${icon}" + else + sudo cp "${SCRIPT_DIR}/icons/${icon}" "${INSTALL_DIR}/icons/${icon}" + fi +done + +# Copy Mesa software renderer libraries (if present) +if [ -d "${SCRIPT_DIR}/lib" ]; then + if [ -w "${INSTALL_DIR}" ]; then + cp -a "${SCRIPT_DIR}/lib/"* "${INSTALL_DIR}/lib/" 2>/dev/null || true + else + sudo cp -a "${SCRIPT_DIR}/lib/"* "${INSTALL_DIR}/lib/" 2>/dev/null || true + fi + info "Mesa software renderer libraries installed." +fi + +info "Application files installed to ${INSTALL_DIR}" + +# --- Install icons to XDG icon directories ------------------------------------ +info "Installing desktop icons..." + +mkdir -p "${ICON_DIR}/48x48/apps" +mkdir -p "${ICON_DIR}/128x128/apps" +mkdir -p "${ICON_DIR}/256x256/apps" +mkdir -p "${ICON_DIR}/scalable/apps" + +# Use logo-only.png as the app icon (it's the icon without text) +for size_dir in 48x48 128x128 256x256; do + cp "${SCRIPT_DIR}/icons/logo-only.png" "${ICON_DIR}/${size_dir}/apps/${APP_ID}.png" +done + +# Also install to a generic location for fallback +cp "${SCRIPT_DIR}/icons/logo-only.png" "${ICON_DIR}/scalable/apps/${APP_ID}.png" + +# Update icon cache if available +if command -v gtk-update-icon-cache >/dev/null 2>&1; then + gtk-update-icon-cache -f -t "${ICON_DIR}" 2>/dev/null || true +fi + +info "Icons installed to ${ICON_DIR}" + +# --- Create .desktop file ---------------------------------------------------- +info "Creating desktop entry..." + +mkdir -p "${DESKTOP_DIR}" + +cat > "${DESKTOP_DIR}/${APP_ID}.desktop" << EOF +[Desktop Entry] +Type=Application +Name=${APP_NAME} +Comment=Water AI Assistant +Exec=${INSTALL_DIR}/${APP_NAME} %U +Icon=${APP_ID} +Terminal=false +Categories=Development;Utility; +StartupNotify=true +StartupWMClass=${APP_NAME} +MimeType=x-scheme-handler/water; +EOF + +# Validate the desktop file if desktop-file-validate is available +if command -v desktop-file-validate >/dev/null 2>&1; then + desktop-file-validate "${DESKTOP_DIR}/${APP_ID}.desktop" 2>/dev/null || true +fi + +# Update desktop database if available +if command -v update-desktop-database >/dev/null 2>&1; then + update-desktop-database "${DESKTOP_DIR}" 2>/dev/null || true +fi + +info "Desktop entry created at ${DESKTOP_DIR}/${APP_ID}.desktop" + +# --- Create symlink in /usr/local/bin for PATH access ------------------------- +SYMLINK_PATH="/usr/local/bin/water" +if [ -w "$(dirname "${SYMLINK_PATH}")" ]; then + ln -sf "${INSTALL_DIR}/${APP_NAME}" "${SYMLINK_PATH}" + info "Symlink created: ${SYMLINK_PATH} -> ${INSTALL_DIR}/${APP_NAME}" +elif command -v sudo >/dev/null 2>&1; then + sudo ln -sf "${INSTALL_DIR}/${APP_NAME}" "${SYMLINK_PATH}" 2>/dev/null || \ + warn "Could not create symlink at ${SYMLINK_PATH} (run with sudo for PATH access)" +else + warn "Could not create symlink at ${SYMLINK_PATH} (no sudo available)" +fi + +# --- Done --------------------------------------------------------------------- +echo "" +info "╔══════════════════════════════════════════════════════════════╗" +info "║ ${APP_NAME} AI installed successfully! ║" +info "╠══════════════════════════════════════════════════════════════╣" +info "║ Install location: ${INSTALL_DIR}" +info "║ Launch from: Application menu or run 'water' in terminal" +info "║ Desktop entry: ${DESKTOP_DIR}/${APP_ID}.desktop" +info "╚══════════════════════════════════════════════════════════════╝" +echo "" diff --git a/scripts/linux-uninstall.sh b/scripts/linux-uninstall.sh new file mode 100755 index 0000000..0a69316 --- /dev/null +++ b/scripts/linux-uninstall.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# ============================================================================== +# Water AI — Linux Uninstaller Script +# ============================================================================== + +set -euo pipefail + +APP_NAME="Water" +APP_ID="ai.water.app" +INSTALL_DIR="/opt/${APP_NAME}" +ICON_DIR="${HOME}/.local/share/icons/hicolor" +DESKTOP_DIR="${HOME}/.local/share/applications" + +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' + +info() { echo -e "${GREEN}[INFO]${NC} $*"; } +error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } + +info "Uninstalling ${APP_NAME}..." + +# Remove desktop entry +if [ -f "${DESKTOP_DIR}/${APP_ID}.desktop" ]; then + rm -f "${DESKTOP_DIR}/${APP_ID}.desktop" + info "Removed desktop entry" +fi + +# Remove icons +for size_dir in 48x48 128x128 256x256 scalable; do + rm -f "${ICON_DIR}/${size_dir}/apps/${APP_ID}.png" 2>/dev/null || true +done + +# Update icon cache +if command -v gtk-update-icon-cache >/dev/null 2>&1; then + gtk-update-icon-cache -f -t "${ICON_DIR}" 2>/dev/null || true +fi +info "Removed icons" + +# Remove symlink +SYMLINK_PATH="/usr/local/bin/water" +if [ -L "${SYMLINK_PATH}" ]; then + if [ -w "$(dirname "${SYMLINK_PATH}")" ]; then + rm -f "${SYMLINK_PATH}" + else + sudo rm -f "${SYMLINK_PATH}" 2>/dev/null || true + fi + info "Removed symlink" +fi + +# Remove install directory +if [ -d "${INSTALL_DIR}" ]; then + if [ -w "$(dirname "${INSTALL_DIR}")" ]; then + rm -rf "${INSTALL_DIR}" + else + sudo rm -rf "${INSTALL_DIR}" + fi + info "Removed ${INSTALL_DIR}" +fi + +# Update desktop database +if command -v update-desktop-database >/dev/null 2>&1; then + update-desktop-database "${DESKTOP_DIR}" 2>/dev/null || true +fi + +info "${APP_NAME} has been uninstalled." diff --git a/scripts/water-launcher.sh b/scripts/water-launcher.sh index 0a32e33..34a9ebb 100755 --- a/scripts/water-launcher.sh +++ b/scripts/water-launcher.sh @@ -7,7 +7,7 @@ # software OpenGL renderer (llvmpipe/swrast) if the system lacks a hardware # OpenGL driver. # -# Directory layout expected: +# Directory layout expected (works both in bundle and installed location): # Water — this launcher script (renamed from water-launcher.sh) # bin/Water — the actual binary # lib/ — bundled Mesa software rendering libraries @@ -15,6 +15,10 @@ # libEGL.so.1 # libGLX.so.0 # swrast_dri.so (or other Mesa DRI drivers) +# icons/ — application icons +# logo.png +# logo-only.png +# vscode.png # # ==============================================================================