diff --git a/README.md b/README.md index 739c252..7513c78 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,15 @@ docker compose --profile production up -d # Using Bun bun run setup && bun run dev -# Using LXC on Proxmox +# Using LXC Containers +# For Proxmox VE (online) curl -fsSL https://raw.githubusercontent.com/arunavo4/gitea-mirror/main/scripts/gitea-mirror-lxc-installer.sh | bash + +# For local testing (offline-friendly) +sudo LOCAL_REPO_DIR=~/Development/gitea-mirror ./scripts/gitea-mirror-lxc-local.sh ```` -See the [LXC Container Deployment Guide](scripts/README-lxc.md). +See the [LXC Container Deployment Guide](docs/LXC.md).

Dashboard @@ -163,23 +167,39 @@ docker compose --profile production up -d See [Docker build documentation](./scripts/README-docker.md) for more details. -##### Using LXC Containers (for Proxmox Homelab Setups) +##### Using LXC Containers -Gitea Mirror can be deployed on Proxmox LXC containers, which is ideal for homelab setups: +Gitea Mirror offers two deployment options for LXC containers: + +**1. Proxmox VE (online, recommended for production)** ```bash -# One-command installation on an Ubuntu 22.04 LXC container +# One-command installation on Proxmox VE +# Optional env overrides: CTID HOSTNAME STORAGE DISK_SIZE CORES MEMORY BRIDGE IP_CONF curl -fsSL https://raw.githubusercontent.com/arunavo4/gitea-mirror/main/scripts/gitea-mirror-lxc-installer.sh | bash ``` -The installer script: -- Downloads the Gitea Mirror repository -- Installs all dependencies including Bun -- Builds the application -- Sets up a systemd service -- Starts the application +**2. Local testing (offline-friendly, works on developer laptops)** -See the [LXC Container Deployment Guide](scripts/README-lxc.md) for detailed instructions. +```bash +# Download the script +curl -fsSL https://raw.githubusercontent.com/arunavo4/gitea-mirror/main/scripts/gitea-mirror-lxc-local.sh -o gitea-mirror-lxc-local.sh +chmod +x gitea-mirror-lxc-local.sh + +# Run with your local repo directory +sudo LOCAL_REPO_DIR=~/Development/gitea-mirror ./gitea-mirror-lxc-local.sh +``` + +Both scripts: +- Set up a privileged Ubuntu 22.04 LXC container +- Install Bun runtime environment +- Build the application +- Configure a systemd service +- Start the service automatically + +The application includes a health check endpoint at `/api/health` for monitoring. + +See the [LXC Container Deployment Guide](docs/LXC.md) for detailed instructions. ##### Building Your Own Image @@ -379,7 +399,7 @@ docker compose -f docker-compose.dev.yml up -d - **Backend**: Bun - **Database**: SQLite (handles both data storage and event notifications) - **API Integration**: GitHub API (Octokit), Gitea API -- **Deployment Options**: Docker containers, Proxmox LXC containers +- **Deployment Options**: Docker containers, LXC containers (Proxmox VE and local testing) ## Contributing diff --git a/docs/LXC.md b/docs/LXC.md new file mode 100644 index 0000000..482d89b --- /dev/null +++ b/docs/LXC.md @@ -0,0 +1,131 @@ +# LXC Container Deployment Guide + +## Overview +Run **Gitea Mirror** in an isolated LXC container, either: + +1. **Online, on a Proxmox VE host** – script pulls everything from GitHub +2. **Offline / LAN-only, on a developer laptop** – script pushes your local checkout + Bun ZIP + +--- + +## 1. Proxmox VE (online, recommended for prod) + +### Prerequisites +* Proxmox VE node with the default `vmbr0` bridge +* Root shell on the node +* Ubuntu 22.04 LXC template present (`pveam update && pveam download ...`) + +### One-command install + +```bash +# optional env overrides: CTID HOSTNAME STORAGE DISK_SIZE CORES MEMORY BRIDGE IP_CONF +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/arunavo4/gitea-mirror/main/scripts/gitea-mirror-lxc-installer.sh)" +``` + +What it does: + +* Creates **privileged** CT `$CTID` with nesting enabled +* Installs curl / git / Bun (official installer) +* Clones & builds `arunavo4/gitea-mirror` +* Writes a root-run systemd service and starts it +* Prints the container IP + random `JWT_SECRET` + +Browse to: + +``` +http://:4321 +``` + +--- + +## 2. Local testing (LXD on a workstation, works offline) + +### Prerequisites + +* `lxd` installed (`sudo apt install lxd`; `lxd init --auto`) +* Your repo cloned locally – e.g. `~/Development/gitea-mirror` +* Bun ZIP downloaded once: + `https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64.zip` + +### Offline installer script + +```bash +git clone https://github.com/arunavo4/gitea-mirror.git # if not already +curl -fsSL https://raw.githubusercontent.com/arunavo4/gitea-mirror/main/scripts/gitea-mirror-lxc-local.sh -o gitea-mirror-lxc-local.sh +chmod +x gitea-mirror-lxc-local.sh + +sudo LOCAL_REPO_DIR=~/Development/gitea-mirror \ + ./gitea-mirror-lxc-local.sh +``` + +What it does: + +* Launches privileged LXC `gitea-test` (`lxc launch ubuntu:22.04 ...`) +* Pushes **Bun ZIP** + tarred **local repo** into `/opt` +* Unpacks, builds, initializes DB +* Symlinks both `bun` and `bunx` → `/usr/local/bin` +* Creates a root systemd unit and starts it + +Access from host: + +``` +http://$(lxc exec gitea-test -- hostname -I | awk '{print $1}'):4321 +``` + +(Optional) forward to host localhost: + +```bash +sudo lxc config device add gitea-test mirror proxy \ + listen=tcp:0.0.0.0:4321 connect=tcp:127.0.0.1:4321 +``` + +--- + +## Health-check endpoint + +Gitea Mirror includes a built-in health check endpoint at `/api/health` that provides: + +- System status and uptime +- Database connectivity check +- Memory usage statistics +- Environment information + +You can use this endpoint for monitoring your deployment: + +```bash +# Basic check (returns 200 OK if healthy) +curl -I http://:4321/api/health + +# Detailed health information (JSON) +curl http://:4321/api/health +``` + +--- + +## Troubleshooting + +| Check | Command | +| -------------- | ----------------------------------------------------- | +| Service status | `systemctl status gitea-mirror` | +| Live logs | `journalctl -u gitea-mirror -f` | +| Verify Bun | `bun --version && bunx --version` | +| DB perms | `chown -R root:root /opt/gitea-mirror/data` (Proxmox) | + +--- + +## Connecting LXC and Docker Containers + +If you need your LXC container to communicate with Docker containers: + +1. On your host machine, create a bridge network: + ```bash + docker network create gitea-network + ``` + +2. Find the bridge interface created by Docker: + ```bash + ip a | grep docker + # Look for something like docker0 or br-xxxxxxxx + ``` + +3. In Proxmox, edit the LXC container's network configuration to use this bridge. diff --git a/scripts/gitea-mirror-lxc-installer.sh b/scripts/gitea-mirror-lxc-installer.sh index 4cf2d23..44568fa 100755 --- a/scripts/gitea-mirror-lxc-installer.sh +++ b/scripts/gitea-mirror-lxc-installer.sh @@ -1,266 +1,97 @@ -#!/bin/bash -# Gitea Mirror LXC Container Installer -# This is a self-contained script to install Gitea Mirror in an LXC container -# Usage: curl -fsSL https://raw.githubusercontent.com/arunavo4/gitea-mirror/main/scripts/gitea-mirror-lxc-installer.sh | bash +#!/usr/bin/env bash +# gitea-mirror-proxmox.sh +# Fully online installer for a Proxmox LXC guest running Gitea Mirror + Bun. -set -e +set -euo pipefail + +# ────── adjustable defaults ────────────────────────────────────────────── +CTID=${CTID:-106} # container ID +HOSTNAME=${HOSTNAME:-gitea-mirror} +STORAGE=${STORAGE:-local-lvm} # where rootfs lives +DISK_SIZE=${DISK_SIZE:-8G} +CORES=${CORES:-2} +MEMORY=${MEMORY:-2048} # MiB +BRIDGE=${BRIDGE:-vmbr0} +IP_CONF=${IP_CONF:-dhcp} # or "192.168.1.240/24,gw=192.168.1.1" -# Configuration variables - change these as needed -INSTALL_DIR="/opt/gitea-mirror" -REPO_URL="https://github.com/arunavo4/gitea-mirror.git" -SERVICE_USER="gitea-mirror" PORT=4321 +JWT_SECRET=$(openssl rand -hex 32) -# Color codes for better readability -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color +REPO="https://github.com/arunavo4/gitea-mirror.git" +# ───────────────────────────────────────────────────────────────────────── -# Print banner -echo -e "${BLUE}" -echo "╔════════════════════════════════════════════════════════════╗" -echo "║ ║" -echo "║ Gitea Mirror LXC Container Installer ║" -echo "║ ║" -echo "╚════════════════════════════════════════════════════════════╝" -echo -e "${NC}" +TEMPLATE='ubuntu-22.04-standard_22.04-1_amd64.tar.zst' +TEMPLATE_PATH="/var/lib/vz/template/cache/${TEMPLATE}" -# Ensure script is run as root -if [ "$(id -u)" -ne 0 ]; then - echo -e "${RED}This script must be run as root${NC}" >&2 - exit 1 +echo "▶️ Ensuring template exists…" +if [[ ! -f $TEMPLATE_PATH ]]; then + pveam update >/dev/null + pveam download "$STORAGE" "$TEMPLATE" fi -echo -e "${GREEN}Starting Gitea Mirror installation...${NC}" - -# Check if we're in an LXC container -if [ -d /proc/vz ] && [ ! -d /proc/bc ]; then - echo -e "${YELLOW}Running in an OpenVZ container. Some features may not work.${NC}" -elif [ -f /proc/1/environ ] && grep -q container=lxc /proc/1/environ; then - echo -e "${GREEN}Running in an LXC container. Good!${NC}" -else - echo -e "${YELLOW}Not running in a container. This script is designed for LXC containers.${NC}" - read -p "Continue anyway? (y/n) " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo -e "${RED}Installation aborted.${NC}" - exit 1 - fi +echo "▶️ Creating container $CTID (if missing)…" +if ! pct status "$CTID" &>/dev/null; then + pct create "$CTID" "$TEMPLATE_PATH" \ + --rootfs "$STORAGE:$DISK_SIZE" \ + --hostname "$HOSTNAME" \ + --cores "$CORES" --memory "$MEMORY" \ + --net0 "name=eth0,bridge=$BRIDGE,ip=$IP_CONF" \ + --features nesting=1 \ + --unprivileged 0 fi -# Install dependencies -echo -e "${BLUE}Step 1/7: Installing dependencies...${NC}" -apt update -apt install -y curl git sqlite3 build-essential openssl +pct start "$CTID" -# Create service user -echo -e "${BLUE}Step 2/7: Creating service user...${NC}" -if id "$SERVICE_USER" &>/dev/null; then - echo -e "${YELLOW}User $SERVICE_USER already exists${NC}" -else - useradd -m -s /bin/bash "$SERVICE_USER" - echo -e "${GREEN}Created user $SERVICE_USER${NC}" -fi +echo "▶️ Installing base packages inside CT $CTID…" +pct exec "$CTID" -- bash -c 'apt update && apt install -y curl git build-essential openssl sqlite3 unzip' -# Install Bun -echo -e "${BLUE}Step 3/7: Installing Bun runtime...${NC}" -if command -v bun >/dev/null 2>&1; then - echo -e "${YELLOW}Bun is already installed${NC}" +echo "▶️ Installing Bun runtime…" +pct exec "$CTID" -- bash -c ' + export BUN_INSTALL=/opt/bun + curl -fsSL https://bun.sh/install | bash -s -- --yes + ln -sf /opt/bun/bin/bun /usr/local/bin/bun + ln -sf /opt/bun/bin/bun /usr/local/bin/bunx bun --version -else - echo -e "${GREEN}Installing Bun...${NC}" - # Install Bun globally to make it accessible to all users - curl -fsSL https://bun.sh/install | bash - export BUN_INSTALL=${BUN_INSTALL:-"/root/.bun"} - export PATH="$BUN_INSTALL/bin:$PATH" +' - # Make Bun accessible to all users by creating a symlink in /usr/local/bin - echo -e "${GREEN}Making Bun accessible to all users...${NC}" - - # Check if /usr/local/bin is writable - if [ ! -w /usr/local/bin ]; then - echo -e "${RED}Error: /usr/local/bin is not writable. Please run the script as root or with sufficient permissions.${NC}" - exit 1 - fi - - ln -sf "$BUN_INSTALL/bin/bun" /usr/local/bin/bun - - if [ $? -ne 0 ]; then - echo -e "${RED}Error: Failed to create symlink for Bun in /usr/local/bin.${NC}" - exit 1 - fi - - echo -e "${GREEN}Bun installed successfully${NC}" - bun --version -fi - -# Clone repository -echo -e "${BLUE}Step 4/7: Downloading Gitea Mirror...${NC}" -if [ -d "$INSTALL_DIR" ]; then - echo -e "${YELLOW}Directory $INSTALL_DIR already exists${NC}" - read -p "Update existing installation? (y/n) " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]]; then - cd "$INSTALL_DIR" - git pull - echo -e "${GREEN}Repository updated${NC}" - else - echo -e "${YELLOW}Using existing installation${NC}" - fi -else - echo -e "${GREEN}Cloning repository...${NC}" - git clone "$REPO_URL" "$INSTALL_DIR" - echo -e "${GREEN}Repository cloned to $INSTALL_DIR${NC}" -fi - -# Set up application -echo -e "${BLUE}Step 5/7: Setting up application...${NC}" -cd "$INSTALL_DIR" - -# Create data directory with proper permissions -mkdir -p data -chown -R "$SERVICE_USER:$SERVICE_USER" data - -# Ensure the application directory has the right permissions -echo -e "${GREEN}Setting correct permissions for application directory...${NC}" -chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" -chmod -R 755 "$INSTALL_DIR" - -# Install dependencies and build -echo -e "${GREEN}Installing dependencies and building application...${NC}" -bun install -bun run build - -# Initialize database if it doesn't exist -echo -e "${GREEN}Initializing database...${NC}" -if [ ! -f "data/gitea-mirror.db" ]; then +echo "▶️ Cloning & building Gitea Mirror…" +pct exec "$CTID" -- bash -c " + git clone --depth=1 '$REPO' /opt/gitea-mirror || (cd /opt/gitea-mirror && git pull) + cd /opt/gitea-mirror + bun install + bun run build bun run manage-db init - chown "$SERVICE_USER:$SERVICE_USER" data/gitea-mirror.db -fi - -# Generate a random JWT secret if not provided -JWT_SECRET=${JWT_SECRET:-$(openssl rand -hex 32)} - -# Create systemd service -echo -e "${BLUE}Step 6/7: Creating systemd service...${NC}" - -# Store Bun path in a variable for better maintainability -# Use the global Bun path to ensure it's accessible to the service user -BUN_PATH="/usr/local/bin/bun" -echo -e "${GREEN}Using Bun from: $BUN_PATH${NC}" - -# Ensure the Bun executable is accessible to the service user -if [ ! -f "$BUN_PATH" ]; then - echo -e "${YELLOW}Bun not found at $BUN_PATH, creating symlink...${NC}" - - # Check if /usr/local/bin is writable - if [ ! -w "$(dirname "$BUN_PATH")" ]; then - echo -e "${RED}Error: $(dirname "$BUN_PATH") is not writable. Please run the script as root or with sufficient permissions.${NC}" - exit 1 - fi - - ln -sf "$(command -v bun)" "$BUN_PATH" - - if [ $? -ne 0 ]; then - echo -e "${RED}Error: Failed to create symlink for Bun in $(dirname "$BUN_PATH").${NC}" - exit 1 - fi -fi - -# Make sure the Bun executable has the right permissions -if [ -f "$BUN_PATH" ]; then - chmod 755 "$BUN_PATH" -fi +" +echo "▶️ Creating systemd service…" +pct exec "$CTID" -- bash -c " cat >/etc/systemd/system/gitea-mirror.service </dev/null; then - echo -e "${GREEN}Bun is accessible to $SERVICE_USER user${NC}" -else - echo -e "${YELLOW}Warning: $SERVICE_USER cannot access Bun. Fixing permissions...${NC}" - # Make sure the Bun binary and its directory are accessible - BUN_DIR="$(dirname "$BUN_PATH")" - if [ -d "$BUN_DIR" ]; then - chmod 755 "$BUN_DIR" - fi - - if [ -f "$BUN_PATH" ]; then - chmod 755 "$BUN_PATH" - fi - - # Check again - if su - "$SERVICE_USER" -c "$BUN_PATH --version" &>/dev/null; then - echo -e "${GREEN}Fixed: Bun is now accessible to $SERVICE_USER user${NC}" - else - echo -e "${RED}Warning: $SERVICE_USER still cannot access Bun. Service may fail to start.${NC}" - fi -fi - -# Start service -echo -e "${BLUE}Step 7/7: Starting service...${NC}" systemctl daemon-reload -systemctl enable gitea-mirror.service -systemctl start gitea-mirror.service +systemctl enable gitea-mirror +systemctl restart gitea-mirror +" -# Check if service started successfully -if systemctl is-active --quiet gitea-mirror.service; then - echo -e "${GREEN}Gitea Mirror service started successfully!${NC}" -else - echo -e "${RED}Failed to start Gitea Mirror service. Check logs with: journalctl -u gitea-mirror${NC}" - exit 1 -fi +echo -e "\n🔍 Service status:" +pct exec "$CTID" -- systemctl status gitea-mirror --no-pager | head -n15 -# Get IP address -IP_ADDRESS=$(hostname -I | awk '{print $1}') - -# Print success message -echo -e "${GREEN}" -echo "╔════════════════════════════════════════════════════════════╗" -echo "║ ║" -echo "║ Gitea Mirror Installation Complete ║" -echo "║ ║" -echo "╚════════════════════════════════════════════════════════════╝" -echo -e "${NC}" -echo -e "${GREEN}Gitea Mirror is now running at: http://$IP_ADDRESS:$PORT${NC}" -echo -echo -e "${YELLOW}Important security information:${NC}" -echo -e "JWT_SECRET: ${JWT_SECRET}" -echo -e "${YELLOW}Please save this JWT_SECRET in a secure location.${NC}" -echo -echo -e "${BLUE}To check service status:${NC} systemctl status gitea-mirror" -echo -e "${BLUE}To view logs:${NC} journalctl -u gitea-mirror -f" -echo -e "${BLUE}Data directory:${NC} $INSTALL_DIR/data" -echo -echo -e "${YELLOW}Troubleshooting:${NC}" -echo -e "If you encounter permission issues with Bun, try the following:" -echo -e "1. Check if Bun is accessible: ${BLUE}su - $SERVICE_USER -c \"$BUN_PATH --version\"${NC}" -echo -e "2. Fix permissions: ${BLUE}chmod 755 $BUN_PATH${NC}" -echo -e "3. Create a symlink: ${BLUE}ln -sf \$(command -v bun) /usr/local/bin/bun${NC}" -echo -e "4. Restart the service: ${BLUE}systemctl restart gitea-mirror${NC}" -echo -echo -e "${GREEN}Thank you for installing Gitea Mirror!${NC}" +GUEST_IP=$(pct exec "$CTID" -- hostname -I | awk '{print $1}') +echo -e "\n🌐 Browse to: http://$GUEST_IP:$PORT\n" +echo "🗝️ JWT_SECRET = $JWT_SECRET" +echo -e "\n✅ Done – Gitea Mirror is running in CT $CTID." diff --git a/scripts/gitea-mirror-lxc-local.sh b/scripts/gitea-mirror-lxc-local.sh new file mode 100755 index 0000000..339b62a --- /dev/null +++ b/scripts/gitea-mirror-lxc-local.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# gitea-mirror-lxc-local.sh (offline, local repo, verbose) + +set -euo pipefail + +CONTAINER="gitea-test" +IMAGE="ubuntu:22.04" +INSTALL_DIR="/opt/gitea-mirror" +PORT=4321 +JWT_SECRET="$(openssl rand -hex 32)" + +BUN_ZIP="/tmp/bun-linux-x64.zip" +BUN_URL="https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64.zip" + +LOCAL_REPO_DIR="${LOCAL_REPO_DIR:-./gitea-mirror}" +REPO_TAR="/tmp/gitea-mirror-local.tar.gz" + +need() { command -v "$1" >/dev/null || { echo "Missing $1"; exit 1; }; } +need curl; need lxc; need tar; need unzip + +# ── build host artefacts ──────────────────────────────────────────────── +[[ -d $LOCAL_REPO_DIR ]] || { echo "❌ LOCAL_REPO_DIR not found"; exit 1; } +[[ -f $LOCAL_REPO_DIR/package.json ]] || { echo "❌ package.json missing"; exit 1; } +[[ -f $BUN_ZIP ]] || curl -L --retry 5 --retry-delay 5 -o "$BUN_ZIP" "$BUN_URL" +tar -czf "$REPO_TAR" -C "$(dirname "$LOCAL_REPO_DIR")" "$(basename "$LOCAL_REPO_DIR")" + +# ── ensure container exists ───────────────────────────────────────────── +lxd init --auto >/dev/null 2>&1 || true +lxc info "$CONTAINER" >/dev/null 2>&1 || lxc launch "$IMAGE" "$CONTAINER" + +echo "🔧 installing base packages…" +sudo lxc exec "$CONTAINER" -- bash -c 'set -ex; apt update; apt install -y unzip tar openssl sqlite3' + +echo "⬆️ pushing artefacts…" +sudo lxc file push "$BUN_ZIP" "$CONTAINER/opt/" +sudo lxc file push "$REPO_TAR" "$CONTAINER/opt/" + +echo "📦 unpacking Bun + repo…" +sudo lxc exec "$CONTAINER" -- bash -ex <<'IN' +cd /opt +# Bun +unzip -oq bun-linux-x64.zip -d bun +BIN=$(find /opt/bun -type f -name bun -perm -111 | head -n1) +ln -sf "$BIN" /usr/local/bin/bun # bun +ln -sf "$BIN" /usr/local/bin/bunx # bunx shim +# Repo +rm -rf /opt/gitea-mirror +mkdir -p /opt/gitea-mirror +tar -xzf gitea-mirror-local.tar.gz --strip-components=1 -C /opt/gitea-mirror +IN + +echo "🏗️ bun install / build…" +sudo lxc exec "$CONTAINER" -- bash -ex <<'IN' +cd /opt/gitea-mirror +bun install +bun run build +bun run manage-db init +IN + +echo "📝 systemd unit…" +sudo lxc exec "$CONTAINER" -- bash -ex </etc/systemd/system/gitea-mirror.service <