This commit is contained in:
2025-11-17 08:36:00 -08:00
commit cabdc9b8ed
2 changed files with 504 additions and 0 deletions

483
audit_repos.sh Executable file
View File

@@ -0,0 +1,483 @@
#!/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

21
flake.nix Normal file
View File

@@ -0,0 +1,21 @@
{
description = "A collection of utility scripts";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }: {
packages.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.stdenv.mkDerivation {
name = "MyUtils";
src = ./.;
installPhase = ''
mkdir -p $out/bin
cp audit_repos.sh $out/bin/audit_repos
chmod +x $out/bin/audit_repos
'';
};
defaultPackage.x86_64-linux = self.packages.x86_64-linux.default;
};
}