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.
104
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-linux:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
libwebkit2gtk-4.1-dev \
|
||||||
|
libgtk-3-dev \
|
||||||
|
libayatana-appindicator3-dev \
|
||||||
|
librsvg2-dev \
|
||||||
|
libssl-dev \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
libfuse2
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: v2/package-lock.json
|
||||||
|
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Cache Rust dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
v2/src-tauri/target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ hashFiles('v2/src-tauri/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-
|
||||||
|
|
||||||
|
- name: Install npm dependencies
|
||||||
|
working-directory: v2
|
||||||
|
run: npm ci --legacy-peer-deps
|
||||||
|
|
||||||
|
- name: Build Tauri app
|
||||||
|
working-directory: v2
|
||||||
|
run: npx tauri build
|
||||||
|
|
||||||
|
- name: List build artifacts
|
||||||
|
run: |
|
||||||
|
find v2/src-tauri/target/release/bundle -type f \( -name "*.deb" -o -name "*.AppImage" \) | head -20
|
||||||
|
|
||||||
|
- name: Upload .deb
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: bterminal-deb
|
||||||
|
path: v2/src-tauri/target/release/bundle/deb/*.deb
|
||||||
|
|
||||||
|
- name: Upload AppImage
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: bterminal-appimage
|
||||||
|
path: v2/src-tauri/target/release/bundle/appimage/*.AppImage
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: build-linux
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download .deb
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: bterminal-deb
|
||||||
|
path: artifacts/
|
||||||
|
|
||||||
|
- name: Download AppImage
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: bterminal-appimage
|
||||||
|
path: artifacts/
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
generate_release_notes: true
|
||||||
|
files: |
|
||||||
|
artifacts/*.deb
|
||||||
|
artifacts/*.AppImage
|
||||||
215
install-v2.sh
Executable 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\""
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 46 KiB |
|
|
@ -26,13 +26,30 @@
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"active": true,
|
"active": true,
|
||||||
"targets": "all",
|
"targets": ["deb", "appimage"],
|
||||||
"icon": [
|
"icon": [
|
||||||
"icons/32x32.png",
|
"icons/32x32.png",
|
||||||
"icons/128x128.png",
|
"icons/128x128.png",
|
||||||
"icons/128x128@2x.png",
|
"icons/128x128@2x.png",
|
||||||
"icons/icon.icns",
|
"icons/icon.icns",
|
||||||
"icons/icon.ico"
|
"icons/icon.ico"
|
||||||
]
|
],
|
||||||
|
"category": "DeveloperTool",
|
||||||
|
"shortDescription": "Multi-session Claude agent dashboard",
|
||||||
|
"longDescription": "BTerminal is a terminal emulator with integrated Claude AI agent sessions, SSH management, and a tiling pane layout. Built with Tauri, Svelte 5, and xterm.js.",
|
||||||
|
"linux": {
|
||||||
|
"deb": {
|
||||||
|
"depends": [
|
||||||
|
"libwebkit2gtk-4.1-0",
|
||||||
|
"libgtk-3-0",
|
||||||
|
"libayatana-appindicator3-1"
|
||||||
|
],
|
||||||
|
"section": "devel",
|
||||||
|
"priority": "optional"
|
||||||
|
},
|
||||||
|
"appimage": {
|
||||||
|
"bundleMediaFramework": false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||