#!/bin/bash set -euo pipefail ROOT_DIR="$(pwd)" TIMESTAMP="$(date +%Y%m%d_%H%M%S)" REPORT_TXT="$ROOT_DIR/sync_report_$TIMESTAMP.txt" REPORT_JSON="$ROOT_DIR/sync_report_$TIMESTAMP.json" DRY_RUN=false TIMEOUT=60 # Parse args for arg in "$@"; do case $arg in --dry-run) DRY_RUN=true ;; --timeout=*) TIMEOUT="${arg#*=}" ;; esac done log() { echo -e "$1" echo -e "$1" >> "$REPORT_TXT" } json_escape() { echo "$1" | sed 's/"/\\"/g' } run_cmd() { if $DRY_RUN; then log "🟔 [DRY-RUN] $*" return 0 fi timeout "$TIMEOUT" bash -c "$*" >/dev/null 2>&1 } echo "Repo Sync Report - $(date)" > "$REPORT_TXT" echo "[" > "$REPORT_JSON" FIRST=true SUCCESS=0 FAIL=0 SKIP=0 for dir in */; do REPO_PATH="$ROOT_DIR/$dir" REPO_NAME=$(basename "$dir") STATUS="unknown" MESSAGE="" log "\nšŸ”¹ Processing: $REPO_NAME" cd "$REPO_PATH" || { STATUS="fail"; MESSAGE="cannot cd"; ((FAIL++)); continue; } if [ ! -d ".git" ]; then STATUS="skip"; MESSAGE="not a git repo"; ((SKIP++)) log "āš ļø Not a git repo" continue fi if ! git remote get-url origin &>/dev/null; then STATUS="skip"; MESSAGE="no origin"; ((SKIP++)) log "āš ļø No origin" continue fi # Detect default branch DEFAULT_BRANCH=$(git remote show origin | awk '/HEAD branch/ {print $NF}') DEFAULT_BRANCH=${DEFAULT_BRANCH:-main} if ! git show-ref --verify --quiet "refs/heads/$DEFAULT_BRANCH"; then STATUS="skip"; MESSAGE="no local $DEFAULT_BRANCH"; ((SKIP++)) log "āš ļø Missing local branch $DEFAULT_BRANCH" continue fi # Check clean working tree if ! git diff --quiet || ! git diff --cached --quiet; then STATUS="skip"; MESSAGE="uncommitted changes"; ((SKIP++)) log "āš ļø Uncommitted changes" continue fi CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) if [ "$CURRENT_BRANCH" = "HEAD" ]; then STATUS="skip"; MESSAGE="detached HEAD"; ((SKIP++)) log "āš ļø Detached HEAD" continue fi # Fetch if ! run_cmd "git fetch origin --prune"; then STATUS="fail"; MESSAGE="fetch failed"; ((FAIL++)) log "āŒ Fetch failed" continue fi # Checkout default branch if [ "$CURRENT_BRANCH" != "$DEFAULT_BRANCH" ]; then if ! run_cmd "git checkout $DEFAULT_BRANCH"; then STATUS="fail"; MESSAGE="checkout failed"; ((FAIL++)) log "āŒ Checkout failed" continue fi fi # Rebase safely if run_cmd "git rebase origin/$DEFAULT_BRANCH"; then STATUS="success"; MESSAGE="rebased" log "āœ… Rebased onto origin/$DEFAULT_BRANCH" ((SUCCESS++)) else git rebase --abort &>/dev/null || true STATUS="fail"; MESSAGE="rebase conflict" log "āŒ Rebase conflict (aborted)" ((FAIL++)) fi # Return to original branch if [ "$CURRENT_BRANCH" != "$DEFAULT_BRANCH" ]; then run_cmd "git checkout $CURRENT_BRANCH" fi # JSON logging ESC_MSG=$(json_escape "$MESSAGE") if [ "$FIRST" = true ]; then FIRST=false else echo "," >> "$REPORT_JSON" fi echo " {\"repo\":\"$REPO_NAME\",\"status\":\"$STATUS\",\"message\":\"$ESC_MSG\"}" >> "$REPORT_JSON" done echo "]" >> "$REPORT_JSON" log "\n======================================" log "āœ… Success: $SUCCESS" log "āŒ Failed: $FAIL" log "āš ļø Skipped: $SKIP" log "šŸ“„ TXT Report: $REPORT_TXT" log "šŸ“„ JSON Report: $REPORT_JSON" echo -e "\nDone."