feat(v2): add packaging, installer, and CI release workflow (Phase 6)

Build-from-source installer (install-v2.sh) with dependency checks for
Node.js 20+, Rust 1.77+, and system libraries. Tauri bundle config for
.deb and AppImage targets. GitHub Actions workflow builds and uploads
release artifacts on version tags. Icons regenerated as RGBA PNGs.
This commit is contained in:
Hibryda 2026-03-06 14:23:09 +01:00
parent 643eb15697
commit 67875a1f70
8 changed files with 338 additions and 2 deletions

215
install-v2.sh Executable file
View file

@ -0,0 +1,215 @@
#!/bin/bash
set -euo pipefail
# BTerminal v2 installer — builds from source
# Requires: Node.js 20+, Rust toolchain, system libs (WebKit2GTK, etc.)
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BIN_DIR="$HOME/.local/bin"
ICON_DIR="$HOME/.local/share/icons/hicolor/scalable/apps"
DESKTOP_DIR="$HOME/.local/share/applications"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
info() { echo -e " ${GREEN}${NC} $1"; }
warn() { echo -e " ${YELLOW}!${NC} $1"; }
fail() { echo -e " ${RED}${NC} $1"; exit 1; }
echo "=== BTerminal v2 Installer ==="
echo ""
# ─── 1. Check Node.js ────────────────────────────────────────────────────────
echo "[1/6] Checking Node.js..."
if ! command -v node &>/dev/null; then
fail "Node.js not found. Install Node.js 20+ (https://nodejs.org)"
fi
NODE_VER=$(node -v | sed 's/^v//' | cut -d. -f1)
if [ "$NODE_VER" -lt 20 ]; then
fail "Node.js $NODE_VER found, need 20+. Upgrade at https://nodejs.org"
fi
info "Node.js $(node -v)"
if ! command -v npm &>/dev/null; then
fail "npm not found. Install Node.js with npm."
fi
info "npm $(npm -v)"
# ─── 2. Check Rust toolchain ─────────────────────────────────────────────────
echo "[2/6] Checking Rust toolchain..."
if ! command -v rustc &>/dev/null; then
fail "Rust not found. Install via: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
fi
RUST_VER=$(rustc --version | awk '{print $2}')
RUST_MAJOR=$(echo "$RUST_VER" | cut -d. -f1)
RUST_MINOR=$(echo "$RUST_VER" | cut -d. -f2)
if [ "$RUST_MAJOR" -lt 1 ] || { [ "$RUST_MAJOR" -eq 1 ] && [ "$RUST_MINOR" -lt 77 ]; }; then
fail "Rust $RUST_VER found, need 1.77+. Run: rustup update"
fi
info "Rust $RUST_VER"
if ! command -v cargo &>/dev/null; then
fail "Cargo not found. Reinstall Rust toolchain."
fi
info "Cargo $(cargo --version | awk '{print $2}')"
# ─── 3. Check system libraries ───────────────────────────────────────────────
echo "[3/6] Checking system libraries..."
MISSING_PKGS=()
# WebKit2GTK 4.1 (required by Tauri 2.x on Linux)
if ! pkg-config --exists webkit2gtk-4.1 2>/dev/null; then
MISSING_PKGS+=("libwebkit2gtk-4.1-dev")
fi
# GTK3
if ! pkg-config --exists gtk+-3.0 2>/dev/null; then
MISSING_PKGS+=("libgtk-3-dev")
fi
# GLib/GIO
if ! pkg-config --exists gio-2.0 2>/dev/null; then
MISSING_PKGS+=("libglib2.0-dev")
fi
# libayatana-appindicator (system tray)
if ! pkg-config --exists ayatana-appindicator3-0.1 2>/dev/null; then
MISSING_PKGS+=("libayatana-appindicator3-dev")
fi
# librsvg (SVG icon rendering)
if ! pkg-config --exists librsvg-2.0 2>/dev/null; then
MISSING_PKGS+=("librsvg2-dev")
fi
# libssl (TLS)
if ! pkg-config --exists openssl 2>/dev/null; then
MISSING_PKGS+=("libssl-dev")
fi
# Build essentials
if ! command -v cc &>/dev/null; then
MISSING_PKGS+=("build-essential")
fi
# pkg-config itself
if ! command -v pkg-config &>/dev/null; then
MISSING_PKGS+=("pkg-config")
fi
# curl (needed for some build steps)
if ! command -v curl &>/dev/null; then
MISSING_PKGS+=("curl")
fi
# wget (needed for AppImage tools)
if ! command -v wget &>/dev/null; then
MISSING_PKGS+=("wget")
fi
# FUSE (needed for AppImage)
if ! dpkg -s libfuse2t64 &>/dev/null 2>&1 && ! dpkg -s libfuse2 &>/dev/null 2>&1; then
# Try the newer package name first (Debian trixie+), fall back to older
if apt-cache show libfuse2t64 &>/dev/null 2>&1; then
MISSING_PKGS+=("libfuse2t64")
else
MISSING_PKGS+=("libfuse2")
fi
fi
if [ ${#MISSING_PKGS[@]} -gt 0 ]; then
echo ""
warn "Missing system packages: ${MISSING_PKGS[*]}"
echo ""
read -rp " Install with apt? [Y/n] " REPLY
REPLY=${REPLY:-Y}
if [[ "$REPLY" =~ ^[Yy]$ ]]; then
sudo apt update
sudo apt install -y "${MISSING_PKGS[@]}"
info "System packages installed"
else
fail "Cannot continue without: ${MISSING_PKGS[*]}"
fi
else
info "All system libraries present"
fi
# ─── 4. Install npm dependencies ─────────────────────────────────────────────
echo "[4/6] Installing npm dependencies..."
cd "$SCRIPT_DIR/v2"
npm ci --legacy-peer-deps
info "npm dependencies installed"
# ─── 5. Build Tauri app ──────────────────────────────────────────────────────
echo "[5/6] Building BTerminal (this may take a few minutes on first build)..."
npx tauri build 2>&1 | tail -5
info "Build complete"
# Locate built artifacts
BUNDLE_DIR="$SCRIPT_DIR/v2/src-tauri/target/release/bundle"
DEB_FILE=$(find "$BUNDLE_DIR/deb" -name "*.deb" 2>/dev/null | head -1)
APPIMAGE_FILE=$(find "$BUNDLE_DIR/appimage" -name "*.AppImage" 2>/dev/null | head -1)
BINARY="$SCRIPT_DIR/v2/src-tauri/target/release/bterminal"
# ─── 6. Install ──────────────────────────────────────────────────────────────
echo "[6/6] Installing..."
mkdir -p "$BIN_DIR" "$ICON_DIR" "$DESKTOP_DIR"
# Copy binary
cp "$BINARY" "$BIN_DIR/bterminal-v2"
chmod +x "$BIN_DIR/bterminal-v2"
info "Binary: $BIN_DIR/bterminal-v2"
# Copy icon
if [ -f "$SCRIPT_DIR/bterminal.svg" ]; then
cp "$SCRIPT_DIR/bterminal.svg" "$ICON_DIR/bterminal.svg"
info "Icon: $ICON_DIR/bterminal.svg"
fi
# Desktop entry
cat > "$DESKTOP_DIR/bterminal-v2.desktop" << EOF
[Desktop Entry]
Name=BTerminal v2
Comment=Multi-session Claude agent dashboard
Exec=$BIN_DIR/bterminal-v2
Icon=bterminal
Type=Application
Categories=System;TerminalEmulator;Development;
Terminal=false
StartupNotify=true
EOF
info "Desktop entry created"
echo ""
echo "=== Installation complete ==="
echo ""
if [ -n "${DEB_FILE:-}" ]; then
echo " .deb package: $DEB_FILE"
fi
if [ -n "${APPIMAGE_FILE:-}" ]; then
echo " AppImage: $APPIMAGE_FILE"
fi
echo " Binary: $BIN_DIR/bterminal-v2"
echo ""
echo "Run: bterminal-v2"
echo ""
echo "Make sure $BIN_DIR is in your PATH."
echo "If not, add to ~/.bashrc:"
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""