mirror of
https://github.com/sstent/MyUtils.git
synced 2025-12-06 00:11:35 +00:00
484 lines
18 KiB
Bash
Executable File
484 lines
18 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Script to audit repositories against .mrconfig and check for Keybase remotes
|
|
# Requires myrepos (mr) to be installed
|
|
# Usage: ./audit_repos.sh [options] [directory]
|
|
|
|
set -e
|
|
|
|
# Color codes for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Options
|
|
FIX_MRCONFIG=false
|
|
FIX_UNTRACKED_ONLY=false
|
|
FIX_NONGIT_ONLY=false
|
|
INTERACTIVE=true
|
|
DRY_RUN=false
|
|
IGNORE_PATTERNS=("_archive") # Default ignore patterns
|
|
|
|
# Parse command line arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--fix-all)
|
|
FIX_MRCONFIG=true
|
|
shift
|
|
;;
|
|
--fix-untracked)
|
|
FIX_UNTRACKED_ONLY=true
|
|
FIX_MRCONFIG=true
|
|
shift
|
|
;;
|
|
--fix-nongit)
|
|
FIX_NONGIT_ONLY=true
|
|
FIX_MRCONFIG=true
|
|
shift
|
|
;;
|
|
-y|--yes)
|
|
INTERACTIVE=false
|
|
shift
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
--ignore)
|
|
if [[ -n "$2" && ! "$2" =~ ^-- ]]; then
|
|
IGNORE_PATTERNS+=("$2")
|
|
shift 2
|
|
else
|
|
echo "Error: --ignore requires a pattern argument"
|
|
exit 1
|
|
fi
|
|
;;
|
|
-h|--help)
|
|
echo "Usage: $0 [options] [directory]"
|
|
echo ""
|
|
echo "Audits git repositories and manages them with myrepos (mr) and Keybase backup."
|
|
echo ""
|
|
echo "This script will:"
|
|
echo " - Check all subdirectories in the target directory"
|
|
echo " - Identify which are git repos and which are tracked in .mrconfig"
|
|
echo " - Verify Keybase remotes exist for backup"
|
|
echo " - Optionally initialize non-git directories and register everything with mr"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --fix-all Add untracked repos to .mrconfig and initialize non-git dirs"
|
|
echo " --fix-untracked Only add untracked git repos to .mrconfig"
|
|
echo " --fix-nongit Only initialize non-git directories and add to .mrconfig"
|
|
echo " --dry-run Show what would be done without making changes"
|
|
echo " --ignore PATTERN Skip directories matching PATTERN (supports globs)"
|
|
echo " Default: _archive is always ignored"
|
|
echo " -y, --yes Non-interactive mode (auto-confirm all actions)"
|
|
echo " -h, --help Show this help message"
|
|
echo "Examples:"
|
|
echo " $0 # Audit current directory"
|
|
echo " $0 ~/Projects # Audit specific directory"
|
|
echo " $0 --fix-all # Add untracked repos and init non-git dirs"
|
|
echo " $0 --fix-all -y # Fix everything without prompting"
|
|
echo " $0 --fix-all --dry-run # Preview what would be changed"
|
|
echo " $0 --ignore tmp --ignore '*_backup' # Ignore additional patterns"
|
|
echo ""
|
|
echo "Workflow:"
|
|
echo " 1. Run without --fix-all to see what needs attention"
|
|
echo " 2. Run with --fix-all to register repos and initialize directories"
|
|
echo " 3. Run 'mr fixups' to set up Keybase remotes for all repos"
|
|
echo " 4. Use 'mr update' to keep all repositories synchronized"
|
|
echo ""
|
|
echo "Requirements:"
|
|
echo " - myrepos (mr) must be installed"
|
|
echo " - keybase (optional but recommended for backup)"
|
|
exit 0
|
|
;;
|
|
*)
|
|
TARGET_DIR="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Check if mr is installed
|
|
if ! command -v mr &> /dev/null; then
|
|
echo -e "${RED}Error: 'mr' (myrepos) is not installed.${NC}"
|
|
echo "Install it with: apt-get install myrepos (or your package manager)"
|
|
exit 1
|
|
fi
|
|
|
|
# Default to current directory if none specified
|
|
TARGET_DIR="${TARGET_DIR:-.}"
|
|
MRCONFIG="${HOME}/.mrconfig"
|
|
|
|
echo "=== Repository Audit ==="
|
|
echo "Directory: ${TARGET_DIR}"
|
|
echo "Config: ${MRCONFIG}"
|
|
if [[ ${#IGNORE_PATTERNS[@]} -gt 0 ]]; then
|
|
echo "Ignoring: ${IGNORE_PATTERNS[*]}"
|
|
fi
|
|
echo ""
|
|
|
|
# Check if .mrconfig exists
|
|
if [[ ! -f "${MRCONFIG}" ]]; then
|
|
echo -e "${YELLOW}No .mrconfig found. Creating new one...${NC}"
|
|
touch "${MRCONFIG}"
|
|
fi
|
|
|
|
# Function to ensure DEFAULT section with Keybase fixups exists
|
|
ensure_default_fixups() {
|
|
if grep -q '^\[DEFAULT\]' "${MRCONFIG}"; then
|
|
# Check if fixups already exists in DEFAULT section
|
|
if sed -n '/^\[DEFAULT\]/,/^\[/p' "${MRCONFIG}" | grep -q '^fixups'; then
|
|
echo -e "${GREEN}✓ DEFAULT fixups already configured${NC}"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
echo -e "${BLUE}=== [DRY RUN] Would Add DEFAULT Keybase Fixups ===${NC}"
|
|
echo ""
|
|
echo "Would add to .mrconfig:"
|
|
echo "[DEFAULT]"
|
|
echo "fixups = "
|
|
echo " REPO_NAME=\$(basename \"\$MR_REPO\")"
|
|
echo " KEYBASE_USER=\$(keybase whoami 2>/dev/null)"
|
|
echo " if [ -z \"\$KEYBASE_USER\" ]; then"
|
|
echo " echo \"Keybase not available, skipping\""
|
|
echo " exit 0"
|
|
echo " fi"
|
|
echo " "
|
|
echo " # Check if keybase remote exists"
|
|
echo " if ! git config remote.keybase.url > /dev/null; then"
|
|
echo " # No keybase remote, add it"
|
|
echo " keybase git create \"\$REPO_NAME\" 2>/dev/null || true"
|
|
echo " git remote add keybase \"keybase://private/\${KEYBASE_USER}/\${REPO_NAME}\""
|
|
echo " git push keybase --all 2>/dev/null || true"
|
|
echo " git push keybase --tags 2>/dev/null || true"
|
|
echo " fi"
|
|
echo " "
|
|
echo " # If no origin remote exists, set keybase as the default push remote"
|
|
echo " if ! git config remote.origin.url > /dev/null; then"
|
|
echo " git config remote.pushDefault keybase 2>/dev/null || true"
|
|
echo " fi"
|
|
echo ""
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${YELLOW}No DEFAULT fixups found in .mrconfig${NC}"
|
|
|
|
if [[ "$INTERACTIVE" == true ]]; then
|
|
read -p "Add DEFAULT section with Keybase fixups to .mrconfig? [y/N] " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Create a temporary file with the DEFAULT section
|
|
TEMP_FILE=$(mktemp)
|
|
|
|
# Add DEFAULT section at the top
|
|
# IMPORTANT: mr requires literal tab characters for continuation lines
|
|
# The format is: key = <TAB><newline><TAB>line1<newline><TAB>line2...
|
|
printf '[DEFAULT]\n' > "$TEMP_FILE"
|
|
printf 'fixups =\t\n' >> "$TEMP_FILE"
|
|
printf '\tREPO_NAME=$(basename "$MR_REPO")\n' >> "$TEMP_FILE"
|
|
printf '\tKEYBASE_USER=$(keybase whoami 2>/dev/null)\n' >> "$TEMP_FILE"
|
|
printf '\tif [ -z "$KEYBASE_USER" ]; then\n' >> "$TEMP_FILE"
|
|
printf '\t\techo "Keybase not available, skipping"\n' >> "$TEMP_FILE"
|
|
printf '\t\texit 0\n' >> "$TEMP_FILE"
|
|
printf '\tfi\n' >> "$TEMP_FILE"
|
|
printf '\tif ! git config remote.keybase.url > /dev/null; then\n' >> "$TEMP_FILE"
|
|
printf '\t\tkeybase git create "$REPO_NAME" 2>/dev/null || true\n' >> "$TEMP_FILE"
|
|
printf '\t\tgit remote add keybase "keybase://private/${KEYBASE_USER}/${REPO_NAME}"\n' >> "$TEMP_FILE"
|
|
printf '\t\tgit push keybase --all 2>/dev/null || true\n' >> "$TEMP_FILE"
|
|
printf '\t\tgit push keybase --tags 2>/dev/null || true\n' >> "$TEMP_FILE"
|
|
printf '\tfi\n' >> "$TEMP_FILE"
|
|
printf '\tif ! git config remote.origin.url > /dev/null; then\n' >> "$TEMP_FILE"
|
|
printf '\t\tgit config remote.pushDefault keybase 2>/dev/null || true\n' >> "$TEMP_FILE"
|
|
printf '\tfi\n' >> "$TEMP_FILE"
|
|
printf '\n' >> "$TEMP_FILE"
|
|
|
|
# Append existing config if it's not empty
|
|
if [[ -s "${MRCONFIG}" ]]; then
|
|
cat "${MRCONFIG}" >> "$TEMP_FILE"
|
|
fi
|
|
|
|
# Replace original config
|
|
mv "$TEMP_FILE" "${MRCONFIG}"
|
|
|
|
echo -e "${GREEN}✓ Added DEFAULT section with Keybase fixups${NC}"
|
|
echo -e "${BLUE}Note: Run 'mr fixups' after adding repos to set up Keybase remotes${NC}"
|
|
return 0
|
|
}
|
|
|
|
# Parse .mrconfig to get tracked repositories
|
|
declare -A tracked_repos
|
|
if [[ -s "${MRCONFIG}" ]]; then
|
|
while IFS= read -r line; do
|
|
if [[ $line =~ ^\[([^]]+)\]$ ]]; then
|
|
repo_path="${BASH_REMATCH[1]}"
|
|
# Skip DEFAULT section
|
|
if [[ "$repo_path" != "DEFAULT" ]]; then
|
|
tracked_repos["$repo_path"]=1
|
|
fi
|
|
fi
|
|
done < "${MRCONFIG}"
|
|
fi
|
|
|
|
echo "Found ${#tracked_repos[@]} repositories in .mrconfig"
|
|
echo ""
|
|
|
|
# Check all top-level subdirectories
|
|
untracked_dirs=()
|
|
missing_keybase=()
|
|
has_keybase=()
|
|
non_git_dirs=()
|
|
|
|
for dir in "${TARGET_DIR}"/*/ ; do
|
|
# Skip if not a directory
|
|
[[ ! -d "$dir" ]] && continue
|
|
|
|
# Get directory name relative to TARGET_DIR
|
|
dir_name=$(basename "$dir")
|
|
|
|
# Skip hidden directories
|
|
[[ "$dir_name" =~ ^\. ]] && continue
|
|
|
|
# Check if directory matches any ignore pattern
|
|
skip_dir=false
|
|
for pattern in "${IGNORE_PATTERNS[@]}"; do
|
|
if [[ "$dir_name" == $pattern ]]; then
|
|
skip_dir=true
|
|
break
|
|
fi
|
|
done
|
|
[[ "$skip_dir" == true ]] && continue
|
|
|
|
# Get absolute path
|
|
abs_path=$(cd "$dir" && pwd)
|
|
|
|
# Check if it's a git repository
|
|
if [[ ! -d "${dir}/.git" ]]; then
|
|
non_git_dirs+=("$abs_path")
|
|
continue
|
|
fi
|
|
|
|
# Check if tracked in .mrconfig
|
|
is_tracked=false
|
|
for tracked_path in "${!tracked_repos[@]}"; do
|
|
# Expand ~ in tracked path
|
|
expanded_path="${tracked_path/#\~/$HOME}"
|
|
|
|
# Convert relative paths to absolute if needed
|
|
if [[ ! "$expanded_path" =~ ^/ ]]; then
|
|
# Relative path - try expanding from home
|
|
expanded_path="${HOME}/${expanded_path}"
|
|
fi
|
|
|
|
# Compare absolute paths
|
|
if [[ "$expanded_path" == "$abs_path" ]]; then
|
|
is_tracked=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ "$is_tracked" == false ]]; then
|
|
untracked_dirs+=("$abs_path")
|
|
fi
|
|
|
|
# Check for Keybase remote
|
|
cd "$dir"
|
|
remotes=$(git remote -v 2>/dev/null || echo "")
|
|
has_keybase_remote=false
|
|
|
|
while IFS= read -r remote; do
|
|
if [[ $remote =~ keybase:// ]]; then
|
|
has_keybase_remote=true
|
|
break
|
|
fi
|
|
done <<< "$remotes"
|
|
|
|
if [[ "$has_keybase_remote" == true ]]; then
|
|
has_keybase+=("$abs_path")
|
|
else
|
|
missing_keybase+=("$abs_path")
|
|
fi
|
|
|
|
cd - > /dev/null
|
|
done
|
|
|
|
# Report results
|
|
echo "=== Results ==="
|
|
echo ""
|
|
|
|
if [[ ${#non_git_dirs[@]} -gt 0 ]]; then
|
|
echo -e "${YELLOW}! Non-git directories (${#non_git_dirs[@]}):${NC}"
|
|
for dir in "${non_git_dirs[@]}"; do
|
|
echo -e " ${YELLOW}- ${dir}${NC}"
|
|
done
|
|
echo -e "${YELLOW} (These will be initialized as git repos when using --fix-all)${NC}"
|
|
echo ""
|
|
fi
|
|
|
|
if [[ ${#untracked_dirs[@]} -eq 0 ]]; then
|
|
echo -e "${GREEN}✓ All git repositories are tracked in .mrconfig${NC}"
|
|
else
|
|
echo -e "${RED}✗ Untracked repositories (${#untracked_dirs[@]}):${NC}"
|
|
for dir in "${untracked_dirs[@]}"; do
|
|
echo -e " ${YELLOW}- ${dir}${NC}"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
|
|
if [[ ${#missing_keybase[@]} -eq 0 ]]; then
|
|
echo -e "${GREEN}✓ All repositories have Keybase remotes${NC}"
|
|
else
|
|
echo -e "${RED}✗ Repositories missing Keybase remotes (${#missing_keybase[@]}):${NC}"
|
|
for dir in "${missing_keybase[@]}"; do
|
|
echo -e " ${YELLOW}- ${dir}${NC}"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
echo "=== Summary ==="
|
|
echo "Non-git directories: ${#non_git_dirs[@]}"
|
|
echo "Tracked in .mrconfig: $((${#has_keybase[@]} + ${#missing_keybase[@]} - ${#untracked_dirs[@]}))"
|
|
echo "Not tracked: ${#untracked_dirs[@]}"
|
|
echo "Has Keybase remote: ${#has_keybase[@]}"
|
|
echo "Missing Keybase remote: ${#missing_keybase[@]}"
|
|
echo ""
|
|
|
|
# Ensure DEFAULT fixups exist if we're doing any fixes or have missing Keybase remotes
|
|
if ([[ "$FIX_MRCONFIG" == true ]] && [[ ${#untracked_dirs[@]} -gt 0 ]]) || [[ ${#missing_keybase[@]} -gt 0 ]]; then
|
|
echo "=== Checking .mrconfig DEFAULT Configuration ==="
|
|
ensure_default_fixups
|
|
echo ""
|
|
fi
|
|
|
|
# Fix untracked repositories
|
|
if [[ "$FIX_MRCONFIG" == true ]] && [[ ${#untracked_dirs[@]} -gt 0 ]]; then
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
echo -e "${BLUE}=== [DRY RUN] Would Add to .mrconfig ===${NC}"
|
|
|
|
for repo_path in "${untracked_dirs[@]}"; do
|
|
repo_name=$(basename "$repo_path")
|
|
echo -e "${BLUE}Would register:${NC} $repo_name"
|
|
echo " Path: $repo_path"
|
|
echo " Command: mr register"
|
|
echo " (Keybase fixups handled by DEFAULT section)"
|
|
echo ""
|
|
done
|
|
echo ""
|
|
else
|
|
echo -e "${BLUE}=== Adding Repositories to .mrconfig ===${NC}"
|
|
|
|
for repo_path in "${untracked_dirs[@]}"; do
|
|
repo_name=$(basename "$repo_path")
|
|
|
|
if [[ "$INTERACTIVE" == true ]]; then
|
|
read -p "Register $repo_name with mr? [y/N] " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
echo "Skipped $repo_name"
|
|
continue
|
|
fi
|
|
fi
|
|
|
|
echo -e "${BLUE}Registering $repo_name...${NC}"
|
|
|
|
cd "$repo_path"
|
|
|
|
# Check if repo has an origin remote for mr register
|
|
if ! git config --get remote.origin.url > /dev/null 2>&1; then
|
|
# No origin, check if it has keybase
|
|
if git config --get remote.keybase.url > /dev/null 2>&1; then
|
|
keybase_url=$(git config --get remote.keybase.url)
|
|
echo -e "${YELLOW}No origin remote found, using keybase URL for registration${NC}"
|
|
# Temporarily set origin to keybase for mr register
|
|
git remote add origin "$keybase_url" 2>/dev/null || git remote set-url origin "$keybase_url"
|
|
mr register 2>/dev/null
|
|
# Remove the temporary origin
|
|
git remote remove origin 2>/dev/null
|
|
echo -e "${GREEN}✓ Registered $repo_name (Keybase-only repo)${NC}"
|
|
else
|
|
# No remotes at all - local only repo
|
|
echo -e "${YELLOW}No remotes found - local-only repository${NC}"
|
|
|
|
# Get Keybase username if available
|
|
if command -v keybase &> /dev/null; then
|
|
KEYBASE_USER=$(keybase whoami 2>/dev/null || echo "")
|
|
if [[ -n "$KEYBASE_USER" ]]; then
|
|
keybase_url="keybase://private/${KEYBASE_USER}/${repo_name}"
|
|
echo -e "${BLUE}Creating Keybase remote for local repo...${NC}"
|
|
|
|
# Create keybase repo and add as origin for mr register
|
|
keybase git create "$repo_name" 2>/dev/null || true
|
|
git remote add origin "$keybase_url"
|
|
|
|
if mr register 2>/dev/null; then
|
|
# Rename origin to keybase after registration
|
|
git remote rename origin keybase
|
|
git config remote.pushDefault keybase 2>/dev/null || true
|
|
|
|
# Push to keybase
|
|
echo "Pushing to Keybase..."
|
|
git push keybase --all 2>/dev/null || true
|
|
git push keybase --tags 2>/dev/null || true
|
|
|
|
echo -e "${GREEN}✓ Registered $repo_name (local repo, now backed up to Keybase)${NC}"
|
|
else
|
|
git remote remove origin
|
|
echo -e "${RED}✗ Failed to register $repo_name${NC}"
|
|
fi
|
|
else
|
|
echo -e "${YELLOW}Keybase not available. Skipping registration of local-only repo.${NC}"
|
|
echo -e "${YELLOW}Install and login to Keybase, or add a remote manually first.${NC}"
|
|
fi
|
|
else
|
|
echo -e "${YELLOW}Keybase not available. Skipping registration of local-only repo.${NC}"
|
|
echo -e "${YELLOW}Install Keybase or add a remote manually first.${NC}"
|
|
fi
|
|
fi
|
|
else
|
|
# Normal registration with origin
|
|
if mr register 2>/dev/null; then
|
|
echo -e "${GREEN}✓ Registered $repo_name${NC}"
|
|
else
|
|
echo -e "${RED}✗ Failed to register $repo_name${NC}"
|
|
fi
|
|
fi
|
|
cd - > /dev/null
|
|
done
|
|
echo ""
|
|
|
|
if [[ ${#untracked_dirs[@]} -gt 0 ]]; then
|
|
echo -e "${BLUE}=== Next Steps ===${NC}"
|
|
echo "Run the following commands to set up Keybase remotes:"
|
|
echo " mr fixups # Add Keybase remotes to all repos"
|
|
echo ""
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Provide guidance for missing Keybase remotes
|
|
if [[ ${#missing_keybase[@]} -gt 0 ]] && [[ "$FIX_MRCONFIG" == false ]]; then
|
|
echo -e "${YELLOW}Tip: Run 'mr fixups' to add Keybase remotes to existing repos${NC}"
|
|
fi
|
|
|
|
if [[ ${#untracked_dirs[@]} -gt 0 ]] && [[ "$FIX_MRCONFIG" == false ]]; then
|
|
echo -e "${YELLOW}Tip: Run with --fix-all to add untracked repositories to .mrconfig${NC}"
|
|
fi
|
|
|
|
# Exit with error if unfixed issues remain
|
|
if ([[ ${#untracked_dirs[@]} -gt 0 ]] && [[ "$FIX_MRCONFIG" == false ]]) || \
|
|
([[ ${#missing_keybase[@]} -gt 0 ]]) || \
|
|
([[ ${#non_git_dirs[@]} -gt 0 ]] && [[ "$FIX_MRCONFIG" == false ]]); then
|
|
exit 1
|
|
fi
|
|
|
|
exit 0
|