feat(linux): Ubuntu setup script and systemd improvements (#335)
* feat(linux): add Ubuntu setup script and improve systemd services Add comprehensive Ubuntu setup script that transforms a fresh Ubuntu installation into a native tool building machine with: - System dependencies (WebKitGTK, GTK3, libappindicator) - Development tools (Go 1.25.6, Node.js 22.x, gh CLI) - Claude Code CLI installation - Core CLI and core-ide from GitHub releases - XDG autostart configuration - SSH key generation and GitHub authentication Improve systemd services: - Add security hardening to system service (NoNewPrivileges, PrivateTmp, ProtectSystem) - Add user-level service for non-root deployment - Include user service in nfpm package Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: apply gofmt formatting to io.go and transport_tcp.go Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <developers@lethean.io> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Snider <snider@host.uk.com>
This commit is contained in:
parent
4debdc1449
commit
48511478b8
4 changed files with 388 additions and 3 deletions
|
|
@ -1,15 +1,32 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Core IDE Job Runner
|
Description=Core IDE Job Runner (Headless Mode)
|
||||||
|
Documentation=https://github.com/host-uk/core
|
||||||
After=network-online.target
|
After=network-online.target
|
||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=/usr/local/bin/core-ide --headless
|
ExecStart=/usr/local/bin/core-ide --headless
|
||||||
Restart=always
|
Restart=on-failure
|
||||||
RestartSec=10
|
RestartSec=10
|
||||||
|
TimeoutStopSec=30
|
||||||
|
|
||||||
|
# Environment
|
||||||
Environment=CORE_DAEMON=1
|
Environment=CORE_DAEMON=1
|
||||||
Environment=GITHUB_TOKEN=
|
# GitHub token should be set via systemctl edit or drop-in file
|
||||||
|
# Environment=GITHUB_TOKEN=
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=read-only
|
||||||
|
ReadWritePaths=/home
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=core-ide
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
|
||||||
26
internal/core-ide/build/linux/core-ide.user.service
Normal file
26
internal/core-ide/build/linux/core-ide.user.service
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Core IDE Job Runner (User Mode)
|
||||||
|
Documentation=https://github.com/host-uk/core
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=%h/.local/bin/core-ide --headless
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
TimeoutStopSec=30
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
Environment=CORE_DAEMON=1
|
||||||
|
# GitHub token from environment
|
||||||
|
# Set via: systemctl --user edit core-ide
|
||||||
|
# Or in ~/.config/environment.d/core-ide.conf
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=core-ide
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
|
@ -23,9 +23,14 @@ contents:
|
||||||
dst: "/usr/share/icons/hicolor/128x128/apps/core-ide.png"
|
dst: "/usr/share/icons/hicolor/128x128/apps/core-ide.png"
|
||||||
- src: "./build/linux/core-ide.desktop"
|
- src: "./build/linux/core-ide.desktop"
|
||||||
dst: "/usr/share/applications/core-ide.desktop"
|
dst: "/usr/share/applications/core-ide.desktop"
|
||||||
|
# System-wide service (requires root)
|
||||||
- src: "./build/linux/core-ide.service"
|
- src: "./build/linux/core-ide.service"
|
||||||
dst: "/etc/systemd/system/core-ide.service"
|
dst: "/etc/systemd/system/core-ide.service"
|
||||||
type: config
|
type: config
|
||||||
|
# User service template (for per-user deployment)
|
||||||
|
- src: "./build/linux/core-ide.user.service"
|
||||||
|
dst: "/usr/share/core-ide/core-ide.user.service"
|
||||||
|
type: config
|
||||||
|
|
||||||
# Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1
|
# Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1
|
||||||
depends:
|
depends:
|
||||||
|
|
|
||||||
337
scripts/setup-ubuntu.sh
Executable file
337
scripts/setup-ubuntu.sh
Executable file
|
|
@ -0,0 +1,337 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# setup-ubuntu.sh - Turn a fresh Ubuntu into a native tool building machine
|
||||||
|
#
|
||||||
|
# Installs and configures:
|
||||||
|
# - System dependencies (WebKitGTK, build tools)
|
||||||
|
# - Development tools (Go, Node.js, Git, gh CLI)
|
||||||
|
# - Claude Code CLI
|
||||||
|
# - core-ide with system tray integration
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# curl -fsSL https://host.uk.com/setup-ubuntu | bash
|
||||||
|
# # or
|
||||||
|
# ./scripts/setup-ubuntu.sh
|
||||||
|
#
|
||||||
|
# Environment variables (optional):
|
||||||
|
# SKIP_GUI=1 - Skip GUI components (headless server)
|
||||||
|
# SKIP_CLAUDE=1 - Skip Claude Code installation
|
||||||
|
# GITHUB_TOKEN=xxx - Pre-configure GitHub token
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||||
|
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||||
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [[ $EUID -eq 0 ]]; then
|
||||||
|
log_error "Don't run this script as root. It will use sudo when needed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check Ubuntu version
|
||||||
|
if [[ -f /etc/os-release ]]; then
|
||||||
|
. /etc/os-release
|
||||||
|
if [[ "$ID" != "ubuntu" ]] && [[ "$ID_LIKE" != *"ubuntu"* ]]; then
|
||||||
|
log_warn "This script is designed for Ubuntu. Your distro: $ID"
|
||||||
|
read -p "Continue anyway? [y/N] " -n 1 -r
|
||||||
|
echo
|
||||||
|
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Setting up Ubuntu as a native tool building machine..."
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 1: System Dependencies
|
||||||
|
# ============================================================================
|
||||||
|
log_info "Installing system dependencies..."
|
||||||
|
|
||||||
|
sudo apt-get update
|
||||||
|
|
||||||
|
# Build essentials
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
git \
|
||||||
|
jq \
|
||||||
|
unzip
|
||||||
|
|
||||||
|
# GUI dependencies (skip for headless)
|
||||||
|
if [[ -z "${SKIP_GUI:-}" ]]; then
|
||||||
|
log_info "Installing GUI dependencies (WebKitGTK, GTK3)..."
|
||||||
|
|
||||||
|
# Check Ubuntu version for correct WebKitGTK package
|
||||||
|
UBUNTU_VERSION=$(lsb_release -rs 2>/dev/null || echo "22.04")
|
||||||
|
|
||||||
|
# WebKitGTK 4.1 for Ubuntu 22.04+, 4.0 for older
|
||||||
|
if dpkg --compare-versions "$UBUNTU_VERSION" "ge" "22.04"; then
|
||||||
|
WEBKIT_PKG="libwebkit2gtk-4.1-dev"
|
||||||
|
else
|
||||||
|
WEBKIT_PKG="libwebkit2gtk-4.0-dev"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo apt-get install -y \
|
||||||
|
libgtk-3-dev \
|
||||||
|
"$WEBKIT_PKG" \
|
||||||
|
libappindicator3-dev \
|
||||||
|
gir1.2-appindicator3-0.1
|
||||||
|
|
||||||
|
log_success "GUI dependencies installed"
|
||||||
|
else
|
||||||
|
log_info "Skipping GUI dependencies (SKIP_GUI=1)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "System dependencies installed"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 2: Go
|
||||||
|
# ============================================================================
|
||||||
|
GO_VERSION="1.25.6"
|
||||||
|
|
||||||
|
if command -v go &>/dev/null && [[ "$(go version 2>/dev/null | grep -oP 'go\d+\.\d+' | head -1)" == "go1.25" ]]; then
|
||||||
|
log_success "Go $GO_VERSION already installed"
|
||||||
|
else
|
||||||
|
log_info "Installing Go $GO_VERSION..."
|
||||||
|
|
||||||
|
ARCH=$(dpkg --print-architecture)
|
||||||
|
case $ARCH in
|
||||||
|
amd64) GO_ARCH="amd64" ;;
|
||||||
|
arm64) GO_ARCH="arm64" ;;
|
||||||
|
*) log_error "Unsupported architecture: $ARCH"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz
|
||||||
|
sudo rm -rf /usr/local/go
|
||||||
|
sudo tar -C /usr/local -xzf /tmp/go.tar.gz
|
||||||
|
rm /tmp/go.tar.gz
|
||||||
|
|
||||||
|
# Add to path
|
||||||
|
if ! grep -q '/usr/local/go/bin' ~/.bashrc; then
|
||||||
|
echo 'export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin' >> ~/.bashrc
|
||||||
|
fi
|
||||||
|
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
|
||||||
|
|
||||||
|
log_success "Go $GO_VERSION installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 3: Node.js (via fnm for version management)
|
||||||
|
# ============================================================================
|
||||||
|
NODE_VERSION="22"
|
||||||
|
|
||||||
|
if command -v node &>/dev/null && [[ "$(node -v 2>/dev/null | cut -d. -f1)" == "v${NODE_VERSION}" ]]; then
|
||||||
|
log_success "Node.js $NODE_VERSION already installed"
|
||||||
|
else
|
||||||
|
log_info "Installing Node.js $NODE_VERSION via fnm..."
|
||||||
|
|
||||||
|
# Install fnm
|
||||||
|
if ! command -v fnm &>/dev/null; then
|
||||||
|
curl -fsSL https://fnm.vercel.app/install | bash -s -- --skip-shell
|
||||||
|
export PATH="$HOME/.local/share/fnm:$PATH"
|
||||||
|
eval "$(fnm env)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install Node.js
|
||||||
|
fnm install $NODE_VERSION
|
||||||
|
fnm use $NODE_VERSION
|
||||||
|
fnm default $NODE_VERSION
|
||||||
|
|
||||||
|
# Add fnm to bashrc
|
||||||
|
if ! grep -q 'fnm env' ~/.bashrc; then
|
||||||
|
echo 'eval "$(fnm env --use-on-cd)"' >> ~/.bashrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Node.js $NODE_VERSION installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 4: GitHub CLI
|
||||||
|
# ============================================================================
|
||||||
|
if command -v gh &>/dev/null; then
|
||||||
|
log_success "GitHub CLI already installed"
|
||||||
|
else
|
||||||
|
log_info "Installing GitHub CLI..."
|
||||||
|
|
||||||
|
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | \
|
||||||
|
sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
|
||||||
|
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y gh
|
||||||
|
|
||||||
|
log_success "GitHub CLI installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 5: Claude Code CLI
|
||||||
|
# ============================================================================
|
||||||
|
if [[ -z "${SKIP_CLAUDE:-}" ]]; then
|
||||||
|
if command -v claude &>/dev/null; then
|
||||||
|
log_success "Claude Code already installed"
|
||||||
|
else
|
||||||
|
log_info "Installing Claude Code CLI..."
|
||||||
|
|
||||||
|
# Install via npm (requires Node.js)
|
||||||
|
npm install -g @anthropic-ai/claude-code
|
||||||
|
|
||||||
|
log_success "Claude Code installed"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_info "Skipping Claude Code (SKIP_CLAUDE=1)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 6: core CLI
|
||||||
|
# ============================================================================
|
||||||
|
if command -v core &>/dev/null; then
|
||||||
|
log_success "core CLI already installed"
|
||||||
|
else
|
||||||
|
log_info "Installing core CLI..."
|
||||||
|
|
||||||
|
# Install from releases
|
||||||
|
ARCH=$(dpkg --print-architecture)
|
||||||
|
CORE_URL="https://github.com/host-uk/core/releases/latest/download/core-linux-${ARCH}"
|
||||||
|
|
||||||
|
curl -fsSL "$CORE_URL" -o /tmp/core
|
||||||
|
chmod +x /tmp/core
|
||||||
|
sudo mv /tmp/core /usr/local/bin/core
|
||||||
|
|
||||||
|
log_success "core CLI installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 7: core-ide (GUI mode)
|
||||||
|
# ============================================================================
|
||||||
|
if [[ -z "${SKIP_GUI:-}" ]]; then
|
||||||
|
if command -v core-ide &>/dev/null; then
|
||||||
|
log_success "core-ide already installed"
|
||||||
|
else
|
||||||
|
log_info "Installing core-ide..."
|
||||||
|
|
||||||
|
ARCH=$(dpkg --print-architecture)
|
||||||
|
IDE_URL="https://github.com/host-uk/core/releases/latest/download/core-ide-linux-${ARCH}.deb"
|
||||||
|
|
||||||
|
curl -fsSL "$IDE_URL" -o /tmp/core-ide.deb
|
||||||
|
sudo dpkg -i /tmp/core-ide.deb || sudo apt-get install -f -y
|
||||||
|
rm /tmp/core-ide.deb
|
||||||
|
|
||||||
|
log_success "core-ide installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Setup autostart
|
||||||
|
log_info "Configuring autostart..."
|
||||||
|
|
||||||
|
mkdir -p ~/.config/autostart
|
||||||
|
cat > ~/.config/autostart/core-ide.desktop << 'EOF'
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=Core IDE
|
||||||
|
Comment=Development Environment
|
||||||
|
Exec=/usr/local/bin/core-ide
|
||||||
|
Icon=core-ide
|
||||||
|
Terminal=false
|
||||||
|
Categories=Development;
|
||||||
|
X-GNOME-Autostart-enabled=true
|
||||||
|
EOF
|
||||||
|
|
||||||
|
log_success "Autostart configured"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 8: GitHub Authentication
|
||||||
|
# ============================================================================
|
||||||
|
if gh auth status &>/dev/null; then
|
||||||
|
log_success "GitHub already authenticated"
|
||||||
|
else
|
||||||
|
log_info "GitHub authentication required..."
|
||||||
|
|
||||||
|
if [[ -n "${GITHUB_TOKEN:-}" ]]; then
|
||||||
|
echo "$GITHUB_TOKEN" | gh auth login --with-token
|
||||||
|
log_success "GitHub authenticated via token"
|
||||||
|
else
|
||||||
|
log_warn "Run 'gh auth login' to authenticate with GitHub"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 9: SSH Key Setup
|
||||||
|
# ============================================================================
|
||||||
|
if [[ -f ~/.ssh/id_ed25519 ]]; then
|
||||||
|
log_success "SSH key already exists"
|
||||||
|
else
|
||||||
|
log_info "Generating SSH key..."
|
||||||
|
|
||||||
|
read -p "Enter email for SSH key: " EMAIL
|
||||||
|
ssh-keygen -t ed25519 -C "$EMAIL" -f ~/.ssh/id_ed25519 -N ""
|
||||||
|
|
||||||
|
eval "$(ssh-agent -s)"
|
||||||
|
ssh-add ~/.ssh/id_ed25519
|
||||||
|
|
||||||
|
log_success "SSH key generated"
|
||||||
|
log_warn "Add this key to GitHub: https://github.com/settings/keys"
|
||||||
|
echo ""
|
||||||
|
cat ~/.ssh/id_ed25519.pub
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Step 10: Create workspace directory
|
||||||
|
# ============================================================================
|
||||||
|
WORKSPACE="$HOME/Code"
|
||||||
|
|
||||||
|
if [[ -d "$WORKSPACE" ]]; then
|
||||||
|
log_success "Workspace directory exists: $WORKSPACE"
|
||||||
|
else
|
||||||
|
log_info "Creating workspace directory..."
|
||||||
|
mkdir -p "$WORKSPACE"
|
||||||
|
log_success "Created: $WORKSPACE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Summary
|
||||||
|
# ============================================================================
|
||||||
|
echo ""
|
||||||
|
echo "============================================================"
|
||||||
|
echo -e "${GREEN}Setup complete!${NC}"
|
||||||
|
echo "============================================================"
|
||||||
|
echo ""
|
||||||
|
echo "Installed:"
|
||||||
|
echo " - Go $(go version 2>/dev/null | grep -oP 'go\d+\.\d+\.\d+' || echo 'not in path yet')"
|
||||||
|
echo " - Node.js $(node -v 2>/dev/null || echo 'not in path yet')"
|
||||||
|
echo " - GitHub CLI $(gh --version 2>/dev/null | head -1 || echo 'installed')"
|
||||||
|
echo " - core CLI $(core --version 2>/dev/null || echo 'installed')"
|
||||||
|
|
||||||
|
if [[ -z "${SKIP_GUI:-}" ]]; then
|
||||||
|
echo " - core-ide (GUI mode)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${SKIP_CLAUDE:-}" ]]; then
|
||||||
|
echo " - Claude Code CLI"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Restart your terminal (or run: source ~/.bashrc)"
|
||||||
|
echo " 2. Run 'gh auth login' if not already authenticated"
|
||||||
|
|
||||||
|
if [[ ! -f ~/.ssh/id_ed25519.pub ]] || ! gh auth status &>/dev/null; then
|
||||||
|
echo " 3. Add your SSH key to GitHub"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "To start developing:"
|
||||||
|
echo " cd ~/Code"
|
||||||
|
echo " gh repo clone host-uk/core"
|
||||||
|
echo " cd core && core doctor"
|
||||||
|
echo ""
|
||||||
Loading…
Add table
Reference in a new issue