openclaw/scripts/sync-upstream.sh

212 lines
9.5 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Sync with upstream clawdbot while preserving local extensions
#
# Strategy:
# 1. Stash local extensions
# 2. Pull/rebase upstream
# 3. Restore local extensions
# 4. Handle conflicts if any
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(dirname "$SCRIPT_DIR")"
cd "$REPO_DIR"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log() { echo -e "${GREEN}[+]${NC} $1"; }
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
error() { echo -e "${RED}[x]${NC} $1"; exit 1; }
info() { echo -e "${CYAN}[i]${NC} $1"; }
#───────────────────────────────────────────────────────────────────────────────
# Configuration
#───────────────────────────────────────────────────────────────────────────────
UPSTREAM_REMOTE="upstream"
UPSTREAM_URL="https://github.com/clawdbot/clawdbot.git"
MAIN_BRANCH="main"
# Local-only paths (won't conflict with upstream)
LOCAL_EXTENSIONS=(
"extensions/zoho-cliq"
# Add more local extensions here
)
# Backup directory
BACKUP_DIR="$REPO_DIR/.local-backup"
#───────────────────────────────────────────────────────────────────────────────
# Setup upstream remote
#───────────────────────────────────────────────────────────────────────────────
setup_upstream() {
if ! git remote get-url "$UPSTREAM_REMOTE" &>/dev/null; then
log "Adding upstream remote: $UPSTREAM_URL"
git remote add "$UPSTREAM_REMOTE" "$UPSTREAM_URL"
else
info "Upstream remote already configured"
fi
}
#───────────────────────────────────────────────────────────────────────────────
# Backup local extensions
#───────────────────────────────────────────────────────────────────────────────
backup_local() {
log "Backing up local extensions..."
rm -rf "$BACKUP_DIR"
mkdir -p "$BACKUP_DIR"
for ext in "${LOCAL_EXTENSIONS[@]}"; do
if [[ -d "$REPO_DIR/$ext" ]]; then
local dest="$BACKUP_DIR/$ext"
mkdir -p "$(dirname "$dest")"
# Use rsync to exclude node_modules and avoid circular symlinks
rsync -a --exclude='node_modules' "$REPO_DIR/$ext/" "$dest/" 2>/dev/null || {
warn " Backup of $ext had warnings (likely circular symlinks)"
}
info " Backed up: $ext"
fi
done
# Also backup any uncommitted changes
if ! git diff --quiet || ! git diff --cached --quiet; then
git stash push -m "sync-upstream-$(date +%Y%m%d-%H%M%S)"
info " Stashed uncommitted changes"
fi
}
#───────────────────────────────────────────────────────────────────────────────
# Restore local extensions
#───────────────────────────────────────────────────────────────────────────────
restore_local() {
log "Restoring local extensions..."
for ext in "${LOCAL_EXTENSIONS[@]}"; do
local src="$BACKUP_DIR/$ext"
local dest="$REPO_DIR/$ext"
if [[ -d "$src" ]]; then
# Remove upstream version if exists (unlikely for local-only)
rm -rf "$dest"
mkdir -p "$dest"
# Use rsync for consistency with backup
rsync -a "$src/" "$dest/"
info " Restored: $ext"
fi
done
# Restore stashed changes if any
if git stash list | grep -q "sync-upstream-"; then
log "Restoring stashed changes..."
git stash pop || warn "Stash pop had conflicts - resolve manually"
fi
}
#───────────────────────────────────────────────────────────────────────────────
# Sync with upstream
#───────────────────────────────────────────────────────────────────────────────
sync_upstream() {
log "Fetching upstream..."
git fetch "$UPSTREAM_REMOTE"
local LOCAL_HEAD=$(git rev-parse HEAD)
local UPSTREAM_HEAD=$(git rev-parse "$UPSTREAM_REMOTE/$MAIN_BRANCH")
if [[ "$LOCAL_HEAD" == "$UPSTREAM_HEAD" ]]; then
info "Already up to date with upstream"
return 0
fi
# Count commits behind/ahead
local BEHIND=$(git rev-list --count HEAD.."$UPSTREAM_REMOTE/$MAIN_BRANCH")
local AHEAD=$(git rev-list --count "$UPSTREAM_REMOTE/$MAIN_BRANCH"..HEAD)
info "Local is $BEHIND commits behind, $AHEAD commits ahead of upstream"
# Merge strategy: merge upstream into local (preserves local commits)
log "Merging upstream/$MAIN_BRANCH..."
if git merge "$UPSTREAM_REMOTE/$MAIN_BRANCH" -m "Merge upstream $MAIN_BRANCH"; then
log "Merge successful!"
else
error "Merge conflicts detected. Resolve manually then run: git merge --continue"
fi
}
#───────────────────────────────────────────────────────────────────────────────
# Check for local modifications to upstream files
#───────────────────────────────────────────────────────────────────────────────
check_conflicts() {
log "Checking for potential conflicts..."
# Files modified locally that also exist upstream
local MODIFIED=$(git diff --name-only "$UPSTREAM_REMOTE/$MAIN_BRANCH"...HEAD 2>/dev/null || true)
if [[ -n "$MODIFIED" ]]; then
info "Files modified locally (may conflict on next sync):"
echo "$MODIFIED" | head -20
local COUNT=$(echo "$MODIFIED" | wc -l | tr -d ' ')
if [[ $COUNT -gt 20 ]]; then
info " ... and $((COUNT - 20)) more"
fi
fi
}
#───────────────────────────────────────────────────────────────────────────────
# Main
#───────────────────────────────────────────────────────────────────────────────
main() {
echo "═══════════════════════════════════════════════════════════════"
echo " Clawdbot Upstream Sync"
echo "═══════════════════════════════════════════════════════════════"
echo ""
# Ensure we're on the right branch
CURRENT_BRANCH=$(git branch --show-current)
if [[ "$CURRENT_BRANCH" != "$MAIN_BRANCH" ]]; then
warn "Not on $MAIN_BRANCH branch (on: $CURRENT_BRANCH)"
read -p "Switch to $MAIN_BRANCH? [y/N] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
git checkout "$MAIN_BRANCH"
else
error "Aborted"
fi
fi
# Ensure working directory is clean (except for local extensions)
if ! git diff --quiet -- . ":(exclude)extensions/"; then
warn "You have uncommitted changes outside extensions/"
read -p "Continue anyway? [y/N] " -n 1 -r
echo
[[ $REPLY =~ ^[Yy]$ ]] || error "Aborted"
fi
setup_upstream
backup_local
sync_upstream
restore_local
check_conflicts
echo ""
echo "═══════════════════════════════════════════════════════════════"
echo -e "${GREEN}Sync complete!${NC}"
echo "═══════════════════════════════════════════════════════════════"
echo ""
echo "Local extensions preserved:"
for ext in "${LOCAL_EXTENSIONS[@]}"; do
[[ -d "$REPO_DIR/$ext" ]] && echo " - $ext"
done
echo ""
echo "Next: Review changes with 'git log --oneline -10'"
}
# Run
main "$@"