Files
2026-04-28 18:25:20 +02:00

406 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOTS=(
"$HOME/Projekte"
"$HOME/Projects"
"$HOME/Code"
"$HOME/Developer"
"$HOME/dev"
"$HOME/test"
)
notify() {
notify-send "󰅩 Dev Menue" "$1" >/dev/null 2>&1 || true
}
pick() {
wofi --dmenu --prompt "$1" --insensitive
}
terminal_hold() {
local title="$1"
shift
if command -v kitty >/dev/null 2>&1; then
kitty --title "$title" sh -lc "$*; printf '\n'; read -r -p 'Enter zum Schliessen... ' _"
else
notify "kitty ist nicht installiert."
fi
}
shell_quote() {
printf '%q' "$1"
}
code_cmd() {
if command -v code >/dev/null 2>&1; then
printf '%s\n' code
elif command -v codium >/dev/null 2>&1; then
printf '%s\n' codium
else
return 1
fi
}
docker_available() {
if ! command -v docker >/dev/null 2>&1; then
notify "Docker ist nicht installiert."
return 1
fi
if ! docker info >/dev/null 2>&1; then
notify "Docker laeuft nicht oder ist nicht erreichbar."
return 1
fi
}
has_compose() {
local project="$1"
[[ -f "$project/compose.yml" ||
-f "$project/compose.yaml" ||
-f "$project/docker-compose.yml" ||
-f "$project/docker-compose.yaml" ]]
}
project_label() {
local project="$1"
local marker=""
if [[ -d "$project/.git" ]]; then
marker+=" "
fi
if has_compose "$project"; then
marker+="󰡨 "
fi
printf '%s%s' "$marker" "${project#$HOME/}"
}
pick_project() {
local roots=()
local projects=()
local labels=()
local root project choice
for root in "${PROJECT_ROOTS[@]}"; do
[[ -d "$root" ]] && roots+=("$root")
done
if ((${#roots[@]} == 0)); then
notify "Keine Projektordner gefunden."
return 1
fi
mapfile -t projects < <(
{
find "${roots[@]}" -maxdepth 4 -type d -name .git -printf '%h\n' 2>/dev/null
find "${roots[@]}" -maxdepth 3 -type d \( -name node_modules -o -name vendor \) -prune -o \
-type f \( -name compose.yml -o -name compose.yaml -o -name docker-compose.yml -o -name docker-compose.yaml -o -name package.json -o -name Cargo.toml -o -name pyproject.toml -o -name go.mod \) \
-printf '%h\n' 2>/dev/null
} |
sort -u
)
if ((${#projects[@]} == 0)); then
notify "Keine Projekte gefunden."
return 1
fi
for project in "${projects[@]}"; do
labels+=("$(project_label "$project")")
done
choice="$(printf '%s\n' "${labels[@]}" | pick "📁 Projekt")"
[[ -z "${choice:-}" ]] && return 1
for i in "${!labels[@]}"; do
if [[ "$choice" == "${labels[$i]}" ]]; then
printf '%s\n' "${projects[$i]}"
return 0
fi
done
return 1
}
open_project_browser() {
local project="$1"
local choice url
choice="$(
printf '%s\n' \
"🌐 http://localhost:3000" \
"🌐 http://localhost:5173" \
"🌐 http://localhost:8000" \
"🌐 http://localhost:8080" \
"🌐 http://localhost:5000" \
"🌐 http://localhost:4200" \
"📂 Projektordner im Browser" |
pick "🌐 Projekt im Browser"
)"
case "$choice" in
*"Projektordner"*)
xdg-open "$project" >/dev/null 2>&1 &
;;
*"http"*)
url="${choice#* }"
xdg-open "$url" >/dev/null 2>&1 &
;;
esac
}
project_menu() {
local project project_q choice editor
project="$(pick_project)" || return 0
project_q="$(shell_quote "$project")"
choice="$(
printf '%s\n' \
"📂 Projekt oeffnen (Code)" \
"🌐 Projekt im Browser oeffnen" \
"🐳 Docker Compose starten" \
"🛑 Docker stoppen" \
"🔄 Container neu starten" \
"📜 Logs anzeigen" \
"📦 Git Repo Status anzeigen" |
pick "📁 $(basename "$project")"
)"
case "$choice" in
*"Projekt oeffnen"*)
if editor="$(code_cmd)"; then
"$editor" "$project" >/dev/null 2>&1 &
else
notify "VS Code/Codium ist nicht installiert."
fi
;;
*"Browser"*)
open_project_browser "$project"
;;
*"Docker Compose starten"*)
docker_available || return 0
if has_compose "$project"; then
terminal_hold "Docker Compose: $(basename "$project")" "cd $project_q && docker compose up -d && docker compose ps"
else
notify "Kein Docker-Compose File im Projekt."
fi
;;
*"Docker stoppen"*)
docker_available || return 0
if has_compose "$project"; then
terminal_hold "Docker Stop: $(basename "$project")" "cd $project_q && docker compose stop && docker compose ps"
else
notify "Kein Docker-Compose File im Projekt."
fi
;;
*"Container neu starten"*)
docker_available || return 0
if has_compose "$project"; then
terminal_hold "Docker Restart: $(basename "$project")" "cd $project_q && docker compose restart && docker compose ps"
else
notify "Kein Docker-Compose File im Projekt."
fi
;;
*"Logs anzeigen"*)
docker_available || return 0
if has_compose "$project"; then
if command -v kitty >/dev/null 2>&1; then
kitty --title "Logs: $(basename "$project")" sh -lc "cd $project_q && docker compose logs -f --tail=200"
else
notify "kitty ist nicht installiert."
fi
else
notify "Kein Docker-Compose File im Projekt."
fi
;;
*"Git Repo Status"*)
if [[ -d "$project/.git" ]]; then
terminal_hold "Git Status: $(basename "$project")" "cd $project_q && git status --short --branch"
else
notify "Kein Git Repository."
fi
;;
esac
}
pick_container() {
local containers=()
local labels=()
local line name status choice
docker_available || return 1
mapfile -t containers < <(docker ps -a --format '{{.Names}}|{{.Status}}' | sort)
if ((${#containers[@]} == 0)); then
notify "Keine Docker Container gefunden."
return 1
fi
for line in "${containers[@]}"; do
name="${line%%|*}"
status="${line#*|}"
labels+=("🐳 $name · $status")
done
choice="$(printf '%s\n' "${labels[@]}" | pick "🐳 Container")"
[[ -z "${choice:-}" ]] && return 1
for i in "${!labels[@]}"; do
if [[ "$choice" == "${labels[$i]}" ]]; then
printf '%s\n' "${containers[$i]%%|*}"
return 0
fi
done
return 1
}
docker_menu() {
local choice container running_ids all_ids
choice="$(
printf '%s\n' \
"▶️ Alle Container starten" \
"⏹️ Alle stoppen" \
"🔄 Einzelnen Container neu starten" \
"📊 docker stats" \
"📜 Logs anzeigen" \
"🧹 Cleanup dangling images" |
pick "🐳 Docker Control"
)"
case "$choice" in
*"Alle Container starten"*)
docker_available || return 0
all_ids="$(docker ps -aq)"
if [[ -n "$all_ids" ]]; then
terminal_hold "Docker Start" "docker start $all_ids && docker ps"
else
notify "Keine Container gefunden."
fi
;;
*"Alle stoppen"*)
docker_available || return 0
running_ids="$(docker ps -q)"
if [[ -n "$running_ids" ]]; then
terminal_hold "Docker Stop" "docker stop $running_ids && docker ps -a"
else
notify "Keine laufenden Container."
fi
;;
*"Einzelnen Container neu starten"*)
container="$(pick_container)" || return 0
terminal_hold "Docker Restart: $container" "docker restart '$container' && docker ps -a --filter name='^/$container$'"
;;
*"docker stats"*)
docker_available || return 0
if command -v kitty >/dev/null 2>&1; then
kitty --title "docker stats" sh -lc "docker stats"
else
notify "kitty ist nicht installiert."
fi
;;
*"Logs anzeigen"*)
container="$(pick_container)" || return 0
if command -v kitty >/dev/null 2>&1; then
kitty --title "Logs: $container" sh -lc "docker logs -f --tail=200 '$container'"
else
notify "kitty ist nicht installiert."
fi
;;
*"Cleanup dangling images"*)
docker_available || return 0
terminal_hold "Docker Cleanup" "docker image prune -f"
;;
esac
}
launch_codex() {
if ! command -v kitty >/dev/null 2>&1; then
notify "kitty ist nicht installiert."
return 0
fi
if ! command -v codex >/dev/null 2>&1; then
notify "codex ist nicht installiert."
return 0
fi
kitty --title "Codex" sh -lc 'cd "$HOME" && exec codex' >/dev/null 2>&1 &
}
launch_opencode() {
if ! command -v kitty >/dev/null 2>&1; then
notify "kitty ist nicht installiert."
return 0
fi
if ! command -v opencode >/dev/null 2>&1; then
notify "opencode ist nicht installiert."
return 0
fi
kitty --title "opencode" sh -lc 'cd "$HOME" && exec opencode' >/dev/null 2>&1 &
}
choice="$(
printf '%s\n' \
"📁 Projekt Management" \
"󰌘 Homelab Controlcenter" \
"🐳 Docker Control" \
"󰚩 Codex" \
"󰚩 opencode" \
" Terminal" \
" Projektordner" \
" VS Code / Codium" \
"󰊢 Git GUI" |
pick "󰅩 Dev Menue"
)"
case "$choice" in
*"Projekt Management"*)
project_menu
;;
*"Homelab Controlcenter"*)
"$SCRIPT_DIR/homelab-control.sh"
;;
*"Docker Control"*)
docker_menu
;;
*"Codex"*)
launch_codex
;;
*"opencode"*)
launch_opencode
;;
*"Terminal"*)
kitty
;;
*"Projektordner"*)
nautilus
;;
*"VS Code"*|*"Codium"*)
if editor="$(code_cmd)"; then
"$editor" >/dev/null 2>&1 &
else
notify "VS Code/Codium ist nicht installiert."
fi
;;
*"Git GUI"*)
if command -v gitg >/dev/null 2>&1; then
gitg
elif command -v git-cola >/dev/null 2>&1; then
git-cola
else
notify "gitg oder git-cola ist nicht installiert."
fi
;;
esac