From 9d8a08900d11465cebf529d0cb31240de43042f9 Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Sat, 28 Feb 2026 13:22:41 +0800 Subject: [PATCH] feat: add forgejo deploy script, fix nginx envsubst variable leak - services/forgejo/deploy.sh: deploys Forgejo via Docker to /opt/frp, sets up nginx vhost, optionally installs GitHub mirror sync cron - services/forgejo/{.env.example,docker-compose.yml,nginx.conf.example}: bundled templates following find_template pattern (INFRA_DIR override) - services/nginx/deploy.sh: fix bare envsubst clobbering nginx $vars (e.g. $host, $uri) by scoping substitution to known domain vars only --- services/forgejo/.env.example | 10 +++++ services/forgejo/deploy.sh | 67 +++++++++++++++++++++++++++++ services/forgejo/docker-compose.yml | 23 ++++++++++ services/forgejo/nginx.conf.example | 26 +++++++++++ services/nginx/deploy.sh | 2 +- 5 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 services/forgejo/.env.example create mode 100755 services/forgejo/deploy.sh create mode 100644 services/forgejo/docker-compose.yml create mode 100644 services/forgejo/nginx.conf.example diff --git a/services/forgejo/.env.example b/services/forgejo/.env.example new file mode 100644 index 0000000..c1467dd --- /dev/null +++ b/services/forgejo/.env.example @@ -0,0 +1,10 @@ +# role: vps +# description: Forgejo self-hosted git service with optional GitHub mirror sync + +GIT_DOMAIN=git.your-domain.com + +# Optional: GitHub → Forgejo mirror sync (leave blank to skip cron setup) +GITHUB_USER= +GITHUB_TOKEN= +FORGEJO_URL=https://git.your-domain.com +FORGEJO_TOKEN= diff --git a/services/forgejo/deploy.sh b/services/forgejo/deploy.sh new file mode 100755 index 0000000..2d54eb1 --- /dev/null +++ b/services/forgejo/deploy.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# Deploys Forgejo (self-hosted git) via Docker on a VPS. +# Usage: INFRA_DIR=/path/to/infra/services/forgejo ./deploy.sh + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/../../bin/lib/common.sh" + +ENV_FILE="${INFRA_DIR:-.}/.env" +[ -f "$ENV_FILE" ] || { log_error "No .env found at $ENV_FILE"; exit 1; } +set -a; source "$ENV_FILE"; set +a + +require_env GIT_DOMAIN +require_command docker "https://docs.docker.com/engine/install/" + +find_template() { + local f="$1" + if [[ -n "${INFRA_DIR:-}" && -f "${INFRA_DIR}/$f" ]]; then + echo "${INFRA_DIR}/$f" + elif [[ -f "$SCRIPT_DIR/$f" ]]; then + echo "$SCRIPT_DIR/$f" + else + log_error "Template not found: $f" + return 1 + fi +} + +DEPLOY_DIR="/opt/forgejo" +log_info "Creating deploy directory $DEPLOY_DIR..." +mkdir -p "$DEPLOY_DIR/data" + +log_info "Deploying docker-compose.yml..." +cp "$(find_template docker-compose.yml)" "$DEPLOY_DIR/docker-compose.yml" + +log_info "Starting Forgejo container..." +docker compose -f "$DEPLOY_DIR/docker-compose.yml" up -d + +log_info "Deploying nginx vhost for ${GIT_DOMAIN}..." +envsubst '${GIT_DOMAIN}' < "$(find_template nginx.conf.example)" > "/etc/nginx/sites-available/forgejo" +ln -sf /etc/nginx/sites-available/forgejo /etc/nginx/sites-enabled/forgejo +nginx -t +systemctl reload nginx + +# Optional: set up GitHub mirror sync cron +if [[ -n "${GITHUB_USER:-}" && -n "${GITHUB_TOKEN:-}" && -n "${FORGEJO_URL:-}" && -n "${FORGEJO_TOKEN:-}" ]]; then + SYNC_SCRIPT="$(find_template migrate_github_to_forgejo.py 2>/dev/null || true)" + if [[ -n "$SYNC_SCRIPT" ]]; then + log_info "Installing GitHub mirror sync script..." + cp "$SYNC_SCRIPT" "$DEPLOY_DIR/migrate_github_to_forgejo.py" + mkdir -p "$DEPLOY_DIR/logs" + + CRON_LINE="0 3 * * * cd $DEPLOY_DIR && GITHUB_USER=${GITHUB_USER} GITHUB_TOKEN=${GITHUB_TOKEN} FORGEJO_URL=${FORGEJO_URL} FORGEJO_TOKEN=${FORGEJO_TOKEN} python3 migrate_github_to_forgejo.py >> $DEPLOY_DIR/logs/mirror-sync.log 2>&1" + (crontab -l 2>/dev/null | grep -v "migrate_github_to_forgejo"; echo "$CRON_LINE") | crontab - + log_info "Cron sync installed (daily 03:00)" + else + log_warn "migrate_github_to_forgejo.py not found in INFRA_DIR — skipping cron setup" + fi +else + log_info "GitHub sync vars not set — skipping cron setup" +fi + +log_info "Forgejo deployed at http://localhost:3000" +echo "" +echo "Remaining manual steps:" +echo " 1. Get TLS cert: certbot --nginx -d ${GIT_DOMAIN}" +echo " 2. Complete Forgejo initial setup at https://${GIT_DOMAIN}" +echo " 3. Generate Forgejo API token: https://${GIT_DOMAIN}/user/settings/applications" diff --git a/services/forgejo/docker-compose.yml b/services/forgejo/docker-compose.yml new file mode 100644 index 0000000..6063dd5 --- /dev/null +++ b/services/forgejo/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3' + +networks: + forgejo: + external: false + +services: + server: + image: codeberg.org/forgejo/forgejo:9 + container_name: forgejo + environment: + - USER_UID=1000 + - USER_GID=1000 + restart: always + networks: + - forgejo + volumes: + - /opt/forgejo/data:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "2223:22" diff --git a/services/forgejo/nginx.conf.example b/services/forgejo/nginx.conf.example new file mode 100644 index 0000000..e7ef59e --- /dev/null +++ b/services/forgejo/nginx.conf.example @@ -0,0 +1,26 @@ +server { + listen 80; + server_name ${GIT_DOMAIN}; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name ${GIT_DOMAIN}; + + ssl_certificate /etc/letsencrypt/live/${GIT_DOMAIN}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/${GIT_DOMAIN}/privkey.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + location / { + proxy_pass http://localhost:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + client_max_body_size 512M; +} diff --git a/services/nginx/deploy.sh b/services/nginx/deploy.sh index f9c6d59..85bf2e2 100755 --- a/services/nginx/deploy.sh +++ b/services/nginx/deploy.sh @@ -23,7 +23,7 @@ mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled for conf in "${INFRA_DIR}/sites/"*.conf; do name="$(basename "$conf" .conf)" - envsubst < "$conf" > "/etc/nginx/sites-available/${name}" + envsubst '${DOMAIN}${BLOG_DOMAIN}${CHAN_DOMAIN}${MAIL_DOMAIN}${GIT_DOMAIN}' < "$conf" > "/etc/nginx/sites-available/${name}" ln -sf "/etc/nginx/sites-available/${name}" "/etc/nginx/sites-enabled/${name}" log_info " Deployed ${name}" done