git deployment automation
Git Hooks für automatisches Deployment
CI/CD ist toll, aber manchmal overkill. Für kleine Projekte reicht ein Git Hook.
Das Konzept
- Du pushst zu einem Git-Repo auf deinem Server
- Ein Hook triggert
- Deine App wird deployed
Einfach. Schnell. Keine externe Abhängigkeit.
Bare Repository einrichten
Auf dem Server:
mkdir -p /var/repo/myapp.git
cd /var/repo/myapp.git
git init --bare
Post-Receive Hook
Der Magic passiert hier. /var/repo/myapp.git/hooks/post-receive:
#!/bin/bash
set -e
TARGET="/var/www/myapp"
GIT_DIR="/var/repo/myapp.git"
BRANCH="main"
while read oldrev newrev ref
do
if [ "$ref" = "refs/heads/$BRANCH" ]; then
echo ">>> Deploying $BRANCH to $TARGET"
# Checkout
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f $BRANCH
# Build (optional)
cd $TARGET
if [ -f "package.json" ]; then
echo ">>> Installing dependencies"
npm ci --production
fi
# Restart (optional)
if [ -f "docker-compose.yml" ]; then
echo ">>> Restarting containers"
docker compose up -d --build
fi
echo ">>> Deployment complete!"
fi
done
chmod +x /var/repo/myapp.git/hooks/post-receive
Lokal: Remote hinzufügen
git remote add deploy user@server:/var/repo/myapp.git
Deployen
git push deploy main
Das war's. Push = Deploy.
Erweitertes Beispiel
Mit Downtime-Minimierung
#!/bin/bash
set -e
TARGET="/var/www/myapp"
TARGET_NEW="/var/www/myapp-new"
TARGET_OLD="/var/www/myapp-old"
GIT_DIR="/var/repo/myapp.git"
BRANCH="main"
while read oldrev newrev ref
do
if [ "$ref" = "refs/heads/$BRANCH" ]; then
echo ">>> Deploying $BRANCH"
# In neues Verzeichnis auschecken
rm -rf $TARGET_NEW
mkdir -p $TARGET_NEW
git --work-tree=$TARGET_NEW --git-dir=$GIT_DIR checkout -f $BRANCH
# Build
cd $TARGET_NEW
npm ci --production
npm run build
# Atomic swap
rm -rf $TARGET_OLD
mv $TARGET $TARGET_OLD 2>/dev/null || true
mv $TARGET_NEW $TARGET
# Restart
sudo systemctl reload nginx
echo ">>> Done!"
fi
done
Mit Docker Compose
#!/bin/bash
set -e
APP_DIR="/opt/myapp"
while read oldrev newrev ref
do
if [ "$ref" = "refs/heads/main" ]; then
echo ">>> Updating code"
git --work-tree=$APP_DIR checkout -f main
cd $APP_DIR
echo ">>> Pulling new images"
docker compose pull
echo ">>> Rebuilding and restarting"
docker compose up -d --build --remove-orphans
echo ">>> Cleaning up"
docker image prune -f
echo ">>> Done!"
fi
done
Mit Benachrichtigung
#!/bin/bash
set -e
# ... deployment code ...
# Slack Notification
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"Deployed $newrev to production\"}" \
$SLACK_WEBHOOK_URL
Mehrere Branches
while read oldrev newrev ref
do
branch=$(echo $ref | cut -d/ -f3)
case $branch in
main)
TARGET="/var/www/production"
;;
staging)
TARGET="/var/www/staging"
;;
*)
echo ">>> Ignoring branch $branch"
continue
;;
esac
echo ">>> Deploying $branch to $TARGET"
# ... rest of deployment
done
Rollback
Da es ein Git-Repo ist:
# Auf dem Server
cd /var/www/myapp
git log --oneline -5
git checkout HEAD~1
# Oder lokal
git revert HEAD
git push deploy main
Sicherheit
Nur bestimmte User
# Im Hook
ALLOWED_USERS="daniel deploy"
if [[ ! " $ALLOWED_USERS " =~ " $USER " ]]; then
echo ">>> Deployment not allowed for $USER"
exit 1
fi
Validierung
# Tests laufen lassen
npm test || {
echo ">>> Tests failed, aborting deployment"
exit 1
}
Vorteile
- Einfach: Keine externe Infrastruktur
- Schnell: Kein CI/CD Queue
- Transparent: Alles in einem Script
- Flexibel: Bash kann alles
Nachteile
- Keine Parallelisierung: Ein Deploy nach dem anderen
- Server-Abhängig: Hook läuft auf dem Ziel-Server
- Kein Review-Prozess: Direkt in Prod möglich
Wann nutzen?
- Kleine Projekte
- Personal Projects
- Wenn CI/CD Overkill ist
- Schnelle Iteration
Wann NICHT nutzen?
- Team-Projekte (nutze GitHub Actions etc.)
- Wenn du Reviews brauchst
- Komplexe Build-Prozesse
- Multi-Server Deployments
Fazit
Git Hooks sind das Original-CI/CD. Für viele Projekte reichen sie völlig aus. Kein GitHub Actions, kein Jenkins, kein Overhead. Push und fertig.