793 lines
24 KiB
Bash
Executable File
793 lines
24 KiB
Bash
Executable File
#!/bin/zsh
|
||
|
||
# Platform-specific commands
|
||
case $OSTYPE in
|
||
darwin*)
|
||
date_cmd() { gdate "$@"; }
|
||
;;
|
||
linux*)
|
||
date_cmd() { date "$@"; }
|
||
;;
|
||
esac
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Configuration
|
||
SCRIPT_DIR="${0:a:h}"
|
||
REPO_ROOT="${SCRIPT_DIR:h:h}"
|
||
LOGFILE="$REPO_ROOT/.submodule-update.log"
|
||
AI_JSON="$REPO_ROOT/ai.json"
|
||
|
||
# Usage function
|
||
usage() {
|
||
echo "Usage: $0 [--all] [--module=<name>] [--auto] [--dry-run] [--validate] [--sync] [--add=<name>] [--remove=<name>] [--help]"
|
||
echo ""
|
||
echo "Options:"
|
||
echo " --all Update all submodules"
|
||
echo " --module=<name> Update specific submodule (os, gpt, card, etc.)"
|
||
echo " --auto Auto-commit if changes detected"
|
||
echo " --dry-run Show what would be done without making changes"
|
||
echo " --validate Validate URL consistency between ai.json and .gitmodules"
|
||
echo " --sync Sync .gitmodules from ai.json configuration"
|
||
echo " --add=<name> Add new submodule from ai.json configuration"
|
||
echo " --remove=<name> Remove submodule (both from .gitmodules and filesystem)"
|
||
echo " --help Show this help message"
|
||
echo " --check-consistency Check for project inconsistencies and conflicts"
|
||
echo ""
|
||
echo "Available submodules:"
|
||
git config --file .gitmodules --get-regexp path | awk '{print " " $2}'
|
||
exit 1
|
||
}
|
||
|
||
# Logging function
|
||
log() {
|
||
local level="$1"
|
||
shift
|
||
local message="$*"
|
||
local timestamp=$(date_cmd '+%Y-%m-%d %H:%M:%S')
|
||
echo "[$timestamp] [$level] $message" | tee -a "$LOGFILE"
|
||
}
|
||
|
||
# Parse arguments
|
||
UPDATE_ALL=false
|
||
SPECIFIC_MODULE=""
|
||
AUTO_COMMIT=false
|
||
DRY_RUN=false
|
||
VALIDATE_ONLY=false
|
||
SYNC_GITMODULES=false
|
||
ADD_MODULE=""
|
||
REMOVE_MODULE=""
|
||
CHECK_CONSISTENCY=false
|
||
|
||
for arg in "$@"; do
|
||
case $arg in
|
||
--all)
|
||
UPDATE_ALL=true
|
||
;;
|
||
--module=*)
|
||
SPECIFIC_MODULE="${arg#*=}"
|
||
;;
|
||
--auto)
|
||
AUTO_COMMIT=true
|
||
;;
|
||
--dry-run)
|
||
DRY_RUN=true
|
||
;;
|
||
--validate)
|
||
VALIDATE_ONLY=true
|
||
;;
|
||
--sync)
|
||
SYNC_GITMODULES=true
|
||
;;
|
||
--add=*)
|
||
ADD_MODULE="${arg#*=}"
|
||
;;
|
||
--remove=*)
|
||
REMOVE_MODULE="${arg#*=}"
|
||
;;
|
||
--check-consistency)
|
||
CHECK_CONSISTENCY=true
|
||
;;
|
||
--help|-h)
|
||
usage
|
||
;;
|
||
*)
|
||
echo "Unknown argument: $arg"
|
||
usage
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# Validate arguments
|
||
operations_count=0
|
||
[[ "$UPDATE_ALL" == true ]] && ((operations_count++))
|
||
[[ -n "$SPECIFIC_MODULE" ]] && ((operations_count++))
|
||
[[ "$VALIDATE_ONLY" == true ]] && ((operations_count++))
|
||
[[ "$SYNC_GITMODULES" == true ]] && ((operations_count++))
|
||
[[ -n "$ADD_MODULE" ]] && ((operations_count++))
|
||
[[ -n "$REMOVE_MODULE" ]] && ((operations_count++))
|
||
[[ "$CHECK_CONSISTENCY" == true ]] && ((operations_count++))
|
||
|
||
if [[ $operations_count -eq 0 ]]; then
|
||
echo "Error: At least one operation is required"
|
||
usage
|
||
fi
|
||
|
||
if [[ $operations_count -gt 1 ]]; then
|
||
echo "Error: Only one operation can be specified at a time"
|
||
usage
|
||
fi
|
||
|
||
# Change to repository root
|
||
cd "$REPO_ROOT" || exit 1
|
||
|
||
echo -e "${BLUE}🚀 Starting submodule update...${NC}"
|
||
log "INFO" "Starting submodule update (all=$UPDATE_ALL, module=$SPECIFIC_MODULE, auto=$AUTO_COMMIT, dry-run=$DRY_RUN)"
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
echo -e "${YELLOW}🔍 DRY RUN MODE - No changes will be made${NC}"
|
||
fi
|
||
|
||
# Update function
|
||
update_submodule() {
|
||
local module_name="$1"
|
||
local module_path="$2"
|
||
|
||
echo -e "\n${BLUE}📦 Processing: $module_name${NC}"
|
||
|
||
# Check if submodule exists
|
||
if [[ ! -d "$module_path" ]]; then
|
||
echo -e "${RED}❌ Submodule directory not found: $module_path${NC}"
|
||
return 1
|
||
fi
|
||
|
||
# Get current commit
|
||
local current_commit=$(git submodule status "$module_path" | awk '{print $1}' | sed 's/^[+-]//')
|
||
|
||
# Get current branch of the submodule
|
||
cd "$module_path" || return 1
|
||
local current_branch=$(git branch --show-current)
|
||
local target_branch="${branches[$module_name]:-main}"
|
||
|
||
if [[ -z "$current_branch" ]]; then
|
||
# If not on a branch (detached HEAD), use target branch from ai.json
|
||
current_branch="$target_branch"
|
||
fi
|
||
cd "$REPO_ROOT"
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
echo -e "${YELLOW}🔍 [DRY RUN] Would update $module_name (target branch: $target_branch)${NC}"
|
||
echo " Current: $current_commit"
|
||
cd "$module_path" || return 1
|
||
git fetch origin >/dev/null 2>&1
|
||
local latest_commit=$(git rev-parse "origin/$target_branch" 2>/dev/null)
|
||
echo " Latest: $latest_commit"
|
||
cd "$REPO_ROOT"
|
||
|
||
if [[ "$current_commit" != "$latest_commit" ]]; then
|
||
echo -e "${YELLOW} 📝 Changes available${NC}"
|
||
return 0
|
||
else
|
||
echo -e "${GREEN} ✅ Already up to date${NC}"
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
# Update submodule
|
||
echo "🔄 Fetching latest changes..."
|
||
cd "$module_path" || return 1
|
||
|
||
if ! git fetch origin; then
|
||
echo -e "${RED}❌ Failed to fetch from origin${NC}"
|
||
cd "$REPO_ROOT"
|
||
return 1
|
||
fi
|
||
|
||
# Use the target branch from ai.json
|
||
local latest_commit=$(git rev-parse "origin/$target_branch" 2>/dev/null)
|
||
|
||
if [[ "$current_commit" == "$latest_commit" ]]; then
|
||
echo -e "${GREEN}✅ Already up to date${NC}"
|
||
cd "$REPO_ROOT"
|
||
return 1
|
||
fi
|
||
|
||
echo "📝 Updating to latest commit on branch $target_branch (configured in ai.json)..."
|
||
# First ensure we're on the correct branch
|
||
if ! git checkout "$target_branch"; then
|
||
echo -e "${RED}❌ Failed to checkout branch $target_branch${NC}"
|
||
cd "$REPO_ROOT"
|
||
return 1
|
||
fi
|
||
|
||
# Then pull the latest changes
|
||
if ! git pull origin "$target_branch"; then
|
||
echo -e "${RED}❌ Failed to pull latest changes from $target_branch${NC}"
|
||
cd "$REPO_ROOT"
|
||
return 1
|
||
fi
|
||
|
||
cd "$REPO_ROOT"
|
||
|
||
# Get the new commit after update
|
||
local new_commit=$(cd "$module_path" && git rev-parse HEAD)
|
||
|
||
# Stage the submodule update
|
||
git add "$module_path"
|
||
|
||
echo -e "${GREEN}✅ Updated $module_name (branch: $target_branch)${NC}"
|
||
echo " From: $current_commit"
|
||
echo " To: $new_commit"
|
||
|
||
log "INFO" "Updated $module_name on branch $target_branch: $current_commit -> $new_commit"
|
||
return 0
|
||
}
|
||
|
||
# Get list of submodules
|
||
declare -A submodules
|
||
while IFS= read -r line; do
|
||
if [[ $line =~ '^\[submodule "([^"]+)"\]' ]]; then
|
||
current_name="${match[1]}"
|
||
elif [[ $line =~ '^[[:space:]]*path[[:space:]]*=[[:space:]]*(.+)$' ]]; then
|
||
submodules[$current_name]="${match[1]}"
|
||
fi
|
||
done < .gitmodules
|
||
|
||
# Get branch information from ai.json
|
||
declare -A branches
|
||
get_branch_for_module() {
|
||
local module="$1"
|
||
local branch="main" # default branch
|
||
|
||
if [[ -f "$AI_JSON" ]]; then
|
||
# Try to extract branch from ai.json using jq
|
||
if command -v jq >/dev/null 2>&1; then
|
||
local json_branch=$(jq -r ".ai.${module}.branch // \"main\"" "$AI_JSON" 2>/dev/null)
|
||
if [[ -n "$json_branch" && "$json_branch" != "null" ]]; then
|
||
branch="$json_branch"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
echo "$branch"
|
||
}
|
||
|
||
# Get URL and branch information from ai.json
|
||
declare -A urls
|
||
|
||
get_git_config() {
|
||
local config_key="$1"
|
||
local default_value="$2"
|
||
local value=""
|
||
|
||
if [[ -f "$AI_JSON" ]]; then
|
||
if command -v jq >/dev/null 2>&1; then
|
||
value=$(jq -r ".metadata.git.${config_key} // \"${default_value}\"" "$AI_JSON" 2>/dev/null)
|
||
fi
|
||
fi
|
||
|
||
# Fallback to default if not found
|
||
if [[ -z "$value" || "$value" == "null" ]]; then
|
||
value="$default_value"
|
||
fi
|
||
|
||
echo "$value"
|
||
}
|
||
|
||
# Extract username and repo from JSON path
|
||
# For ai.gpt -> username=ai, repo=gpt
|
||
extract_git_info_from_path() {
|
||
local module="$1"
|
||
local username=""
|
||
local repo=""
|
||
|
||
# Check if module exists in ai.json structure
|
||
if [[ -f "$AI_JSON" ]]; then
|
||
if command -v jq >/dev/null 2>&1; then
|
||
# Get all keys in the ai object
|
||
local ai_keys=$(jq -r '.ai | keys[]' "$AI_JSON" 2>/dev/null)
|
||
|
||
# Find the first level (username) that contains our module
|
||
while IFS= read -r key; do
|
||
if jq -e ".ai.${key}.${module}" "$AI_JSON" >/dev/null 2>&1; then
|
||
username="$key"
|
||
repo="$module"
|
||
break
|
||
fi
|
||
# Also check if the key itself is our module (direct under ai)
|
||
if [[ "$key" == "$module" ]]; then
|
||
username="ai" # Default namespace
|
||
repo="$module"
|
||
break
|
||
fi
|
||
done <<< "$ai_keys"
|
||
fi
|
||
fi
|
||
|
||
# Fallback: assume ai namespace
|
||
if [[ -z "$username" || -z "$repo" ]]; then
|
||
username="ai"
|
||
repo="$module"
|
||
fi
|
||
|
||
echo "${username}:${repo}"
|
||
}
|
||
|
||
get_base_url_for_module() {
|
||
local module="$1"
|
||
local host=$(get_git_config "host" "git.syui.ai")
|
||
local protocol=$(get_git_config "protocol" "ssh")
|
||
|
||
# Extract username and repo from JSON structure
|
||
local git_info=$(extract_git_info_from_path "$module")
|
||
local username="${git_info%%:*}"
|
||
local repo="${git_info##*:}"
|
||
|
||
local url=""
|
||
case "$protocol" in
|
||
"ssh")
|
||
url="git@${host}:${username}/${repo}"
|
||
;;
|
||
"https")
|
||
url="https://${host}/${username}/${repo}"
|
||
;;
|
||
*)
|
||
# Default to ssh
|
||
url="git@${host}:${username}/${repo}"
|
||
;;
|
||
esac
|
||
|
||
echo "$url"
|
||
}
|
||
|
||
get_url_for_module() {
|
||
local module="$1"
|
||
local url=$(get_base_url_for_module "$module")
|
||
|
||
# Check if there's a custom git_url override in ai.json
|
||
if [[ -f "$AI_JSON" ]]; then
|
||
if command -v jq >/dev/null 2>&1; then
|
||
local custom_url=$(jq -r ".ai.${module}.git_url // empty" "$AI_JSON" 2>/dev/null)
|
||
if [[ -n "$custom_url" && "$custom_url" != "null" ]]; then
|
||
url="$custom_url"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
echo "$url"
|
||
}
|
||
|
||
# Populate branches and URLs for all modules
|
||
for module in "${(k)submodules[@]}"; do
|
||
branches[$module]=$(get_branch_for_module "$module")
|
||
urls[$module]=$(get_url_for_module "$module")
|
||
done
|
||
|
||
# Log git configuration for debugging
|
||
log "INFO" "Git host: $(get_git_config 'host' 'git.syui.ai'), protocol: $(get_git_config 'protocol' 'ssh')"
|
||
|
||
# Debug: Log found submodules
|
||
log "INFO" "Found ${#submodules} submodules: ${(k)submodules[@]}"
|
||
|
||
# Validation function
|
||
validate_url_consistency() {
|
||
echo -e "${BLUE}🔍 Validating URL consistency between ai.json and .gitmodules...${NC}"
|
||
|
||
local inconsistencies=0
|
||
|
||
for module in "${(k)submodules[@]}"; do
|
||
local gitmodules_url=$(git config --file .gitmodules --get "submodule.${module}.url")
|
||
local ai_json_url="${urls[$module]}"
|
||
|
||
if [[ -n "$ai_json_url" ]]; then
|
||
if [[ "$gitmodules_url" != "$ai_json_url" ]]; then
|
||
echo -e "${RED}❌ $module: URL mismatch${NC}"
|
||
echo " .gitmodules: $gitmodules_url"
|
||
echo " ai.json: $ai_json_url"
|
||
((inconsistencies++))
|
||
else
|
||
echo -e "${GREEN}✅ $module: URLs match${NC}"
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}⚠️ $module: No git_url in ai.json${NC}"
|
||
fi
|
||
done
|
||
|
||
echo ""
|
||
if [[ $inconsistencies -eq 0 ]]; then
|
||
echo -e "${GREEN}🎉 All URLs are consistent!${NC}"
|
||
log "INFO" "URL validation passed: all URLs consistent"
|
||
else
|
||
echo -e "${RED}❌ Found $inconsistencies URL inconsistencies${NC}"
|
||
echo "Run with --sync to fix inconsistencies"
|
||
log "ERROR" "URL validation failed: $inconsistencies inconsistencies"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Sync .gitmodules from ai.json
|
||
sync_gitmodules() {
|
||
echo -e "${BLUE}🔄 Syncing .gitmodules from ai.json...${NC}"
|
||
|
||
local changes=0
|
||
|
||
for module in "${(k)submodules[@]}"; do
|
||
local ai_json_url="${urls[$module]}"
|
||
local ai_json_branch="${branches[$module]}"
|
||
|
||
if [[ -n "$ai_json_url" ]]; then
|
||
local current_url=$(git config --file .gitmodules --get "submodule.${module}.url")
|
||
|
||
if [[ "$current_url" != "$ai_json_url" ]]; then
|
||
echo "📝 Updating $module URL: $current_url -> $ai_json_url"
|
||
git config --file .gitmodules "submodule.${module}.url" "$ai_json_url"
|
||
((changes++))
|
||
fi
|
||
fi
|
||
done
|
||
|
||
if [[ $changes -gt 0 ]]; then
|
||
echo -e "${GREEN}✅ Updated $changes URL(s) in .gitmodules${NC}"
|
||
log "INFO" "Synced .gitmodules: updated $changes URLs"
|
||
|
||
if [[ "$AUTO_COMMIT" == true ]]; then
|
||
git add .gitmodules
|
||
git commit -m "Sync .gitmodules URLs from ai.json
|
||
|
||
🔄 Updated $changes submodule URL(s)
|
||
|
||
🤖 Generated with submodule sync"
|
||
echo -e "${GREEN}✅ Changes committed to .gitmodules${NC}"
|
||
fi
|
||
else
|
||
echo -e "${GREEN}✅ .gitmodules is already in sync${NC}"
|
||
fi
|
||
}
|
||
|
||
# Add new submodule
|
||
add_submodule() {
|
||
local module="$1"
|
||
|
||
echo -e "${BLUE}➕ Adding submodule: $module${NC}"
|
||
|
||
local ai_json_url=$(get_url_for_module "$module")
|
||
local ai_json_branch=$(get_branch_for_module "$module")
|
||
|
||
if [[ -z "$ai_json_url" ]]; then
|
||
echo -e "${RED}❌ No git_url found for '$module' in ai.json${NC}"
|
||
return 1
|
||
fi
|
||
|
||
if [[ -d "$module" ]]; then
|
||
echo -e "${RED}❌ Directory '$module' already exists${NC}"
|
||
return 1
|
||
fi
|
||
|
||
echo "📦 Adding submodule $module"
|
||
echo " URL: $ai_json_url"
|
||
echo " Branch: $ai_json_branch"
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
echo -e "${YELLOW}🔍 [DRY RUN] Would add submodule${NC}"
|
||
return 0
|
||
fi
|
||
|
||
if git submodule add -b "$ai_json_branch" "$ai_json_url" "$module"; then
|
||
echo -e "${GREEN}✅ Successfully added submodule $module${NC}"
|
||
log "INFO" "Added submodule $module from $ai_json_url (branch: $ai_json_branch)"
|
||
|
||
if [[ "$AUTO_COMMIT" == true ]]; then
|
||
git commit -m "Add submodule: $module
|
||
|
||
📦 Added from ai.json configuration
|
||
🌐 URL: $ai_json_url
|
||
🌿 Branch: $ai_json_branch
|
||
|
||
🤖 Generated with submodule manager"
|
||
echo -e "${GREEN}✅ Submodule addition committed${NC}"
|
||
fi
|
||
else
|
||
echo -e "${RED}❌ Failed to add submodule $module${NC}"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Remove submodule
|
||
remove_submodule() {
|
||
local module="$1"
|
||
|
||
echo -e "${BLUE}➖ Removing submodule: $module${NC}"
|
||
|
||
if [[ ! -d "$module" ]]; then
|
||
echo -e "${RED}❌ Submodule '$module' does not exist${NC}"
|
||
return 1
|
||
fi
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
echo -e "${YELLOW}🔍 [DRY RUN] Would remove submodule${NC}"
|
||
return 0
|
||
fi
|
||
|
||
# Remove from .gitmodules
|
||
git config --file .gitmodules --remove-section "submodule.$module" 2>/dev/null || true
|
||
|
||
# Remove from .git/config
|
||
git config --remove-section "submodule.$module" 2>/dev/null || true
|
||
|
||
# Remove from git index
|
||
git rm --cached "$module" 2>/dev/null || true
|
||
|
||
# Remove directory
|
||
rm -rf "$module"
|
||
|
||
# Remove from .git/modules
|
||
rm -rf ".git/modules/$module"
|
||
|
||
echo -e "${GREEN}✅ Successfully removed submodule $module${NC}"
|
||
log "INFO" "Removed submodule $module"
|
||
|
||
if [[ "$AUTO_COMMIT" == true ]]; then
|
||
git add .gitmodules "$module" 2>/dev/null || true
|
||
git commit -m "Remove submodule: $module
|
||
|
||
🗑️ Completely removed submodule
|
||
|
||
🤖 Generated with submodule manager"
|
||
echo -e "${GREEN}✅ Submodule removal committed${NC}"
|
||
fi
|
||
}
|
||
|
||
# Consistency check function
|
||
check_project_consistency() {
|
||
echo -e "${BLUE}🔍 Checking project consistency...${NC}"
|
||
|
||
local issues=0
|
||
local warnings=0
|
||
|
||
# Check for uncommitted changes in submodules
|
||
echo -e "${BLUE}📝 Checking for uncommitted changes...${NC}"
|
||
for module in "${(k)submodules[@]}"; do
|
||
local module_path="${submodules[$module]}"
|
||
|
||
if [[ -d "$module_path" ]]; then
|
||
cd "$module_path"
|
||
|
||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||
echo -e "${YELLOW}⚠️ $module: Has uncommitted changes${NC}"
|
||
git status --short | sed 's/^/ /'
|
||
((warnings++))
|
||
else
|
||
echo -e "${GREEN}✅ $module: Clean working directory${NC}"
|
||
fi
|
||
|
||
cd "$REPO_ROOT"
|
||
fi
|
||
done
|
||
|
||
# Check for branch inconsistencies
|
||
echo -e "${BLUE}🌿 Checking branch consistency...${NC}"
|
||
for module in "${(k)submodules[@]}"; do
|
||
local module_path="${submodules[$module]}"
|
||
local expected_branch="${branches[$module]}"
|
||
|
||
if [[ -d "$module_path" ]]; then
|
||
cd "$module_path"
|
||
|
||
local current_branch=$(git branch --show-current)
|
||
if [[ -z "$current_branch" ]]; then
|
||
current_branch="(detached HEAD)"
|
||
fi
|
||
|
||
if [[ "$current_branch" != "$expected_branch" ]]; then
|
||
echo -e "${YELLOW}⚠️ $module: Branch mismatch${NC}"
|
||
echo " Current: $current_branch"
|
||
echo " Expected: $expected_branch"
|
||
((warnings++))
|
||
else
|
||
echo -e "${GREEN}✅ $module: Correct branch ($current_branch)${NC}"
|
||
fi
|
||
|
||
cd "$REPO_ROOT"
|
||
fi
|
||
done
|
||
|
||
# Check for remote synchronization
|
||
echo -e "${BLUE}🌐 Checking remote synchronization...${NC}"
|
||
for module in "${(k)submodules[@]}"; do
|
||
local module_path="${submodules[$module]}"
|
||
|
||
if [[ -d "$module_path" ]]; then
|
||
cd "$module_path"
|
||
|
||
git fetch --quiet 2>/dev/null || true
|
||
|
||
local ahead=$(git rev-list --count HEAD ^origin/HEAD 2>/dev/null || echo "0")
|
||
local behind=$(git rev-list --count origin/HEAD ^HEAD 2>/dev/null || echo "0")
|
||
|
||
if [[ "$ahead" -gt 0 || "$behind" -gt 0 ]]; then
|
||
echo -e "${YELLOW}⚠️ $module: Out of sync with remote${NC}"
|
||
if [[ "$ahead" -gt 0 ]]; then
|
||
echo " $ahead commits ahead"
|
||
fi
|
||
if [[ "$behind" -gt 0 ]]; then
|
||
echo " $behind commits behind"
|
||
fi
|
||
((warnings++))
|
||
else
|
||
echo -e "${GREEN}✅ $module: Synchronized with remote${NC}"
|
||
fi
|
||
|
||
cd "$REPO_ROOT"
|
||
fi
|
||
done
|
||
|
||
# Check parent repository status
|
||
echo -e "${BLUE}🏠 Checking parent repository...${NC}"
|
||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||
echo -e "${YELLOW}⚠️ Parent repository has uncommitted changes${NC}"
|
||
git status --short | sed 's/^/ /'
|
||
((warnings++))
|
||
else
|
||
echo -e "${GREEN}✅ Parent repository is clean${NC}"
|
||
fi
|
||
|
||
# Check for submodule pointer mismatches
|
||
echo -e "${BLUE}🔗 Checking submodule pointers...${NC}"
|
||
for module in "${(k)submodules[@]}"; do
|
||
local module_path="${submodules[$module]}"
|
||
|
||
if [[ -d "$module_path" ]]; then
|
||
local submodule_commit=$(git ls-tree HEAD "$module_path" | awk '{print $3}')
|
||
local actual_commit=$(cd "$module_path" && git rev-parse HEAD)
|
||
|
||
if [[ "$submodule_commit" != "$actual_commit" ]]; then
|
||
echo -e "${YELLOW}⚠️ $module: Submodule pointer mismatch${NC}"
|
||
echo " Pointer: $submodule_commit"
|
||
echo " Actual: $actual_commit"
|
||
((warnings++))
|
||
else
|
||
echo -e "${GREEN}✅ $module: Submodule pointer correct${NC}"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# Summary
|
||
echo ""
|
||
echo -e "${BLUE}📊 Consistency Check Summary:${NC}"
|
||
echo " 📦 Modules checked: ${#submodules}"
|
||
|
||
if [[ $issues -eq 0 && $warnings -eq 0 ]]; then
|
||
echo -e "${GREEN} ✅ No issues found${NC}"
|
||
echo -e "${GREEN}🎉 All projects are consistent!${NC}"
|
||
log "INFO" "Consistency check passed: no issues found"
|
||
return 0
|
||
else
|
||
if [[ $issues -gt 0 ]]; then
|
||
echo -e "${RED} ❌ Issues: $issues${NC}"
|
||
fi
|
||
if [[ $warnings -gt 0 ]]; then
|
||
echo -e "${YELLOW} ⚠️ Warnings: $warnings${NC}"
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${BLUE}💡 Recommended actions:${NC}"
|
||
echo " • Commit uncommitted changes in affected modules"
|
||
echo " • Switch to expected branches where needed"
|
||
echo " • Run: ./claude/scripts/update-submodules.sh --all --auto"
|
||
echo " • Use session-end.sh for proper session cleanup"
|
||
|
||
log "WARNING" "Consistency check found $issues issues and $warnings warnings"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Handle special operations
|
||
if [[ "$VALIDATE_ONLY" == true ]]; then
|
||
validate_url_consistency
|
||
exit $?
|
||
fi
|
||
|
||
if [[ "$SYNC_GITMODULES" == true ]]; then
|
||
sync_gitmodules
|
||
exit 0
|
||
fi
|
||
|
||
if [[ -n "$ADD_MODULE" ]]; then
|
||
add_submodule "$ADD_MODULE"
|
||
exit $?
|
||
fi
|
||
|
||
if [[ -n "$REMOVE_MODULE" ]]; then
|
||
remove_submodule "$REMOVE_MODULE"
|
||
exit $?
|
||
fi
|
||
|
||
if [[ "$CHECK_CONSISTENCY" == true ]]; then
|
||
check_project_consistency
|
||
exit $?
|
||
fi
|
||
|
||
# Main execution
|
||
success_count=0
|
||
total_count=0
|
||
|
||
if [[ "$UPDATE_ALL" == true ]]; then
|
||
echo -e "${BLUE}📚 Updating all submodules...${NC}"
|
||
|
||
for module_name in "${(k)submodules[@]}"; do
|
||
module_path="${submodules[$module_name]}"
|
||
((total_count++))
|
||
|
||
if update_submodule "$module_name" "$module_path"; then
|
||
((success_count++))
|
||
fi
|
||
done
|
||
else
|
||
echo -e "${BLUE}📖 Updating submodule: $SPECIFIC_MODULE${NC}"
|
||
|
||
if [[ -z "${submodules[$SPECIFIC_MODULE]}" ]]; then
|
||
echo -e "${RED}❌ Submodule '$SPECIFIC_MODULE' not found${NC}"
|
||
echo "Available submodules:"
|
||
for name in "${(k)submodules[@]}"; do
|
||
echo " - $name (path: ${submodules[$name]})"
|
||
done
|
||
exit 1
|
||
fi
|
||
|
||
module_path="${submodules[$SPECIFIC_MODULE]}"
|
||
((total_count++))
|
||
|
||
if update_submodule "$SPECIFIC_MODULE" "$module_path"; then
|
||
((success_count++))
|
||
fi
|
||
fi
|
||
|
||
# Summary
|
||
echo ""
|
||
echo -e "${BLUE}📊 Summary:${NC}"
|
||
echo " 📦 Modules processed: $total_count"
|
||
echo " ✅ Updates applied: $success_count"
|
||
echo " 📝 No changes: $((total_count - success_count))"
|
||
|
||
if [[ "$DRY_RUN" == true ]]; then
|
||
echo ""
|
||
echo -e "${YELLOW}🔍 This was a dry run. To apply changes, run without --dry-run${NC}"
|
||
exit 0
|
||
fi
|
||
|
||
# Auto-commit if requested and changes exist
|
||
if [[ "$success_count" -gt 0 ]]; then
|
||
if [[ "$AUTO_COMMIT" == true ]]; then
|
||
echo ""
|
||
echo -e "${BLUE}💾 Auto-committing submodule updates...${NC}"
|
||
|
||
commit_message="Update submodules
|
||
|
||
📦 Updated modules: $success_count/$total_count
|
||
$(git diff --cached --name-only | sed 's/^/- /')
|
||
|
||
🤖 Generated with submodule auto-update
|
||
$(date_cmd '+%Y-%m-%d %H:%M:%S')"
|
||
|
||
if git commit -m "$commit_message"; then
|
||
echo -e "${GREEN}✅ Changes committed successfully${NC}"
|
||
log "INFO" "Auto-committed $success_count submodule updates"
|
||
else
|
||
echo -e "${RED}❌ Failed to commit changes${NC}"
|
||
log "ERROR" "Failed to auto-commit submodule updates"
|
||
fi
|
||
else
|
||
echo ""
|
||
echo -e "${YELLOW}📝 Changes staged but not committed. Run 'git commit' to commit them.${NC}"
|
||
echo "Or use --auto flag to commit automatically."
|
||
fi
|
||
fi
|
||
|
||
echo ""
|
||
echo -e "${GREEN}🎉 Submodule update completed!${NC}"
|
||
log "INFO" "Submodule update completed: $success_count/$total_count updated" |