commit 32fd43ed18bdcb871b91be012d1db56cb8003dfe Author: Hammer Date: Thu Jan 29 18:48:00 2026 +0000 Initial infrastructure setup: Gitea + PostgreSQL + Actions Runner diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..dda3170 --- /dev/null +++ b/.env.example @@ -0,0 +1,21 @@ +# Gitea Infrastructure Environment Variables +# Copy to .env and fill in values + +# Domain +GITEA_DOMAIN=git.infra.donovankelly.xyz + +# PostgreSQL +POSTGRES_USER=gitea +POSTGRES_PASSWORD=CHANGE_ME_generate_with_openssl_rand_hex_16 +POSTGRES_DB=gitea + +# Gitea Secrets (preserve from old instance's app.ini) +INTERNAL_TOKEN= +LFS_JWT_SECRET= +OAUTH2_JWT_SECRET= +SECRET_KEY= + +# Gitea Actions Runner +# Generate after Gitea is running: +# docker exec gitea gitea actions generate-runner-token +RUNNER_REGISTRATION_TOKEN=REPLACE_ME_AFTER_FIRST_DEPLOY diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..925dddd --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,249 @@ +# Gitea Migration: SQLite → PostgreSQL + +## Overview + +Migrating Gitea from: +- **Old**: `git.infra.nkode.tech` (SQLite, Dokploy on 191.101.0.153) +- **New**: `git.infra.donovankelly.xyz` (PostgreSQL, Dokploy on 191.101.0.153) + +This is a **destructive migration** — back up everything before proceeding. + +--- + +## Prerequisites + +- [ ] DNS: Point `git.infra.donovankelly.xyz` → `191.101.0.153` +- [ ] Old Gitea export at `/home/clawdbot/clawd/gitea-export/` on the VPS +- [ ] Docker and docker-compose available on the Dokploy server +- [ ] The new compose service deployed via Dokploy (but NOT yet migrated) + +--- + +## Step-by-Step Migration + +### Step 1: DNS Setup + +Add an A record: +``` +git.infra.donovankelly.xyz → 191.101.0.153 +``` + +Wait for propagation (check with `dig git.infra.donovankelly.xyz`). + +### Step 2: Stop the Old Gitea Instance + +In Dokploy, stop the old Gitea application (`nkode-giteasqlite`). + +```bash +# Or via CLI on 191.101.0.153: +docker stop +``` + +### Step 3: Deploy the New Stack (Fresh, No Data Yet) + +Deploy the compose service from Dokploy. This will start: +- PostgreSQL 16 (empty database) +- Gitea (fresh install, connected to Postgres) +- Act Runner (will fail until token is set — that's OK) + +Wait for Gitea to be healthy: +```bash +docker logs -f gitea +# Look for "Starting new Web server" +``` + +### Step 4: Stop Gitea (Keep Postgres Running) + +```bash +docker stop gitea +``` + +We need Gitea stopped to safely import data. + +### Step 5: Migrate the SQLite Database to PostgreSQL + +**Option A: Using `gitea dump` and restore (Recommended)** + +The export already has `gitea-dump.zip`. We'll use Gitea's built-in migration: + +```bash +# Copy the SQLite database into the Gitea container's volume +docker cp /home/clawdbot/clawd/gitea-export/gitea.db gitea:/data/gitea/gitea.db + +# Start gitea temporarily to run the migration +docker start gitea + +# Run the database migration from SQLite to Postgres +docker exec -u git gitea gitea doctor convert --type sqlite --database /data/gitea/gitea.db + +# If `doctor convert` isn't available in your version, use dump/restore: +# First, generate a SQL dump from SQLite using the gitea binary: +docker exec -u git gitea gitea dump --type postgres --database /data/gitea/gitea.db --file /tmp/gitea-pg-dump.sql + +# Then import it into Postgres: +docker exec -i gitea-postgres psql -U gitea -d gitea < /tmp/gitea-pg-dump.sql +``` + +**Option B: Using `gitea migrate` on fresh start** + +If the above doesn't work cleanly, use pgloader to convert SQLite → Postgres directly: + +```bash +# Install pgloader (on the host or in a container) +docker run --rm --network gitea-net \ + -v /home/clawdbot/clawd/gitea-export/gitea.db:/data/gitea.db \ + dimitri/pgloader:latest \ + pgloader /data/gitea.db \ + postgresql://gitea:YOUR_POSTGRES_PASSWORD@postgres:5432/gitea +``` + +**Option C: Manual approach using Gitea's dump file** + +```bash +# Extract the dump +cd /tmp +unzip /home/clawdbot/clawd/gitea-export/gitea-dump.zip + +# The dump contains gitea-db.sql (SQLite format) +# Convert using pgloader or sqlite3 + sed for Postgres compatibility +``` + +**Recommended approach: Option A with `doctor convert`** + +### Step 6: Copy Repository Data and LFS + +The git repositories, LFS objects, avatars, and other data files need to be copied into the new volume: + +```bash +# Find the new gitea-data volume mount point +VOLUME_PATH=$(docker volume inspect gitea-data --format '{{ .Mountpoint }}') + +# Stop gitea first +docker stop gitea + +# Copy the data directory contents +sudo cp -a /home/clawdbot/clawd/gitea-export/data/git/* ${VOLUME_PATH}/git/ +sudo cp -a /home/clawdbot/clawd/gitea-export/data/gitea/avatars ${VOLUME_PATH}/gitea/ +sudo cp -a /home/clawdbot/clawd/gitea-export/data/gitea/repo-avatars ${VOLUME_PATH}/gitea/ +sudo cp -a /home/clawdbot/clawd/gitea-export/data/gitea/attachments ${VOLUME_PATH}/gitea/ +sudo cp -a /home/clawdbot/clawd/gitea-export/data/gitea/uploads ${VOLUME_PATH}/gitea/ 2>/dev/null || true +sudo cp -a /home/clawdbot/clawd/gitea-export/data/ssh/* ${VOLUME_PATH}/ssh/ 2>/dev/null || true + +# Fix ownership (Gitea runs as UID 1000 / git user) +sudo chown -R 1000:1000 ${VOLUME_PATH}/ + +# Start gitea again +docker start gitea +``` + +### Step 7: Verify the Migration + +```bash +# Check Gitea logs for errors +docker logs gitea --tail 100 + +# Test the web UI +curl -s https://git.infra.donovankelly.xyz/api/v1/version + +# Log in with your existing credentials +# Check that repos, users, orgs, webhooks are all present +``` + +**Checklist:** +- [ ] Can log in with existing user accounts +- [ ] All repositories are visible and cloneable +- [ ] LFS objects are accessible +- [ ] Webhooks are configured +- [ ] Organizations and teams are intact +- [ ] Avatars display correctly +- [ ] SSH keys work (`ssh -T git@git.infra.donovankelly.xyz -p 22222`) + +### Step 8: Set Up Gitea Actions Runner + +```bash +# Generate a runner registration token +docker exec -u git gitea gitea actions generate-runner-token + +# Copy the token output, then update .env: +# RUNNER_REGISTRATION_TOKEN= + +# Restart the runner +docker restart gitea-act-runner + +# Verify the runner is connected +docker logs gitea-act-runner +``` + +You can also register the runner via the Gitea web UI: +1. Go to **Site Administration** → **Runners** +2. Click **Create new runner** +3. Copy the token + +### Step 9: Configure Dokploy Domain / SSL + +In Dokploy, configure the compose service's domain: +1. Go to the "Infrastructure" project → compose service +2. Add domain `git.infra.donovankelly.xyz` pointing to the gitea service on port 3000 +3. Enable HTTPS (Let's Encrypt) + +### Step 10: Update Git Remotes + +On all machines that had the old remote: +```bash +git remote set-url origin https://git.infra.donovankelly.xyz//.git +# or for SSH: +git remote set-url origin ssh://git@git.infra.donovankelly.xyz:22222//.git +``` + +### Step 11: Clean Up + +- [ ] Remove the old Gitea application from Dokploy +- [ ] Remove old Docker volumes +- [ ] Update any CI/CD webhooks pointing to the old domain +- [ ] Update any OAuth apps using the old domain +- [ ] Remove DNS record for `git.infra.nkode.tech` (optional, keep for redirect) + +--- + +## Rollback Plan + +If migration fails: +1. Stop the new stack: `docker compose down` +2. Restart the old Gitea container in Dokploy +3. The old data is untouched in `/home/clawdbot/clawd/gitea-export/` + +--- + +## Architecture + +``` +┌─────────────────────────────────────┐ +│ Dokploy (Traefik) │ +│ git.infra.donovankelly.xyz:443 │ +│ ↓ :3000 │ +│ ┌─────────────────────────────┐ │ +│ │ Gitea 1.24 │ │ +│ │ (Actions enabled) │ │ +│ │ SSH :222 → external :22222 │ │ +│ └──────────┬──────────────────┘ │ +│ │ │ +│ ┌──────────▼──────────────────┐ │ +│ │ PostgreSQL 16 │ │ +│ │ (gitea database) │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ Act Runner │ │ +│ │ (CI/CD workflows) │ │ +│ └─────────────────────────────┘ │ +└─────────────────────────────────────┘ +``` + +## Files + +| File | Purpose | +|------|---------| +| `docker-compose.yml` | Service definitions | +| `.env` | Secret values (DO NOT commit) | +| `.env.example` | Template for secrets | +| `MIGRATION.md` | This document | diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..36457e3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,127 @@ +version: "3.8" + +services: + gitea: + image: docker.gitea.com/gitea:1.24 + container_name: gitea + environment: + - USER_UID=1000 + - USER_GID=1000 + - GITEA_CUSTOM=/data/gitea + # Database + - GITEA__database__DB_TYPE=postgres + - GITEA__database__HOST=postgres:5432 + - GITEA__database__NAME=${POSTGRES_DB} + - GITEA__database__USER=${POSTGRES_USER} + - GITEA__database__PASSWD=${POSTGRES_PASSWORD} + - GITEA__database__SSL_MODE=disable + # Server + - GITEA__server__DOMAIN=${GITEA_DOMAIN} + - GITEA__server__SSH_DOMAIN=${GITEA_DOMAIN} + - GITEA__server__ROOT_URL=https://${GITEA_DOMAIN}/ + - GITEA__server__HTTP_PORT=3000 + - GITEA__server__SSH_PORT=22222 + - GITEA__server__SSH_LISTEN_PORT=222 + - GITEA__server__START_SSH_SERVER=true + - GITEA__server__DISABLE_SSH=false + - GITEA__server__LFS_START_SERVER=true + - GITEA__server__LFS_JWT_SECRET=${LFS_JWT_SECRET} + - GITEA__server__OFFLINE_MODE=true + - GITEA__server__APP_DATA_PATH=/data/gitea + # Security + - GITEA__security__INSTALL_LOCK=true + - GITEA__security__SECRET_KEY=${SECRET_KEY} + - GITEA__security__INTERNAL_TOKEN=${INTERNAL_TOKEN} + - GITEA__security__REVERSE_PROXY_LIMIT=1 + - GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES=* + - GITEA__security__PASSWORD_HASH_ALGO=pbkdf2 + # Service + - GITEA__service__DISABLE_REGISTRATION=true + - GITEA__service__REQUIRE_SIGNIN_VIEW=false + - GITEA__service__ENABLE_OPENID_SIGNUP=false + - GITEA__service__ENABLE_OPENID_SIGNIN=false + # OAuth2 + - GITEA__oauth2__JWT_SECRET=${OAUTH2_JWT_SECRET} + # Actions + - GITEA__actions__ENABLED=true + # Repository + - GITEA__repository__ROOT=/data/git/repositories + - GITEA__repository__ENABLE_GO_GET=true + # LFS + - GITEA__lfs__PATH=/data/git/lfs + # Session + - GITEA__session__PROVIDER=file + - GITEA__session__PROVIDER_CONFIG=/data/gitea/sessions + # Log + - GITEA__log__MODE=console + - GITEA__log__LEVEL=info + # Mailer + - GITEA__mailer__ENABLED=false + # Cron + - GITEA__cron.update_checker__ENABLED=false + restart: unless-stopped + volumes: + - gitea-data:/data + ports: + - "3000:3000" + - "22222:222" + depends_on: + postgres: + condition: service_healthy + networks: + - gitea-net + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/v1/version"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s + + postgres: + image: postgres:16-alpine + container_name: gitea-postgres + environment: + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + restart: unless-stopped + volumes: + - postgres-data:/var/lib/postgresql/data + networks: + - gitea-net + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + + act-runner: + image: gitea/act_runner:latest + container_name: gitea-act-runner + environment: + - GITEA_INSTANCE_URL=http://gitea:3000 + - GITEA_RUNNER_REGISTRATION_TOKEN=${RUNNER_REGISTRATION_TOKEN} + - GITEA_RUNNER_NAME=default-runner + - GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://ubuntu:22.04 + restart: unless-stopped + volumes: + - runner-data:/data + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + gitea: + condition: service_healthy + networks: + - gitea-net + +volumes: + gitea-data: + driver: local + postgres-data: + driver: local + runner-data: + driver: local + +networks: + gitea-net: + driver: bridge