fix: forgejo ip

This commit is contained in:
2026-01-27 00:52:16 +01:00
parent 9d7b8082c0
commit eba36f9d56
5 changed files with 532 additions and 2 deletions

View File

@@ -102,6 +102,7 @@
"/snapcast.cloonar.com/${config.networkPrefix}.97.21" "/snapcast.cloonar.com/${config.networkPrefix}.97.21"
"/lms.cloonar.com/${config.networkPrefix}.97.21" "/lms.cloonar.com/${config.networkPrefix}.97.21"
"/git.cloonar.com/${config.networkPrefix}.97.50" "/git.cloonar.com/${config.networkPrefix}.97.50"
"/forgejo.cloonar.com/${config.networkPrefix}.97.55"
"/feeds.cloonar.com/188.34.191.144" "/feeds.cloonar.com/188.34.191.144"
"/nukibridge1a753f72.cloonar.smart/${config.networkPrefix}.100.112" "/nukibridge1a753f72.cloonar.smart/${config.networkPrefix}.100.112"
"/allywatch.cloonar.com/${config.networkPrefix}.97.5" "/allywatch.cloonar.com/${config.networkPrefix}.97.5"

View File

@@ -20,6 +20,9 @@ in
users.groups.forgejo = group; users.groups.forgejo = group;
# Reuse the existing git.cloonar.com ACME cert from gitea.nix # Reuse the existing git.cloonar.com ACME cert from gitea.nix
security.acme.certs."forgejo.cloonar.com" = {
group = "nginx";
};
containers.forgejo = { containers.forgejo = {
autoStart = false; # Don't start until migration is complete autoStart = false; # Don't start until migration is complete
@@ -27,14 +30,15 @@ in
privateNetwork = true; privateNetwork = true;
hostBridge = "server"; hostBridge = "server";
hostAddress = "${networkPrefix}.97.1"; hostAddress = "${networkPrefix}.97.1";
localAddress = "${networkPrefix}.97.51/24"; # Different from gitea's .50 localAddress = "${networkPrefix}.97.55/24"; # Different from gitea's .50
bindMounts = { bindMounts = {
"/var/lib/forgejo" = { "/var/lib/forgejo" = {
hostPath = "/var/lib/forgejo/"; hostPath = "/var/lib/forgejo/";
isReadOnly = false; isReadOnly = false;
}; };
"/var/lib/acme/forgejo/" = { "/var/lib/acme/forgejo/" = {
hostPath = config.security.acme.certs.${domain}.directory; # hostPath = config.security.acme.certs.${domain}.directory;
hostPath = config.security.acme.certs."forgejo.cloonar.com".directory;
isReadOnly = true; isReadOnly = true;
}; };
"/run/secrets/forgejo-mailer-password" = { "/run/secrets/forgejo-mailer-password" = {

View File

@@ -7,6 +7,15 @@
proxyPass = "https://git.cloonar.com/"; proxyPass = "https://git.cloonar.com/";
}; };
}; };
services.nginx.virtualHosts."forgejo.cloonar.com" = {
forceSSL = true;
enableACME = true;
acmeRoot = null;
locations."/" = {
proxyPass = "http://${config.networkPrefix}.97.55:3001/";
proxyWebsockets = true;
};
};
services.nginx.virtualHosts."foundry-vtt.cloonar.com" = { services.nginx.virtualHosts."foundry-vtt.cloonar.com" = {
forceSSL = true; forceSSL = true;
enableACME = true; enableACME = true;

View File

@@ -0,0 +1,19 @@
# Gitea to Forgejo Migration - Environment Configuration
#
# Copy this file to migrate-gitea-to-forgejo.env and adjust values.
# Then run: ./scripts/migrate-gitea-to-forgejo.sh
#
# IMPORTANT: Ensure Gitea is stopped before running migration.
# Source (Gitea) - READ ONLY, never modified
# This is the original Gitea data directory
SOURCE_DATA=/var/lib/gitea
# Target (Forgejo) - where data will be copied
# Must be on a filesystem with enough space (1.2x source size)
TARGET_DATA=/var/lib/forgejo
# User/group for target files
# These should match your Forgejo service user
TARGET_USER=forgejo
TARGET_GROUP=forgejo

View File

@@ -0,0 +1,497 @@
#!/usr/bin/env bash
#
# Gitea 1.25.4 to Forgejo Migration Script
#
# This script copies data from Gitea to Forgejo and rolls back the database
# schema from version 322/323 to 304, allowing Forgejo to run its own migrations.
#
# IMPORTANT: This script NEVER modifies source data. All operations work on copies,
# so the original Gitea instance can be restarted as a rollback.
#
# Usage:
# 1. Copy migrate-gitea-to-forgejo.env.example to migrate-gitea-to-forgejo.env
# 2. Edit the .env file with your paths
# 3. Stop Gitea
# 4. Run: ./scripts/migrate-gitea-to-forgejo.sh
# 5. Update NixOS config and deploy
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/migrate-gitea-to-forgejo.env"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
log_success() { echo -e "${GREEN}[OK]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
# Load environment file
if [[ ! -f "$ENV_FILE" ]]; then
log_error "Environment file not found: $ENV_FILE"
log_info "Copy migrate-gitea-to-forgejo.env.example to migrate-gitea-to-forgejo.env and configure it."
exit 1
fi
# shellcheck source=/dev/null
source "$ENV_FILE"
# Verify required variables
: "${SOURCE_DATA:?SOURCE_DATA must be set in $ENV_FILE}"
: "${TARGET_DATA:?TARGET_DATA must be set in $ENV_FILE}"
: "${TARGET_USER:?TARGET_USER must be set in $ENV_FILE}"
: "${TARGET_GROUP:?TARGET_GROUP must be set in $ENV_FILE}"
echo "========================================"
echo "Gitea to Forgejo Migration Script"
echo "========================================"
echo ""
echo "Source: $SOURCE_DATA (read-only)"
echo "Target: $TARGET_DATA"
echo "User: $TARGET_USER:$TARGET_GROUP"
echo ""
# ============================================
# PHASE 1: Pre-flight Checks
# ============================================
log_info "Phase 1: Pre-flight checks..."
# Check if running as root (needed for chown)
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root (for chown operations)"
exit 1
fi
# Verify SQLite version >= 3.35 (required for DROP COLUMN)
if ! command -v sqlite3 &> /dev/null; then
log_error "sqlite3 command not found. Please install SQLite."
exit 1
fi
sqlite_version=$(sqlite3 --version | cut -d' ' -f1)
sqlite_major=$(echo "$sqlite_version" | cut -d'.' -f1)
sqlite_minor=$(echo "$sqlite_version" | cut -d'.' -f2)
if [[ "$sqlite_major" -lt 3 ]] || { [[ "$sqlite_major" -eq 3 ]] && [[ "$sqlite_minor" -lt 35 ]]; }; then
log_error "SQLite $sqlite_version is too old. Need 3.35+ for DROP COLUMN support."
exit 1
fi
log_success "SQLite version: $sqlite_version"
# Verify rsync is available (needed for incremental copying)
if ! command -v rsync &> /dev/null; then
log_error "rsync command not found. Please install rsync."
exit 1
fi
log_success "rsync available"
# Verify source exists
if [[ ! -d "$SOURCE_DATA" ]]; then
log_error "Source directory not found: $SOURCE_DATA"
exit 1
fi
log_success "Source directory exists"
# Find source database (could be gitea.db or forgejo.db depending on setup)
SOURCE_DB=""
if [[ -f "$SOURCE_DATA/data/gitea.db" ]]; then
SOURCE_DB="$SOURCE_DATA/data/gitea.db"
elif [[ -f "$SOURCE_DATA/gitea.db" ]]; then
SOURCE_DB="$SOURCE_DATA/gitea.db"
else
log_error "Source database not found in $SOURCE_DATA/data/ or $SOURCE_DATA/"
exit 1
fi
log_success "Source database found: $SOURCE_DB"
# Verify source app.ini exists
SOURCE_INI=""
if [[ -f "$SOURCE_DATA/custom/conf/app.ini" ]]; then
SOURCE_INI="$SOURCE_DATA/custom/conf/app.ini"
elif [[ -f "$SOURCE_DATA/conf/app.ini" ]]; then
SOURCE_INI="$SOURCE_DATA/conf/app.ini"
else
log_error "Source app.ini not found in $SOURCE_DATA/custom/conf/ or $SOURCE_DATA/conf/"
exit 1
fi
log_success "Source app.ini found: $SOURCE_INI"
# Check disk space (need 1.2x source size)
source_size=$(du -sb "$SOURCE_DATA" | cut -f1)
required=$((source_size * 12 / 10))
target_parent=$(dirname "$TARGET_DATA")
mkdir -p "$target_parent"
available=$(df --output=avail -B1 "$target_parent" | tail -1)
if [[ "$available" -lt "$required" ]]; then
log_error "Not enough disk space. Need $(numfmt --to=iec $required), have $(numfmt --to=iec $available)"
exit 1
fi
log_success "Disk space OK: need $(numfmt --to=iec $required), have $(numfmt --to=iec $available)"
# Warn if target exists (rsync will sync incrementally)
if [[ -d "$TARGET_DATA" ]]; then
log_warn "Target directory exists: $TARGET_DATA"
log_info "rsync will perform incremental sync (only copying changed files)"
read -p "Continue with incremental sync? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_error "Aborted by user"
exit 1
fi
fi
# ============================================
# PHASE 2: Copy All Data
# ============================================
log_info "Phase 2: Copying data..."
mkdir -p "$TARGET_DATA/data"
mkdir -p "$TARGET_DATA/custom/conf"
# Copy database
log_info "Copying database..."
rsync -a --info=progress2 "$SOURCE_DB" "$TARGET_DATA/data/forgejo.db"
log_success "Database copied"
# Copy all data directories (preserve attributes, sync incrementally)
for dir in repositories avatars attachments packages lfs custom queues indexers; do
if [[ -d "$SOURCE_DATA/$dir" ]]; then
log_info "Syncing $dir..."
mkdir -p "$TARGET_DATA/$dir"
rsync -a --delete --info=progress2 "$SOURCE_DATA/$dir/" "$TARGET_DATA/$dir/"
log_success "Synced $dir"
fi
done
# Also check data/ subdirectory structure
for dir in repositories avatars attachments packages lfs; do
if [[ -d "$SOURCE_DATA/data/$dir" ]]; then
log_info "Syncing data/$dir..."
mkdir -p "$TARGET_DATA/data/$dir"
rsync -a --delete --info=progress2 "$SOURCE_DATA/data/$dir/" "$TARGET_DATA/data/$dir/"
log_success "Synced data/$dir"
fi
done
# ============================================
# PHASE 3: Database Schema Rollback
# ============================================
log_info "Phase 3: Rolling back database schema..."
TARGET_DB="$TARGET_DATA/data/forgejo.db"
# Show current schema version
current_version=$(sqlite3 "$TARGET_DB" "SELECT version FROM version WHERE id=1;")
log_info "Current Gitea schema version: $current_version"
log_info "Target version: 304"
# Create rollback SQL script
ROLLBACK_SQL=$(mktemp)
cat > "$ROLLBACK_SQL" << 'ROLLBACK_EOF'
-- ================================================================
-- Gitea 1.25.4 to Forgejo Rollback Script
-- Rolls back migrations 305-322 to allow Forgejo to migrate cleanly
-- ================================================================
-- Enable foreign keys check after we're done
PRAGMA foreign_keys = OFF;
-- ============================================
-- MIGRATION 305: Drop repo_license table
-- ============================================
DROP TABLE IF EXISTS repo_license;
-- ============================================
-- MIGRATION 308 & 317: Drop action table indices
-- (These are the main conflict source)
-- ============================================
DROP INDEX IF EXISTS IDX_action_r_u_d;
DROP INDEX IF EXISTS IDX_action_au_r_c_u_d;
DROP INDEX IF EXISTS IDX_action_c_u_d;
DROP INDEX IF EXISTS IDX_action_c_u;
DROP INDEX IF EXISTS IDX_action_au_c_u;
-- Alternative naming conventions
DROP INDEX IF EXISTS UQE_action_r_u_d;
DROP INDEX IF EXISTS UQE_action_au_r_c_u_d;
DROP INDEX IF EXISTS UQE_action_c_u_d;
DROP INDEX IF EXISTS UQE_action_c_u;
DROP INDEX IF EXISTS UQE_action_au_c_u;
-- ============================================
-- MIGRATION 309: Drop notification table indices
-- ============================================
DROP INDEX IF EXISTS IDX_notification_u_s_uu;
DROP INDEX IF EXISTS IDX_notification_user_id;
DROP INDEX IF EXISTS IDX_notification_repo_id;
DROP INDEX IF EXISTS IDX_notification_status;
DROP INDEX IF EXISTS IDX_notification_source;
DROP INDEX IF EXISTS IDX_notification_issue_id;
DROP INDEX IF EXISTS IDX_notification_commit_id;
DROP INDEX IF EXISTS IDX_notification_updated_by;
DROP INDEX IF EXISTS UQE_notification_u_s_uu;
-- ============================================
-- MIGRATION 313: Drop issue_pin table
-- (pin_order restoration handled separately)
-- ============================================
DROP TABLE IF EXISTS issue_pin;
-- ============================================
-- MIGRATION 306: Drop protected_branch column
-- ============================================
ALTER TABLE protected_branch DROP COLUMN IF EXISTS block_admin_merge_override;
-- ============================================
-- MIGRATION 310: Drop protected_branch column
-- ============================================
ALTER TABLE protected_branch DROP COLUMN IF EXISTS priority;
-- ============================================
-- MIGRATION 311: Drop issue column
-- ============================================
ALTER TABLE issue DROP COLUMN IF EXISTS time_estimate;
-- ============================================
-- MIGRATION 312: Drop pull_auto_merge column
-- ============================================
ALTER TABLE pull_auto_merge DROP COLUMN IF EXISTS delete_branch_after_merge;
-- ============================================
-- MIGRATION 315: Drop action_runner column
-- ============================================
ALTER TABLE action_runner DROP COLUMN IF EXISTS ephemeral;
-- ============================================
-- MIGRATION 316: Drop description columns
-- ============================================
ALTER TABLE secret DROP COLUMN IF EXISTS description;
ALTER TABLE action_variable DROP COLUMN IF EXISTS description;
-- ============================================
-- MIGRATION 318: Drop repo_unit column
-- ============================================
ALTER TABLE repo_unit DROP COLUMN IF EXISTS anonymous_access_mode;
-- ============================================
-- MIGRATION 319: Drop label column
-- ============================================
ALTER TABLE label DROP COLUMN IF EXISTS exclusive_order;
-- ============================================
-- MIGRATION 320: Drop login_source column
-- ============================================
ALTER TABLE login_source DROP COLUMN IF EXISTS two_factor_policy;
-- ============================================
-- SET VERSION TO 304
-- ============================================
UPDATE version SET version = 304 WHERE id = 1;
PRAGMA foreign_keys = ON;
ROLLBACK_EOF
log_info "Executing schema rollback..."
# SQLite doesn't support DROP COLUMN IF EXISTS, so we need to handle errors gracefully
# Execute each ALTER TABLE separately to handle missing columns
sqlite3 "$TARGET_DB" << 'SQL_PART1'
PRAGMA foreign_keys = OFF;
-- Drop tables
DROP TABLE IF EXISTS repo_license;
DROP TABLE IF EXISTS issue_pin;
-- Drop indices (these always work, even if index doesn't exist)
DROP INDEX IF EXISTS IDX_action_r_u_d;
DROP INDEX IF EXISTS IDX_action_au_r_c_u_d;
DROP INDEX IF EXISTS IDX_action_c_u_d;
DROP INDEX IF EXISTS IDX_action_c_u;
DROP INDEX IF EXISTS IDX_action_au_c_u;
DROP INDEX IF EXISTS UQE_action_r_u_d;
DROP INDEX IF EXISTS UQE_action_au_r_c_u_d;
DROP INDEX IF EXISTS UQE_action_c_u_d;
DROP INDEX IF EXISTS UQE_action_c_u;
DROP INDEX IF EXISTS UQE_action_au_c_u;
DROP INDEX IF EXISTS IDX_notification_u_s_uu;
DROP INDEX IF EXISTS IDX_notification_user_id;
DROP INDEX IF EXISTS IDX_notification_repo_id;
DROP INDEX IF EXISTS IDX_notification_status;
DROP INDEX IF EXISTS IDX_notification_source;
DROP INDEX IF EXISTS IDX_notification_issue_id;
DROP INDEX IF EXISTS IDX_notification_commit_id;
DROP INDEX IF EXISTS IDX_notification_updated_by;
DROP INDEX IF EXISTS UQE_notification_u_s_uu;
SQL_PART1
# Function to drop column if it exists
drop_column_if_exists() {
local table="$1"
local column="$2"
local exists
exists=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM pragma_table_info('$table') WHERE name='$column';")
if [[ "$exists" -gt 0 ]]; then
log_info "Dropping column $table.$column..."
sqlite3 "$TARGET_DB" "ALTER TABLE $table DROP COLUMN $column;"
log_success "Dropped $table.$column"
else
log_info "Column $table.$column does not exist, skipping"
fi
}
# Drop columns added in migrations 306-320
drop_column_if_exists "protected_branch" "block_admin_merge_override"
drop_column_if_exists "protected_branch" "priority"
drop_column_if_exists "issue" "time_estimate"
drop_column_if_exists "pull_auto_merge" "delete_branch_after_merge"
drop_column_if_exists "action_runner" "ephemeral"
drop_column_if_exists "secret" "description"
drop_column_if_exists "action_variable" "description"
drop_column_if_exists "repo_unit" "anonymous_access_mode"
drop_column_if_exists "label" "exclusive_order"
drop_column_if_exists "login_source" "two_factor_policy"
# Check if pin_order column needs to be added back to issue table (migration 313 removed it)
log_info "Checking if pin_order column needs to be restored to issue table..."
has_pin_order=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM pragma_table_info('issue') WHERE name='pin_order';")
if [[ "$has_pin_order" -eq 0 ]]; then
log_info "Adding pin_order column back to issue table..."
sqlite3 "$TARGET_DB" "ALTER TABLE issue ADD COLUMN pin_order INTEGER DEFAULT 0;"
log_success "Added pin_order column to issue table"
else
log_info "pin_order column already exists in issue table"
fi
# Set version to 304 (allows Forgejo to run migration 305 which converts two_factor.secret from TEXT to BLOB)
sqlite3 "$TARGET_DB" "UPDATE version SET version = 304 WHERE id = 1;"
log_success "Database version set to 304"
rm -f "$ROLLBACK_SQL"
# ============================================
# PHASE 4: Clear Regeneratable Data
# ============================================
log_info "Phase 4: Clearing regeneratable data..."
# Remove indexers (will be rebuilt on first start)
if [[ -d "$TARGET_DATA/indexers" ]]; then
rm -rf "$TARGET_DATA/indexers"
log_success "Removed indexers (will be rebuilt)"
fi
# Remove queues (will be recreated)
if [[ -d "$TARGET_DATA/queues" ]]; then
rm -rf "$TARGET_DATA/queues"
log_success "Removed queues (will be recreated)"
fi
# ============================================
# PHASE 5: Update Configuration
# ============================================
log_info "Phase 5: Updating configuration..."
# Copy app.ini
rsync -a --info=progress2 "$SOURCE_INI" "$TARGET_DATA/custom/conf/app.ini"
log_success "Copied app.ini"
# Update paths from gitea to forgejo
sed -i 's|/var/lib/gitea|/var/lib/forgejo|g' "$TARGET_DATA/custom/conf/app.ini"
log_success "Updated paths in app.ini"
# Check if WAL mode is already configured
if ! grep -q "SQLITE_JOURNAL_MODE" "$TARGET_DATA/custom/conf/app.ini"; then
# Add WAL mode after [database] section
sed -i '/^\[database\]/a SQLITE_JOURNAL_MODE = WAL' "$TARGET_DATA/custom/conf/app.ini"
log_success "Enabled SQLite WAL mode"
else
log_info "SQLite journal mode already configured"
fi
# ============================================
# PHASE 6: Set Permissions
# ============================================
log_info "Phase 6: Setting permissions..."
chown -R "$TARGET_USER:$TARGET_GROUP" "$TARGET_DATA"
chmod 750 "$TARGET_DATA"
chmod 640 "$TARGET_DATA/data/forgejo.db"
log_success "Permissions set for $TARGET_USER:$TARGET_GROUP"
# ============================================
# PHASE 7: Verify Database Integrity
# ============================================
log_info "Phase 7: Verifying database integrity..."
sqlite3 "$TARGET_DB" << 'VERIFY_SQL'
.headers off
.mode list
-- Verify version was set correctly
SELECT 'Version: ' || CASE WHEN version = 304 THEN 'PASS (304)' ELSE 'FAIL (version=' || version || ')' END
FROM version WHERE id = 1;
-- Check critical tables exist
SELECT 'Users: ' || CASE WHEN COUNT(*) > 0 THEN 'PASS (' || COUNT(*) || ' users)' ELSE 'WARN (empty)' END FROM user;
SELECT 'Repositories: ' || CASE WHEN COUNT(*) > 0 THEN 'PASS (' || COUNT(*) || ' repos)' ELSE 'WARN (empty)' END FROM repository;
SELECT 'Secrets: PASS (' || COUNT(*) || ' secrets)' FROM secret;
SELECT 'Runners: PASS (' || COUNT(*) || ' runners)' FROM action_runner;
SELECT 'Variables: PASS (' || COUNT(*) || ' variables)' FROM action_variable;
VERIFY_SQL
# Verify dropped tables are gone
repo_license_exists=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='repo_license';")
issue_pin_exists=$(sqlite3 "$TARGET_DB" "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='issue_pin';")
if [[ "$repo_license_exists" -eq 0 ]]; then
log_success "repo_license table: DROPPED"
else
log_warn "repo_license table: STILL EXISTS"
fi
if [[ "$issue_pin_exists" -eq 0 ]]; then
log_success "issue_pin table: DROPPED"
else
log_warn "issue_pin table: STILL EXISTS"
fi
# ============================================
# PHASE 8: Print Next Steps
# ============================================
echo ""
echo "========================================"
echo -e "${GREEN}Migration complete!${NC}"
echo "========================================"
echo ""
echo "Data copied to: $TARGET_DATA"
echo "Database schema rolled back to version 304"
echo ""
echo "Next steps:"
echo ""
echo "1. Update NixOS configuration:"
echo " - Create hosts/fw/modules/forgejo.nix based on gitea.nix"
echo " - Change services.gitea to services.forgejo"
echo " - Update bind mount paths in container config"
echo " - Update runner configuration for Forgejo"
echo ""
echo "2. Deploy:"
echo " nixos-rebuild switch"
echo ""
echo "3. Monitor first startup:"
echo " journalctl -u container@git -f"
echo ""
echo "4. Verify functionality:"
echo " [ ] Forgejo starts without errors"
echo " [ ] Login via OpenID (auth.cloonar.com)"
echo " [ ] All repositories visible"
echo " [ ] Can push/pull to repositories"
echo " [ ] CI/CD runners connect"
echo " [ ] Workflow with secrets runs"
echo " [ ] Packages registry accessible"
echo ""
echo -e "${YELLOW}ROLLBACK:${NC} If anything fails, original Gitea data is untouched."
echo "Just revert NixOS config and restart Gitea container."
echo "========================================"