rd13_copilot_setup/scripts/copilot-update.sh
Conrad Schulz 9e14a5fe90 feat(deploy): non-invasive git config + opt-in init-copilot + portable detection
WP6: Globaler 'git init'-Override und init.templateDir werden nicht mehr gesetzt (griff auch bei git clone fremder Repos). Stattdessen Opt-in-Alias 'git init-copilot'. Frueher gesetzte invasive Werte werden gezielt entfernt (nur wenn sie auf unsere Templates/Bootstrap zeigen). WP7: post-merge Auto-Deploy im Setup-Repo nur noch mit --with-self-update-hook. WP4: detect_vscode_user_dir (Server/Insiders/VSCodium/Cursor/Flatpak/macOS/Windows-WSL), identisch in deploy + copilot-update; ${USER:-} gegen set -u abgesichert. Getestet isoliert: kein alias.init, init-copilot gesetzt, kein init.templateDir, Hook nur mit Flag, unbekanntes Arg -> exit 2.
2026-06-10 10:26:55 +02:00

252 lines
10 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env sh
# copilot-update.sh
# Zieht die neueste Version des Copilot-Setup-Repos und aktualisiert:
# - Dieses Script selbst (~/.local/bin/copilot-update.sh)
# - Globale Git-Templates (~/.git-templates/)
# - VS Code Prompt-Dateien
# - Git-Hooks im aktuellen Repo (pre-commit)
# - .github/copilot-instructions.md (Framework-Sektion immer, Projekt-Teil bleibt)
#
# Usage:
# copilot-update.sh # aktuelles Verzeichnis (Git-Repo optional)
# git copilot-update # via Git-Alias
# ── Konfiguration laden: env > config-file > leer ─────────────────────────
# Env-Variablen (Vorrang):
# COPILOT_SETUP_REMOTE_SSH / _HTTP Remote-URLs des Setup-Repos
# COPILOT_SETUP_DIR lokaler Klon/Arbeitskopie (Offline)
# COPILOT_SETUP_CONFIG alternativer Pfad zur Config-Datei
# Config-Datei (sh-Syntax, gesourct): $XDG_CONFIG_HOME/copilot-setup/config
# Standalone-Skript -> keine gemeinsame Lib sourcebar; gleiche Aufloesung in
# deploy.sh. Bei Aenderungen beide Stellen pflegen.
_env_ssh="${COPILOT_SETUP_REMOTE_SSH:-}"
_env_http="${COPILOT_SETUP_REMOTE_HTTP:-}"
_env_dir="${COPILOT_SETUP_DIR:-}"
CONFIG_FILE="${COPILOT_SETUP_CONFIG:-${XDG_CONFIG_HOME:-$HOME/.config}/copilot-setup/config}"
if [ -f "$CONFIG_FILE" ]; then
. "$CONFIG_FILE"
fi
# Env ueberschreibt Werte aus der Config-Datei
[ -n "$_env_ssh" ] && COPILOT_SETUP_REMOTE_SSH="$_env_ssh"
[ -n "$_env_http" ] && COPILOT_SETUP_REMOTE_HTTP="$_env_http"
[ -n "$_env_dir" ] && COPILOT_SETUP_DIR="$_env_dir"
SETUP_REPO_SSH="${COPILOT_SETUP_REMOTE_SSH:-}"
SETUP_REPO_HTTP="${COPILOT_SETUP_REMOTE_HTTP:-}"
CACHE_DIR="${COPILOT_SETUP_DIR:-$HOME/.copilot-setup}"
have_remote() { [ -n "$SETUP_REPO_SSH" ] || [ -n "$SETUP_REPO_HTTP" ]; }
clone_setup_repo() {
if [ -n "$SETUP_REPO_SSH" ]; then
echo " → Klone Setup-Repo (SSH)..."
if git clone --quiet "$SETUP_REPO_SSH" "$CACHE_DIR" 2>/dev/null; then
echo " ✓ Geklont via SSH"; return 0
fi
echo " ✗ SSH fehlgeschlagen"
fi
if [ -n "$SETUP_REPO_HTTP" ]; then
echo " → Klone Setup-Repo (HTTP)..."
if git clone --quiet "$SETUP_REPO_HTTP" "$CACHE_DIR" 2>/dev/null; then
echo " ✓ Geklont via HTTP"; return 0
fi
echo " ✗ HTTP fehlgeschlagen"
fi
return 1
}
# VS Code User-Verzeichnis portabel ueber Editionen/OS ermitteln.
# Identischer Block in deploy.sh (Standalone -> keine Lib sourcebar).
detect_vscode_user_dir() {
for d in \
"$HOME/.vscode-server/data/User" \
"$HOME/.vscode-server-insiders/data/User" \
"$HOME/Library/Application Support/Code/User" \
"$HOME/Library/Application Support/Code - Insiders/User" \
"$HOME/Library/Application Support/VSCodium/User" \
"$HOME/Library/Application Support/Cursor/User" \
"$HOME/.config/Code/User" \
"$HOME/.config/Code - Insiders/User" \
"$HOME/.config/VSCodium/User" \
"$HOME/.config/Cursor/User" \
"$HOME/.var/app/com.visualstudio.code/config/Code/User" \
"/mnt/c/Users/${USER:-}/AppData/Roaming/Code/User" \
"/mnt/c/Users/${USER:-}/AppData/Roaming/Code - Insiders/User"
do
if [ -d "$d" ]; then
printf '%s\n' "$d"
return 0
fi
done
case "$(uname -s)" in
Darwin) printf '%s\n' "$HOME/Library/Application Support/Code/User" ;;
*) printf '%s\n' "$HOME/.config/Code/User" ;;
esac
}
echo "=== Copilot Update ==="
# ── 1. Setup-Repo Cache aktuell halten ───────────────────────────────────────
if [ -d "$CACHE_DIR/.git" ]; then
if have_remote; then
echo " → Pulling latest from setup repo..."
if git -C "$CACHE_DIR" pull --ff-only --quiet 2>/dev/null; then
echo " ✓ Cache aktualisiert: $CACHE_DIR"
else
echo " ⚠ git pull fehlgeschlagen nutze vorhandenen Cache (offline)"
fi
else
echo " ─ Kein Remote konfiguriert nutze lokalen Klon: $CACHE_DIR"
fi
elif [ -d "$CACHE_DIR/git-templates" ]; then
echo " ─ Lokale Setup-Quelle ohne .git (Offline-Betrieb): $CACHE_DIR"
else
if have_remote && clone_setup_repo; then
:
else
echo ""
echo " ⚠ Keine Setup-Quelle verfuegbar Update uebersprungen."
if have_remote; then
echo " Remote nicht erreichbar:"
[ -n "$SETUP_REPO_SSH" ] && echo " SSH: $SETUP_REPO_SSH"
[ -n "$SETUP_REPO_HTTP" ] && echo " HTTP: $SETUP_REPO_HTTP"
else
echo " Kein Remote konfiguriert. Setze COPILOT_SETUP_REMOTE_SSH/_HTTP,"
echo " COPILOT_SETUP_DIR (lokaler Klon) oder lege $CONFIG_FILE an."
fi
echo " (Kein Fehler ohne Quelle gibt es nichts zu aktualisieren.)"
exit 0
fi
fi
SOURCE="$CACHE_DIR"
# ── 1b. Self-update: dieses Script selbst aktualisieren ─────────────────────
SELF="$HOME/.local/bin/copilot-update.sh"
if [ -f "$SOURCE/scripts/copilot-update.sh" ]; then
if ! cmp -s "$SOURCE/scripts/copilot-update.sh" "$SELF" 2>/dev/null; then
cp "$SOURCE/scripts/copilot-update.sh" "$SELF"
chmod +x "$SELF"
echo " ✓ copilot-update.sh selbst aktualisiert starte neue Version..."
exec "$SELF" "$@"
fi
fi
GIT_TEMPLATE_DIR="$HOME/.git-templates"
# ── 2. ~/.git-templates/ aktualisieren ──────────────────────────────────────
mkdir -p "$GIT_TEMPLATE_DIR/.github" "$GIT_TEMPLATE_DIR/.vscode" \
"$GIT_TEMPLATE_DIR/hooks" "$GIT_TEMPLATE_DIR/docs" \
"$GIT_TEMPLATE_DIR/history/summary"
cp "$SOURCE/git-templates/.github/copilot-instructions.md" "$GIT_TEMPLATE_DIR/.github/"
cp "$SOURCE/git-templates/.vscode/settings.json" "$GIT_TEMPLATE_DIR/.vscode/"
cp "$SOURCE/git-templates/.vscode/extensions.json" "$GIT_TEMPLATE_DIR/.vscode/"
cp "$SOURCE/git-templates/hooks/pre-commit" "$GIT_TEMPLATE_DIR/hooks/"
chmod +x "$GIT_TEMPLATE_DIR/hooks/pre-commit"
if [ -f "$SOURCE/git-templates/hooks/post-merge" ]; then
cp "$SOURCE/git-templates/hooks/post-merge" "$GIT_TEMPLATE_DIR/hooks/"
chmod +x "$GIT_TEMPLATE_DIR/hooks/post-merge"
fi
cp "$SOURCE/git-templates/docs/USER.md" "$GIT_TEMPLATE_DIR/docs/"
cp "$SOURCE/git-templates/docs/ADMIN.md" "$GIT_TEMPLATE_DIR/docs/"
cp "$SOURCE/git-templates/docs/MAINTAINER.md" "$GIT_TEMPLATE_DIR/docs/"
cp "$SOURCE/git-templates/history/summary/PROJECT_CONTEXT.md" "$GIT_TEMPLATE_DIR/history/summary/"
echo " ✓ ~/.git-templates/ aktualisiert"
# ── 3. VS Code Prompt-Dateien aktualisieren ───────────────────────────────────
VSCODE_USER="$(detect_vscode_user_dir)"
if [ -d "$VSCODE_USER" ]; then
mkdir -p "$VSCODE_USER/prompts"
PROMPTS_UPDATED=0
for f in "$SOURCE/prompts/"*.prompt.md; do
fname="$(basename "$f")"
cp "$f" "$VSCODE_USER/prompts/$fname"
PROMPTS_UPDATED=$((PROMPTS_UPDATED + 1))
done
echo "$PROMPTS_UPDATED Prompt-Dateien aktualisiert → $VSCODE_USER/prompts/"
else
echo " ─ VS Code User-Verzeichnis nicht gefunden, Prompts übersprungen"
fi
# ── 4. Repo-lokale Updates (nur wenn in einem Git-Repo) ──────────────────────
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
if [ -z "$REPO_ROOT" ]; then
echo ""
echo " ─ Kein Git-Repo erkannt repo-lokale Updates übersprungen"
echo ""
echo "=== Done ==="
exit 0
fi
echo ""
echo " Git-Repo erkannt: $REPO_ROOT"
# ── 4a. Git-Hooks aktualisieren ───────────────────────────────────────────────
HOOKS_DIR="$REPO_ROOT/.git/hooks"
mkdir -p "$HOOKS_DIR"
HOOKS_UPDATED=0
for hook in pre-commit; do
if [ -f "$SOURCE/git-templates/hooks/$hook" ]; then
cp "$SOURCE/git-templates/hooks/$hook" "$HOOKS_DIR/$hook"
chmod +x "$HOOKS_DIR/$hook"
HOOKS_UPDATED=$((HOOKS_UPDATED + 1))
echo " ✓ .git/hooks/$hook aktualisiert"
fi
done
if [ "$HOOKS_UPDATED" -eq 0 ]; then
echo " ─ Keine Hook-Templates gefunden"
fi
# ── 4b. copilot-instructions.md: Framework-Sektion immer aktualisieren ─────────
# Die Datei besteht aus zwei Teilen:
# 1) Framework-Sektion (bis zum ersten ---): Session-Protokoll, Verbotene Aktionen
# 2) Projekt-Sektion (ab ---): Stack, Architecture, Conventions (repo-spezifisch)
# Der Framework-Teil wird immer aus dem Template aktualisiert.
# Der Projekt-Teil bleibt unverändert.
COPILOT_INSTRUCTIONS="$REPO_ROOT/.github/copilot-instructions.md"
TEMPLATE_INSTRUCTIONS="$SOURCE/git-templates/.github/copilot-instructions.md"
if [ -f "$COPILOT_INSTRUCTIONS" ] && [ -f "$TEMPLATE_INSTRUCTIONS" ]; then
# Framework-Sektion aus Template (alles vor erstem ---)
framework=$(awk '/^---$/{exit} {print}' "$TEMPLATE_INSTRUCTIONS")
# Projekt-Sektion aus Ziel-Datei (ab erstem --- inkl.)
project=$(awk '/^---$/{found=1} found{print}' "$COPILOT_INSTRUCTIONS")
if [ -n "$project" ]; then
printf '%s\n\n%s\n' "$framework" "$project" > "$COPILOT_INSTRUCTIONS"
echo " ✓ .github/copilot-instructions.md Framework-Sektion aktualisiert"
else
# Keine Projekt-Sektion vorhanden (noch nicht angepasst) komplett ersetzen
cp "$TEMPLATE_INSTRUCTIONS" "$COPILOT_INSTRUCTIONS"
echo " ✓ .github/copilot-instructions.md aktualisiert (noch nicht angepasst)"
fi
else
echo " ─ .github/copilot-instructions.md nicht vorhanden, übersprungen"
fi
# ── 4c. Fehlende 3-Zielgruppen-Docs anlegen (nur wenn noch nicht vorhanden) ──
# Überschreibt nie vorhandene Dateien nur initial anlegen
if [ -d "$REPO_ROOT/docs" ]; then
DOCS_CREATED=0
for doc in USER.md ADMIN.md MAINTAINER.md; do
if [ ! -f "$REPO_ROOT/docs/$doc" ] && [ -f "$GIT_TEMPLATE_DIR/docs/$doc" ]; then
cp "$GIT_TEMPLATE_DIR/docs/$doc" "$REPO_ROOT/docs/$doc"
echo " ✓ docs/$doc angelegt (Template)"
DOCS_CREATED=$((DOCS_CREATED + 1))
fi
done
if [ "$DOCS_CREATED" -eq 0 ]; then
echo " ─ docs/ 3-Zielgruppen-Docs bereits vorhanden"
fi
fi
echo ""
echo "=== Done ==="
echo "Tipp: 'git copilot-update' jederzeit ausführen um Templates aktuell zu halten."