192 lines
6.2 KiB
Bash
192 lines
6.2 KiB
Bash
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# devlab — DevLab Management CLI
|
|
# Usage: devlab <command> [args]
|
|
#
|
|
# Install to PATH: sudo cp devlab.sh /usr/local/bin/devlab && chmod +x /usr/local/bin/devlab
|
|
# =============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
CONFIG_FILE="/opt/lab/.devlab-config"
|
|
[[ -f "$CONFIG_FILE" ]] && source "$CONFIG_FILE"
|
|
LAB_ROOT="${LAB_ROOT:-/opt/lab}"
|
|
|
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
|
|
|
|
SERVICES=(caddy code-server gitea gitea-runner ollama open-webui portainer watchtower)
|
|
|
|
cmd_help() {
|
|
cat <<EOF
|
|
${BOLD}devlab${RESET} — DevLab Management CLI
|
|
|
|
${BOLD}COMMANDS${RESET}
|
|
status Show status of all services
|
|
start Start all services
|
|
stop Stop all services
|
|
restart [name] Restart all or a specific service
|
|
logs <name> Tail logs for a service
|
|
update Pull latest images and recreate containers
|
|
backup Run a manual backup now
|
|
models List downloaded Ollama models
|
|
pull <model> Pull an Ollama model (e.g. codellama:34b)
|
|
remove <model> Remove an Ollama model
|
|
urls Print all service URLs
|
|
compose up <file> Start a test environment from a compose file
|
|
compose down <file> Stop a test environment
|
|
compose ls List running compose stacks
|
|
|
|
${BOLD}SERVICES${RESET}
|
|
${SERVICES[*]}
|
|
|
|
${BOLD}EXAMPLES${RESET}
|
|
devlab status
|
|
devlab logs gitea
|
|
devlab pull mistral:latest
|
|
devlab compose up /opt/lab/compose/examples/lamp-stack.yml
|
|
devlab restart code-server
|
|
EOF
|
|
}
|
|
|
|
cmd_status() {
|
|
echo -e "\n${BOLD}DevLab Service Status${RESET}\n"
|
|
printf "%-20s %-12s %s\n" "CONTAINER" "STATUS" "IMAGE"
|
|
printf '%.0s─' {1..60}; echo
|
|
for svc in "${SERVICES[@]}"; do
|
|
local info
|
|
info=$(docker inspect "$svc" --format '{{.State.Status}} {{.Config.Image}}' 2>/dev/null) || {
|
|
printf "%-20s ${RED}%-12s${RESET} %s\n" "$svc" "not found" "-"
|
|
continue
|
|
}
|
|
local status="${info%% *}"
|
|
local image="${info#* }"
|
|
local color="$RED"
|
|
[[ "$status" == "running" ]] && color="$GREEN"
|
|
printf "%-20s ${color}%-12s${RESET} %s\n" "$svc" "$status" "$image"
|
|
done
|
|
echo ""
|
|
}
|
|
|
|
cmd_start() {
|
|
for svc in "${SERVICES[@]}"; do
|
|
docker start "$svc" 2>/dev/null && \
|
|
echo -e " ${GREEN}✓${RESET} ${svc}" || \
|
|
echo -e " ${RED}✗${RESET} ${svc} (not found)"
|
|
done
|
|
}
|
|
|
|
cmd_stop() {
|
|
read -rp "Stop all DevLab services? [y/N]: " confirm
|
|
[[ "$confirm" =~ ^[Yy]$ ]] || { echo "Aborted."; return; }
|
|
for svc in "${SERVICES[@]}"; do
|
|
docker stop "$svc" 2>/dev/null && \
|
|
echo -e " ${YELLOW}■${RESET} ${svc} stopped" || true
|
|
done
|
|
}
|
|
|
|
cmd_restart() {
|
|
local target="${1:-all}"
|
|
if [[ "$target" == "all" ]]; then
|
|
for svc in "${SERVICES[@]}"; do
|
|
docker restart "$svc" 2>/dev/null && \
|
|
echo -e " ${GREEN}↺${RESET} ${svc}" || true
|
|
done
|
|
else
|
|
docker restart "$target" && echo -e " ${GREEN}↺${RESET} ${target} restarted"
|
|
fi
|
|
}
|
|
|
|
cmd_logs() {
|
|
local svc="${1:-}"
|
|
[[ -z "$svc" ]] && { echo "Usage: devlab logs <service>"; exit 1; }
|
|
docker logs -f --tail=100 "$svc"
|
|
}
|
|
|
|
cmd_update() {
|
|
echo -e "${CYAN}Pulling latest images...${RESET}"
|
|
for svc in "${SERVICES[@]}"; do
|
|
local image
|
|
image=$(docker inspect "$svc" --format '{{.Config.Image}}' 2>/dev/null) || continue
|
|
echo -e " Updating ${image}..."
|
|
docker pull "$image" || true
|
|
docker restart "$svc" || true
|
|
done
|
|
echo -e "${GREEN}Update complete.${RESET}"
|
|
}
|
|
|
|
cmd_backup() {
|
|
[[ -x "${LAB_ROOT}/backup.sh" ]] || { echo "Backup script not found."; exit 1; }
|
|
"${LAB_ROOT}/backup.sh"
|
|
}
|
|
|
|
cmd_models() {
|
|
docker exec ollama ollama list
|
|
}
|
|
|
|
cmd_pull() {
|
|
local model="${1:-}"
|
|
[[ -z "$model" ]] && { echo "Usage: devlab pull <model>"; exit 1; }
|
|
docker exec ollama ollama pull "$model"
|
|
}
|
|
|
|
cmd_remove_model() {
|
|
local model="${1:-}"
|
|
[[ -z "$model" ]] && { echo "Usage: devlab remove <model>"; exit 1; }
|
|
docker exec ollama ollama rm "$model"
|
|
}
|
|
|
|
cmd_urls() {
|
|
echo ""
|
|
echo -e "${BOLD}DevLab Service URLs${RESET}"
|
|
echo -e " Web IDE → ${GREEN}https://${CODE_HOST:-code.yourdomain.com}${RESET}"
|
|
echo -e " Git → ${GREEN}https://${GIT_HOST:-git.yourdomain.com}${RESET}"
|
|
echo -e " AI Chat → ${GREEN}https://${AI_HOST:-ai.yourdomain.com}${RESET}"
|
|
echo -e " Portainer → ${GREEN}https://${PM_HOST:-portainer.yourdomain.com}${RESET}"
|
|
echo ""
|
|
}
|
|
|
|
cmd_compose() {
|
|
local action="${1:-}"
|
|
shift
|
|
case "$action" in
|
|
up)
|
|
local file="${1:-}"
|
|
[[ -z "$file" ]] && { echo "Usage: devlab compose up <file.yml>"; exit 1; }
|
|
docker compose -f "$file" up -d
|
|
echo -e "${GREEN}Stack started.${RESET}"
|
|
;;
|
|
down)
|
|
local file="${1:-}"
|
|
[[ -z "$file" ]] && { echo "Usage: devlab compose down <file.yml>"; exit 1; }
|
|
docker compose -f "$file" down
|
|
;;
|
|
ls)
|
|
docker compose ls
|
|
;;
|
|
*)
|
|
echo "Usage: devlab compose {up|down|ls} [file]"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ─── Router ──────────────────────────────────────────────────────────────────
|
|
COMMAND="${1:-help}"
|
|
shift 2>/dev/null || true
|
|
|
|
case "$COMMAND" in
|
|
status) cmd_status ;;
|
|
start) cmd_start ;;
|
|
stop) cmd_stop ;;
|
|
restart) cmd_restart "$@" ;;
|
|
logs) cmd_logs "$@" ;;
|
|
update) cmd_update ;;
|
|
backup) cmd_backup ;;
|
|
models) cmd_models ;;
|
|
pull) cmd_pull "$@" ;;
|
|
remove) cmd_remove_model "$@" ;;
|
|
urls) cmd_urls ;;
|
|
compose) cmd_compose "$@" ;;
|
|
help|--help|-h) cmd_help ;;
|
|
*) echo "Unknown command: ${COMMAND}. Run: devlab help" && exit 1 ;;
|
|
esac |