feature: Add Gitea docs

This commit is contained in:
Keith Solomon
2025-11-30 21:24:04 -06:00
parent 9291b641da
commit 474a967a24
7 changed files with 684 additions and 0 deletions

223
assets/files/gitea/forge Normal file
View File

@@ -0,0 +1,223 @@
#!/usr/bin/env bash
set -euo pipefail
# ===========================
# SoloForge Toolkit (forge)
# ===========================
#
# Handy commands for managing:
# - Gitea (SoloForge)
# - Gitea Actions runner
# - Backups
#
# Paths: adjust if you move things.
GITEA_DIR="/gitea"
RUNNER_DIR="/gitea/gitea-runner"
BACKUP_DIR="/gitea/backups"
GITEA_COMPOSE="$GITEA_DIR/docker-compose.yml"
RUNNER_COMPOSE="$RUNNER_DIR/docker-compose.yml"
GITEA_CONTAINER_NAME="Gitea"
RUNNER_CONTAINER_NAME="gitea-act-runner"
# Prefer `docker compose`, fall back to `docker-compose` if needed
dc() {
if command -v docker >/dev/null 2>&1; then
if docker compose version >/dev/null 2>&1; then
docker compose "$@"
elif command -v docker-compose >/dev/null 2>&1; then
docker-compose "$@"
else
echo "Error: neither 'docker compose' nor 'docker-compose' found." >&2
exit 1
fi
else
echo "Error: docker not found in PATH." >&2
exit 1
fi
}
usage() {
cat <<EOF
SoloForge toolkit
Usage:
forge status Show status of Gitea and runner containers
forge ps Alias for status
forge gitea-logs Tail logs from Gitea container
forge runner-logs Tail logs from Actions runner container
forge backup Run a Gitea dump and move it into BACKUP_DIR
forge restart-gitea Restart Gitea stack
forge restart-runner Restart Actions runner stack
forge runner-reset Re-register runner with current labels (destroys .runner)
forge diag Quick diagnostic summary
Config:
GITEA_DIR = $GITEA_DIR
RUNNER_DIR = $RUNNER_DIR
BACKUP_DIR = $BACKUP_DIR
EOF
}
ensure_dirs() {
mkdir -p "$BACKUP_DIR"
}
cmd_status() {
echo "== Docker containers =="
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' \
| grep -E "$GITEA_CONTAINER_NAME|$RUNNER_CONTAINER_NAME" || true
echo
echo "== Gitea compose =="
echo " $GITEA_COMPOSE"
echo "== Runner compose =="
echo " $RUNNER_COMPOSE"
}
cmd_gitea_logs() {
echo "Tailing logs for container: $GITEA_CONTAINER_NAME"
docker logs -f "$GITEA_CONTAINER_NAME"
}
cmd_runner_logs() {
echo "Tailing logs for container: $RUNNER_CONTAINER_NAME"
docker logs -f "$RUNNER_CONTAINER_NAME"
}
cmd_backup() {
ensure_dirs
echo "Running Gitea dump inside container: $GITEA_CONTAINER_NAME"
# Gitea dump will write gitea-dump-*.zip under /data which is mounted to $GITEA_DIR/gitea
docker exec -u 1000 "$GITEA_CONTAINER_NAME" gitea dump -c /data/gitea/conf/app.ini
echo "Locating latest dump..."
local dump
dump=$(ls -1t "$GITEA_DIR"/gitea/gitea-dump-*.zip 2>/dev/null | head -n1 || true)
if [[ -z "$dump" ]]; then
echo "Error: no gitea-dump-*.zip found under $GITEA_DIR/gitea" >&2
exit 1
fi
local base
base=$(basename "$dump")
local dest="$BACKUP_DIR/$base"
echo "Moving $dump -> $dest"
mv "$dump" "$dest"
echo "Backup complete: $dest"
}
cmd_restart_gitea() {
echo "Restarting Gitea stack (compose: $GITEA_COMPOSE)"
(cd "$GITEA_DIR" && dc -f "$GITEA_COMPOSE" down)
(cd "$GITEA_DIR" && dc -f "$GITEA_COMPOSE" up -d)
echo "Gitea stack restarted."
}
cmd_restart_runner() {
echo "Restarting runner stack (compose: $RUNNER_COMPOSE)"
(cd "$RUNNER_DIR" && dc -f "$RUNNER_COMPOSE" down || true)
(cd "$RUNNER_DIR" && dc -f "$RUNNER_COMPOSE" up -d)
echo "Runner stack restarted."
}
cmd_runner_reset() {
echo "Resetting runner registration..."
(cd "$RUNNER_DIR" && dc -f "$RUNNER_COMPOSE" down || true)
local runner_state="$RUNNER_DIR/data/.runner"
if [[ -f "$runner_state" ]]; then
echo "Removing $runner_state"
rm -f "$runner_state"
else
echo "No existing .runner file found (nothing to delete)."
fi
echo "Bringing runner stack up (will auto-register with current env)..."
(cd "$RUNNER_DIR" && dc -f "$RUNNER_COMPOSE" up -d)
echo "Runner reset requested. Check runner logs via: forge runner-logs"
}
cmd_diag() {
echo "== SoloForge diagnostic =="
echo
echo "-- Paths --"
echo "GITEA_DIR = $GITEA_DIR"
echo "RUNNER_DIR = $RUNNER_DIR"
echo "BACKUP_DIR = $BACKUP_DIR"
echo
echo "-- Containers --"
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' \
| grep -E "$GITEA_CONTAINER_NAME|$RUNNER_CONTAINER_NAME" || echo " (no Gitea/runner containers found)"
echo
echo "-- Runner .runner file --"
if [[ -f "$RUNNER_DIR/data/.runner" ]]; then
echo "Found: $RUNNER_DIR/data/.runner"
else
echo "No .runner file present (runner may not be registered)."
fi
echo
echo "-- Latest backup --"
if [[ -d "$BACKUP_DIR" ]]; then
local latest
latest=$(ls -1t "$BACKUP_DIR"/gitea-dump-*.zip 2>/dev/null | head -n1 || true)
if [[ -n "$latest" ]]; then
echo " $latest"
else
echo " No backups found in $BACKUP_DIR"
fi
else
echo " Backup dir does not exist: $BACKUP_DIR"
fi
}
# ===========================
# Dispatch
# ===========================
cmd="${1:-help}"
shift || true
case "$cmd" in
status|ps)
cmd_status
;;
gitea-logs)
cmd_gitea_logs
;;
runner-logs)
cmd_runner_logs
;;
backup)
cmd_backup
;;
restart-gitea)
cmd_restart_gitea
;;
restart-runner)
cmd_restart_runner
;;
runner-reset)
cmd_runner_reset
;;
diag)
cmd_diag
;;
help|--help|-h)
usage
;;
*)
echo "Unknown command: $cmd" >&2
echo
usage
exit 1
;;
esac

View File

@@ -0,0 +1,92 @@
#!/usr/bin/env bash
set -euo pipefail
# ---- Config: set these at the top ----
GITEA_BASE="https://git.keithsolomon.net"
GITEA_USER="keith"
GITEA_TOKEN="f4b01fa50f56271f8ba002221fb68404de955077"
REMOTE_NAME="origin" # which remote to use/push
SEARCH_ROOT="${1:-$PWD}" # default to current dir if not provided
# sanity checks
command -v jq >/dev/null 2>&1 || { echo "jq is required. Install with: sudo apt install jq"; exit 1; }
: "${GITEA_BASE:?Need GITEA_BASE}"
: "${GITEA_USER:?Need GITEA_USER}"
: "${GITEA_TOKEN:?Need GITEA_TOKEN}"
SEARCH_ROOT="$(realpath "$SEARCH_ROOT")"
echo "Scanning for git repos under: $SEARCH_ROOT"
echo
# Use -print0 to safely handle spaces/newlines in paths
find "$SEARCH_ROOT" -type d -name ".git" -print0 | while IFS= read -r -d '' gitdir; do
# Absolute repo dir
repo_dir="$(dirname "$gitdir")"
repo_dir="$(realpath "$repo_dir")"
echo "=== $repo_dir ==="
# Run everything for this repo in a subshell so cd doesn't affect the outer loop
(
cd "$repo_dir" || exit 0
# Get origin URL (or skip if none)
if ! url=$(git remote get-url "$REMOTE_NAME" 2>/dev/null); then
echo " -> Skipping (no '$REMOTE_NAME' remote)"
exit 0
fi
# Only migrate repos that point (or pointed) at git.keithsolomon.net
if [[ "$url" != *"git.keithsolomon.net"* ]]; then
echo " -> Skipping (origin is not git.keithsolomon.net)"
exit 0
fi
# Derive repo name: strip .git and take basename
clean_url="${url%.git}"
repo_name="$(basename "$clean_url")"
echo " -> Repo name on Gitea: $repo_name"
# Create repo via Gitea API (idempotent: 201 = created, 409 = already exists)
create_payload=$(jq -n \
--arg name "$repo_name" \
--arg desc "Imported from local clone at $repo_dir" \
'{name: $name, private: true, description: $desc}')
http_status=$(
curl -sS -o /tmp/gitea-create-repo.out \
-w "%{http_code}" \
-X POST "$GITEA_BASE/api/v1/user/repos" \
-H "Content-Type: application/json" \
-H "Authorization: token $GITEA_TOKEN" \
-d "$create_payload" || echo "000"
)
if [[ "$http_status" == "201" ]]; then
echo " -> Created repo on Gitea"
elif [[ "$http_status" == "409" ]]; then
echo " -> Repo already exists on Gitea, continuing"
else
echo " !! Unexpected HTTP status $http_status creating repo, output:"
cat /tmp/gitea-create-repo.out
echo " !! Skipping push for $repo_dir"
echo
exit 0
fi
# Make sure origin URL points at the new Gitea HTTPS remote
new_url="$GITEA_BASE/$GITEA_USER/$repo_name.git"
git remote set-url "$REMOTE_NAME" "$new_url" 2>/dev/null || git remote add "$REMOTE_NAME" "$new_url"
echo " -> Pushing all branches..."
git push --all "$REMOTE_NAME" || echo " !! Failed pushing branches"
echo " -> Pushing tags..."
git push --tags "$REMOTE_NAME" || echo " !! Failed pushing tags"
echo
)
done
echo "Done."

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
GITEA_BASE="https://git.keithsolomon.net"
GITEA_USER="keith" # your username
GITEA_TOKEN="f4b01fa50f56271f8ba002221fb68404de955077"
echo "Fetching repos for user: $GITEA_USER"
repos=$(curl -sS \
-H "Authorization: token $GITEA_TOKEN" \
"$GITEA_BASE/api/v1/users/$GITEA_USER/repos" | jq -r '.[].name')
for repo in $repos; do
echo "Setting $repo → public..."
curl -sS -X PATCH \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d '{"private":false}' \
"$GITEA_BASE/api/v1/repos/$GITEA_USER/$repo" \
>/dev/null
echo "✓ $repo is now public"
done
echo "Done."

View File

@@ -0,0 +1,65 @@
services:
gitea-db:
container_name: Gitea-db
restart: unless-stopped
image: postgres:16
environment:
POSTGRES_USER: gitea
POSTGRES_PASSWORD: <change-me>
POSTGRES_DB: gitea
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gitea"]
interval: 10s
timeout: 5s
retries: 5
networks:
- frontend
volumes:
- /gitea/postgres:/var/lib/postgresql/data
gitea:
container_name: Gitea
restart: unless-stopped
image: gitea/gitea:latest
depends_on:
gitea-db:
condition: service_healthy
environment:
USER_UID: 1000
USER_GID: 1000
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: gitea-db:5432
GITEA__database__NAME: gitea
GITEA__database__USER: gitea
GITEA__database__PASSWD: <change-me>
GITEA__server__DOMAIN: git.keithsolomon.net
GITEA__server__ROOT_URL: https://git.keithsolomon.net/
GITEA__server__SSH_PORT: 222
GITEA__log__MODE: console
volumes:
- /gitea/gitea:/data
networks:
- frontend
ports:
- "3000:3000" # HTTP web UI
- "222:22" # SSH for git over SSH
labels:
- "traefik.enable=true"
- "traefik.docker.network=frontend"
- "traefik.http.routers.gitea.rule=Host(`git.keithsolomon.net`)"
- "traefik.http.services.gitea.loadbalancer.server.port=3000" # Gitea's default web port
networks:
frontend:
external: true
name: frontend

View File

@@ -0,0 +1,20 @@
services:
runner:
container_name: gitea-act-runner
restart: unless-stopped
image: gitea/act_runner:latest
working_dir: /data
environment:
GITEA_INSTANCE_URL: "https://git.keithsolomon.net"
GITEA_RUNNER_REGISTRATION_TOKEN: "vmdpAbiuPoaX8pEmzZkJjUJpIlI4iIIlXGGEBU5n"
GITEA_RUNNER_NAME: "hetzner-runner-1"
GITEA_RUNNER_LABELS: "ubuntu-latest:docker://node:20-bullseye,self-hosted,linux,x86_64,docker"
TZ: "America/Winnipeg"
command: ["act_runner", "daemon"]
volumes:
- /gitea/gitea-runner/data:/data
- /var/run/docker.sock:/var/run/docker.sock