187 lines
6.0 KiB
Bash
187 lines
6.0 KiB
Bash
#!/bin/bash
|
||
# =============================================================================
|
||
# 🔧 StianNOR-ZSHkit — .zshrc Merge Script
|
||
# Merges managed sections from ZSHkit/.zshrc into the live ~/.zshrc
|
||
# without overwriting personal aliases or custom configuration.
|
||
#
|
||
# How it works:
|
||
# - Managed sections in ~/.zshrc are wrapped with markers:
|
||
# # --- ZSHKIT:section_name ---
|
||
# ... content ...
|
||
# # --- /ZSHKIT:section_name ---
|
||
# - On first run, markers are injected into your existing ~/.zshrc
|
||
# - On subsequent runs, content between markers is updated from ZSHkit
|
||
# - Everything outside the markers is never touched
|
||
#
|
||
# Usage:
|
||
# ./update_zshrc.sh — merge and apply
|
||
# ./update_zshrc.sh --dry-run — show what would change, no writes
|
||
# ./update_zshrc.sh --status — show which sections are present
|
||
# =============================================================================
|
||
set -euo pipefail
|
||
|
||
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'
|
||
RED='\033[0;31m'; BOLD='\033[1m'; RESET='\033[0m'
|
||
LINE="────────────────────────────────────────────────────────────────────────────"
|
||
|
||
info() { echo -e "${CYAN}ℹ $*${RESET}"; }
|
||
success() { echo -e "${GREEN}✅ $*${RESET}"; }
|
||
warn() { echo -e "${YELLOW}⚠ $*${RESET}"; }
|
||
error() { echo -e "${RED}❌ $*${RESET}"; }
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
SOURCE="$SCRIPT_DIR/.zshrc"
|
||
TARGET="$HOME/.zshrc"
|
||
BACKUP="$HOME/.zshrc.bak.$(date +%Y%m%d-%H%M%S)"
|
||
DRY_RUN=false
|
||
STATUS_ONLY=false
|
||
|
||
case "${1:-}" in
|
||
--dry-run) DRY_RUN=true ;;
|
||
--status) STATUS_ONLY=true ;;
|
||
esac
|
||
|
||
if [[ ! -f "$SOURCE" ]]; then
|
||
error "Source not found: $SOURCE"
|
||
exit 1
|
||
fi
|
||
|
||
if [[ ! -f "$TARGET" ]]; then
|
||
warn "No ~/.zshrc found — copying source directly"
|
||
cp "$SOURCE" "$TARGET"
|
||
success "Created $TARGET from ZSHkit source"
|
||
exit 0
|
||
fi
|
||
|
||
declare -A SECTION_START=(
|
||
[autosuggestions]="# ─── Autosuggestions"
|
||
[aliases]="# ─── User Aliases and Functions"
|
||
)
|
||
declare -A SECTION_END=(
|
||
[autosuggestions]="source \$ZSH/oh-my-zsh.sh"
|
||
[aliases]="alias rsy="
|
||
)
|
||
|
||
# =============================================================================
|
||
# STATUS MODE
|
||
# =============================================================================
|
||
if [[ "$STATUS_ONLY" = true ]]; then
|
||
echo -e "\n${BOLD}${CYAN}🔧 .zshrc Managed Sections Status${RESET}"
|
||
echo -e "${LINE}"
|
||
for section in "${!SECTION_START[@]}"; do
|
||
marker="# --- ZSHKIT:${section} ---"
|
||
if grep -qF "$marker" "$TARGET" 2>/dev/null; then
|
||
success "Section present: $section"
|
||
else
|
||
warn "Section missing (will be injected on next run): $section"
|
||
fi
|
||
done
|
||
echo -e "${LINE}\n"
|
||
exit 0
|
||
fi
|
||
|
||
# =============================================================================
|
||
# BACKUP
|
||
# =============================================================================
|
||
cp "$TARGET" "$BACKUP"
|
||
info "Backup saved: $BACKUP"
|
||
|
||
echo -e "\n${BOLD}${CYAN}🔧 Merging .zshrc sections${RESET}"
|
||
echo -e "${LINE}"
|
||
|
||
CHANGED=0
|
||
|
||
merge_section() {
|
||
local name="$1"
|
||
local start_pat="$2"
|
||
local end_pat="$3"
|
||
|
||
local begin_marker="# --- ZSHKIT:${name} ---"
|
||
local end_marker="# --- /ZSHKIT:${name} ---"
|
||
|
||
local new_content
|
||
new_content=$(awk "/$start_pat/{found=1} found{print} /$end_pat/{if(found) {found=0}}" "$SOURCE")
|
||
|
||
if [[ -z "$new_content" ]]; then
|
||
warn "Could not extract section '$name' from source — skipping"
|
||
return
|
||
fi
|
||
|
||
local block
|
||
block="$begin_marker
|
||
$new_content
|
||
$end_marker"
|
||
|
||
if grep -qF "$begin_marker" "$TARGET"; then
|
||
local existing
|
||
existing=$(awk "/$begin_marker/{found=1; next} /$end_marker/{found=0; next} found{print}" "$TARGET")
|
||
if [[ "$existing" == "$new_content" ]]; then
|
||
success "Up to date: $name"
|
||
return
|
||
fi
|
||
if [[ "$DRY_RUN" = true ]]; then
|
||
warn "[DRY RUN] Would update section: $name"
|
||
diff <(echo "$existing") <(echo "$new_content") || true
|
||
else
|
||
python3 - "$TARGET" "$begin_marker" "$end_marker" "$new_content" <<'PYEOF'
|
||
import sys, re
|
||
target = sys.argv[1]
|
||
begin = sys.argv[2]
|
||
end = sys.argv[3]
|
||
new_body = sys.argv[4]
|
||
with open(target, 'r') as f:
|
||
content = f.read()
|
||
pattern = re.escape(begin) + r'.*?' + re.escape(end)
|
||
replacement = begin + '\n' + new_body + '\n' + end
|
||
new_content = re.sub(pattern, replacement, content, flags=re.DOTALL)
|
||
with open(target, 'w') as f:
|
||
f.write(new_content)
|
||
PYEOF
|
||
success "Updated section: $name"
|
||
(( CHANGED++ )) || true
|
||
fi
|
||
else
|
||
if [[ "$DRY_RUN" = true ]]; then
|
||
warn "[DRY RUN] Would inject new section: $name"
|
||
else
|
||
if grep -q "# End of .zshrc" "$TARGET"; then
|
||
python3 - "$TARGET" "# End of .zshrc" "$block" <<'PYEOF'
|
||
import sys
|
||
target = sys.argv[1]
|
||
marker = sys.argv[2]
|
||
new_block = sys.argv[3]
|
||
with open(target, 'r') as f:
|
||
content = f.read()
|
||
content = content.replace(marker, new_block + '\n\n' + marker)
|
||
with open(target, 'w') as f:
|
||
f.write(content)
|
||
PYEOF
|
||
else
|
||
echo "" >> "$TARGET"
|
||
echo "$block" >> "$TARGET"
|
||
fi
|
||
success "Injected new section: $name"
|
||
(( CHANGED++ )) || true
|
||
fi
|
||
fi
|
||
}
|
||
|
||
merge_section "autosuggestions" \
|
||
"# ─── Autosuggestions" \
|
||
"source \\\$ZSH/oh-my-zsh.sh"
|
||
|
||
merge_section "aliases" \
|
||
"# ─── User Aliases and Functions" \
|
||
"alias rsy="
|
||
|
||
echo -e "${LINE}"
|
||
if [[ "$DRY_RUN" = true ]]; then
|
||
warn "Dry run complete — no files were modified"
|
||
elif [[ $CHANGED -eq 0 ]]; then
|
||
success "Everything already up to date — no changes needed"
|
||
else
|
||
success "$CHANGED section(s) updated in $TARGET"
|
||
echo -e "\n ${YELLOW}Run ${CYAN}fresh${RESET}${YELLOW} or open a new terminal to apply changes.${RESET}"
|
||
fi
|
||
echo -e "${LINE}\n"
|