Add declarative infrastructure for self-hosted DevOps stack on black: Reconciliation service (devops-stack.sh): - Detects Docker installation and daemon status - Auto-generates secrets on first deployment - Syncs config from repo to remote - Manages container lifecycle - Staged deployment (Forgejo first, Woodpecker after OAuth) Docker configuration: - Forgejo with nginx proxy, postgres, runner - Woodpecker CI with Forgejo OAuth integration - Shared network for internal communication Integration: - Added to black host inventory - rectify-deploy detects forgejo/woodpecker changes - Convenience wrapper script (deploy-devops-stack.sh) Also removes deprecated service-registry (replaced by status-dashboard). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
182 lines
5.2 KiB
Bash
Executable file
182 lines
5.2 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# Deploy Forgejo + Woodpecker CI DevOps Stack to black
|
|
# Wrapper around the reconciliation system for manual invocation
|
|
#
|
|
# Usage:
|
|
# ./deploy-devops-stack.sh # Full deploy (via reconciler)
|
|
# ./deploy-devops-stack.sh --status # Check status
|
|
# ./deploy-devops-stack.sh --logs # View logs
|
|
# ./deploy-devops-stack.sh --restart # Restart services
|
|
# ./deploy-devops-stack.sh --check # Dry-run, show drift
|
|
#
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
INFRA_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
RECONCILE_DIR="$INFRA_DIR/reconciliation"
|
|
FORGEJO_DIR="$INFRA_DIR/docker/forgejo"
|
|
|
|
# Load utilities
|
|
source "$SCRIPT_DIR/lib/colors.sh"
|
|
source "$SCRIPT_DIR/lib/logger.sh"
|
|
log_init "DEVOPS"
|
|
|
|
# Configuration
|
|
TARGET_HOST="black"
|
|
TARGET_IP="10.0.0.11"
|
|
REMOTE_DIR="/bigdisk/forgejo"
|
|
|
|
# ============================================================================
|
|
# Helper Functions
|
|
# ============================================================================
|
|
|
|
check_ssh() {
|
|
log_step "Checking SSH connectivity to $TARGET_HOST..."
|
|
if ! ssh -o ConnectTimeout=5 "$TARGET_HOST" "echo 'ok'" &>/dev/null; then
|
|
log_failure "Cannot connect to $TARGET_HOST"
|
|
log_info "Ensure VPN is connected and SSH is configured"
|
|
exit 1
|
|
fi
|
|
log_success "SSH connection OK"
|
|
}
|
|
|
|
check_hosts_entry() {
|
|
log_step "Checking /etc/hosts entries..."
|
|
local missing=()
|
|
|
|
if ! grep -q "forge.nasty.sh" /etc/hosts 2>/dev/null; then
|
|
missing+=("forge.nasty.sh")
|
|
fi
|
|
if ! grep -q "ci.nasty.sh" /etc/hosts 2>/dev/null; then
|
|
missing+=("ci.nasty.sh")
|
|
fi
|
|
|
|
if [ ${#missing[@]} -gt 0 ]; then
|
|
log_warn "Missing /etc/hosts entries: ${missing[*]}"
|
|
log_info "Add to /etc/hosts:"
|
|
echo -e "${COLOR_CYAN} $TARGET_IP forge.nasty.sh ci.nasty.sh${COLOR_NC}"
|
|
echo ""
|
|
else
|
|
log_success "Hosts entries OK"
|
|
fi
|
|
}
|
|
|
|
remote_docker() {
|
|
ssh "$TARGET_HOST" "cd $REMOTE_DIR && docker compose $*"
|
|
}
|
|
|
|
show_status() {
|
|
log_section "Stack Status"
|
|
|
|
log_step "Container status:"
|
|
remote_docker ps || true
|
|
|
|
echo ""
|
|
log_step "Service URLs (VPN required):"
|
|
echo -e " ${COLOR_CYAN}Forgejo:${COLOR_NC} http://forge.nasty.sh/"
|
|
echo -e " ${COLOR_CYAN}Woodpecker:${COLOR_NC} http://ci.nasty.sh/"
|
|
echo -e " ${COLOR_CYAN}SSH Clone:${COLOR_NC} ssh://git@forge.nasty.sh:2222/<user>/<repo>.git"
|
|
}
|
|
|
|
show_logs() {
|
|
log_section "Container Logs"
|
|
|
|
local service="${1:-}"
|
|
|
|
if [ -n "$service" ]; then
|
|
remote_docker logs -f --tail=100 "$service"
|
|
else
|
|
remote_docker logs -f --tail=50
|
|
fi
|
|
}
|
|
|
|
restart_stack() {
|
|
log_section "Restarting Stack"
|
|
|
|
log_step "Stopping containers..."
|
|
remote_docker down
|
|
|
|
log_step "Reconciling and restarting..."
|
|
"$RECONCILE_DIR/reconcile" --host "$TARGET_HOST" --service devops-stack
|
|
|
|
log_success "Stack restarted"
|
|
}
|
|
|
|
pull_images() {
|
|
log_section "Pulling Latest Images"
|
|
|
|
remote_docker pull
|
|
log_success "Images updated"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Main
|
|
# ============================================================================
|
|
|
|
main() {
|
|
local action="${1:-deploy}"
|
|
|
|
case "$action" in
|
|
--status|-s)
|
|
check_ssh
|
|
show_status
|
|
;;
|
|
--logs|-l)
|
|
check_ssh
|
|
show_logs "${2:-}"
|
|
;;
|
|
--restart|-r)
|
|
log_banner "Forgejo + Woodpecker CI - Restart"
|
|
check_ssh
|
|
restart_stack
|
|
;;
|
|
--pull|-p)
|
|
log_banner "Forgejo + Woodpecker CI - Pull Images"
|
|
check_ssh
|
|
pull_images
|
|
;;
|
|
--check|-c)
|
|
log_banner "Forgejo + Woodpecker CI - Check Drift"
|
|
check_ssh
|
|
"$RECONCILE_DIR/reconcile" --host "$TARGET_HOST" --service devops-stack --check
|
|
;;
|
|
deploy|--deploy|-d|"")
|
|
log_banner "Forgejo + Woodpecker CI - Deploy"
|
|
check_ssh
|
|
check_hosts_entry
|
|
|
|
# Use reconciler for deployment
|
|
"$RECONCILE_DIR/reconcile" --host "$TARGET_HOST" --service devops-stack
|
|
|
|
show_status
|
|
;;
|
|
--help|-h)
|
|
echo "Usage: $0 [action]"
|
|
echo ""
|
|
echo "Actions:"
|
|
echo " (default) Deploy stack via reconciler"
|
|
echo " --check, -c Dry-run, show drift without fixing"
|
|
echo " --status, -s Show container status"
|
|
echo " --logs, -l View logs (optionally: -l <service>)"
|
|
echo " --restart, -r Restart all services"
|
|
echo " --pull, -p Pull latest images"
|
|
echo " --help, -h Show this help"
|
|
echo ""
|
|
echo "Services: nginx, forgejo, db, runner, woodpecker-server, woodpecker-agent"
|
|
echo ""
|
|
echo "The reconciler handles:"
|
|
echo " - Docker installation check"
|
|
echo " - Secret generation (first run)"
|
|
echo " - Config sync from repo"
|
|
echo " - Container lifecycle"
|
|
;;
|
|
*)
|
|
log_error "Unknown action: $action"
|
|
echo "Run '$0 --help' for usage"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|