Bash script that mirrors repositories from my private Forgejo instance
forgejo-mirror.sh edited
86 lines 2.5 kB view raw
1#!/usr/bin/env bash 2 3# This script mirrors repositories from my private Forgejo instance to any 4# external Git hosts (in this case, Tangled.) 5# 6# 1. Set `FORGEJO_DATA_DIR` to your Forgejo's repositories directory, 7# `STATE_FILE` is how this script takes note of changes. 8# 9# 2. Create a repository (e.g. mary/mirror-config, configurable via 10# `CONFIG_REPO_PATH`) and write a `repos.txt` file containing the 11# repositories you want to mirror: 12# 13# mary/pkg-protobuf=git@tangled.sh:mary.my.id/pkg-protobuf 14# # comments and blank lines are ignored 15# 16# 3. Generate a new SSH key, or use your existing one, and set `GIT_SSH_COMMAND` 17# to make use of it. 18# 19# 4. Run this script as a cron job. 20 21set -euo pipefail 22 23GIT_SSH_COMMAND="ssh -i ~/.ssh/forgejo_mirror_bot -o StrictHostKeyChecking=no" 24export GIT_SSH_COMMAND 25 26STATE_FILE="$HOME/.forgejo-mirror-state" 27FORGEJO_DATA_DIR="$HOME/dockers/data/forgejo/git/repositories" 28CONFIG_REPO_PATH="$FORGEJO_DATA_DIR/mary/mirror-config.git" 29 30# Read repo mapping from config 31cd "$CONFIG_REPO_PATH" 32REPOS=$(git show HEAD:config.txt) 33 34# Load previous state 35declare -A last_hashes 36if [[ -f "$STATE_FILE" ]]; then 37 while IFS='=' read -r repo hash; do 38 last_hashes["$repo"]="$hash" 39 done < "$STATE_FILE" 40fi 41 42# Prepare new state and track if anything changed 43declare -A new_hashes 44changes_made=false 45 46# Process each repo mapping 47while IFS= read -r line; do 48 # Skip empty lines and comments 49 [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue 50 51 # Parse source=target format 52 if [[ "$line" =~ ^([^=]+)=(.+)$ ]]; then 53 source_repo="${BASH_REMATCH[1]}" 54 target_url="${BASH_REMATCH[2]}" 55 56 repo_path="$FORGEJO_DATA_DIR/$source_repo.git" 57 if [ -d "$repo_path" ]; then 58 cd "$repo_path" 59 current_hash=$(git show-ref | sha256sum | cut -d' ' -f1) 60 new_hashes["$source_repo"]="$current_hash" 61 62 if [[ "${last_hashes[$source_repo]:-}" != "$current_hash" ]]; then 63 echo "[-] Mirroring $source_repo -> $target_url (changed)" 64 git push --mirror "$target_url" || { 65 echo "Failed to mirror $source_repo" 66 continue 67 } 68 changes_made=true 69 else 70 echo "[-] Skipping $source_repo (no changes)" 71 fi 72 else 73 echo "[-] Repository $source_repo not found at $repo_path" 74 fi 75 else 76 echo "[-] Invalid format: $line (expected source=target)" 77 fi 78done <<< "$REPOS" 79 80# Only save new state if there were changes 81if [[ "$changes_made" == true ]]; then 82 > "$STATE_FILE" 83 for repo in "${!new_hashes[@]}"; do 84 echo "$repo=${new_hashes[$repo]}" >> "$STATE_FILE" 85 done 86fi