From 94880d476e378c69b5259b1732115bf25b80fb35 Mon Sep 17 00:00:00 2001 From: Michael Czechowski Date: Sat, 16 Aug 2025 23:20:50 +0200 Subject: [PATCH] refactor: improve local development environment setup --- common.nix | 255 ++++++++++++++++++++++++++++++++++------------------- flake.nix | 62 ++++++------- 2 files changed, 196 insertions(+), 121 deletions(-) diff --git a/common.nix b/common.nix index 8440352..4412818 100644 --- a/common.nix +++ b/common.nix @@ -1,4 +1,4 @@ -{ pkgs, lib ? pkgs.lib, cloudServerNames, isLiveIso ? false, ... }: +{ pkgs, lib ? pkgs.lib, cloudServerNames, isLiveIso ? false, ... }: let # Only include isoImage config when building ISO @@ -8,26 +8,62 @@ let makeUsbBootable = true; }; }; - + # Complete Co-op Cloud recipe list (based on your ABRA_RECIPES.md and more) allRecipes = [ # Tier 1 - Production Ready (Score 5) - "gitea" "mealie" "nextcloud" - + "gitea" + "mealie" + "nextcloud" + # Tier 2 - Stable (Score 4) - "gotosocial" "wordpress" - + "gotosocial" + "wordpress" + # Tier 3 - Community (Score 3) - "collabora" "croc" "custom-php" "dokuwiki" "engelsystem" "fab-manager" - "ghost" "karrot" "lauti" "loomio" "mattermost" "mattermost-lts" "mrbs" - "onlyoffice" "open-inventory" "outline" "owncast" "rallly" - + "collabora" + "croc" + "custom-php" + "dokuwiki" + "engelsystem" + "fab-manager" + "ghost" + "karrot" + "lauti" + "loomio" + "mattermost" + "mattermost-lts" + "mrbs" + "onlyoffice" + "open-inventory" + "outline" + "owncast" + "rallly" + # Additional recipes from Co-op Cloud catalog - "hedgedoc" "mediawiki" "seafile" "jitsi-meet" "matrix-synapse" - "rocketchat" "prestashop" "invoiceninja" "kimai" "pretix" - "drone" "n8n" "gitlab" "jupyter-lab" "plausible" "matomo" - "uptime-kuma" "grafana" "peertube" "funkwhale" "mastodon" - "pixelfed" "jellyfin" + "hedgedoc" + "mediawiki" + "seafile" + "jitsi-meet" + "matrix-synapse" + "rocketchat" + "prestashop" + "invoiceninja" + "kimai" + "pretix" + "drone" + "n8n" + "gitlab" + "jupyter-lab" + "plausible" + "matomo" + "uptime-kuma" + "grafana" + "peertube" + "funkwhale" + "mastodon" + "pixelfed" + "jellyfin" ]; in @@ -35,10 +71,10 @@ isoConfig // { system.stateVersion = "25.05"; networking = { - wireless.enable = false; # Disable to avoid conflicts + wireless.enable = false; # Disable to avoid conflicts networkmanager = { enable = true; - dns = "none"; # Critical: Don't let NetworkManager manage DNS + dns = "none"; # Critical: Don't let NetworkManager manage DNS }; hostName = if isLiveIso then "workshop-live" else "workshop-vm"; }; @@ -49,24 +85,24 @@ isoConfig // { settings = { # Wildcard: *.workshop.local -> 127.0.0.1 address = "/.workshop.local/127.0.0.1"; - + # Use upstream DNS for everything else server = [ "8.8.8.8" "1.1.1.1" ]; - + # Listen on all interfaces (important for VM/container access) listen-address = [ "127.0.0.1" ]; - + # Bind to interfaces bind-interfaces = true; - + # Don't read /etc/hosts for our custom domains no-hosts = false; - + # Cache settings cache-size = 1000; log-queries = true; log-dhcp = true; - + # Local domain handling local = "/workshop.local/"; domain-needed = true; @@ -108,7 +144,7 @@ isoConfig // { tree nano dnsutils - dig # For DNS debugging + dig # For DNS debugging ]; # Auto-install abra and setup Docker Swarm @@ -118,7 +154,7 @@ isoConfig // { wants = [ "network-online.target" ]; script = '' export HOME=/home/workshop - + # Wait for network and services with better testing echo "Waiting for services to start..." for i in {1..30}; do @@ -129,7 +165,7 @@ isoConfig // { fi sleep 2 done - + # Test DNS resolution specifically for i in {1..20}; do if ${pkgs.dnsutils}/bin/nslookup test.workshop.local 127.0.0.1 >/dev/null 2>&1; then @@ -139,7 +175,7 @@ isoConfig // { echo "๐Ÿ”„ Waiting for DNS... (attempt $i)" sleep 2 done - + # Test Docker for i in {1..10}; do if ${pkgs.docker}/bin/docker info >/dev/null 2>&1; then @@ -148,22 +184,22 @@ isoConfig // { fi sleep 2 done - + # Install abra for workshop user if [ ! -f /home/workshop/.local/bin/abra ]; then sudo -u workshop mkdir -p /home/workshop/.local/bin cd /home/workshop sudo -u workshop ${pkgs.curl}/bin/curl -fsSL https://install.abra.coopcloud.tech | sudo -u workshop ${pkgs.bash}/bin/bash fi - + # Initialize Docker Swarm if ! ${pkgs.docker}/bin/docker info | grep -q "Swarm: active"; then ${pkgs.docker}/bin/docker swarm init --advertise-addr 127.0.0.1 2>/dev/null || true fi - + # Ensure workshop user is in docker group usermod -aG docker workshop - + # Test final DNS resolution if ${pkgs.dnsutils}/bin/nslookup test.workshop.local 127.0.0.1; then echo "๐ŸŽ‰ All services ready!" @@ -185,7 +221,7 @@ isoConfig // { echo "๐Ÿš€ CODE CRISPIES Workshop Environment" echo "Mode: Local Development + Cloud Access" echo "" - + # Test DNS immediately on login if command -v nslookup &> /dev/null; then if nslookup test.workshop.local 127.0.0.1 >/dev/null 2>&1; then @@ -208,7 +244,7 @@ isoConfig // { COMPREPLY=() cur="''${COMP_WORDS[COMP_CWORD]}" prev="''${COMP_WORDS[COMP_CWORD-1]}" - + case "''${prev}" in deploy|browser) opts="$ALL_RECIPES" @@ -226,107 +262,144 @@ isoConfig // { setup-traefik() { echo "๐Ÿ”ง Setting up local Traefik proxy..." - - # Test DNS first + + # Test DNS first if ! nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then echo "โŒ DNS not resolving *.workshop.local" echo "๐Ÿ”„ Restarting dnsmasq..." sudo systemctl restart dnsmasq sleep 3 - + if ! nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then echo "โŒ DNS still not working!" - echo "๐Ÿ” Debug info:" - echo " systemctl status dnsmasq" - echo " nslookup traefik.workshop.local 127.0.0.1" return 1 fi fi - + echo "โœ… DNS resolution working" - - # Rest of your existing setup-traefik function... + + # Ensure Docker Swarm is initialized + if ! docker info 2>/dev/null | grep -q "Swarm: active"; then + echo "๐Ÿ”ฅ Initializing Docker Swarm..." + docker swarm init --advertise-addr 127.0.0.1 || true + sleep 2 + fi + + # Create proxy network (CRITICAL for Traefik) + if ! docker network ls | grep -q "proxy"; then + echo "๐Ÿ“ก Creating proxy overlay network..." + docker network create -d overlay proxy + fi + + # Ensure abra is available if ! command -v abra &> /dev/null; then echo "โŒ Abra not found. Installing..." sudo systemctl restart workshop-abra-setup sleep 5 export PATH="$HOME/.local/bin:$PATH" fi - # Ensure Docker Swarm is ready - if ! docker info 2>/dev/null | grep -q "Swarm: active"; then - echo "๐Ÿ”ฅ Initializing Docker Swarm..." - docker swarm init --advertise-addr 127.0.0.1 || true - fi - # Create abra context if not exists - if ! abra server ls 2>/dev/null | grep -q "workshop-local"; then - echo "๐Ÿ— Creating local abra context..." + + # Check current server setup + echo "๐Ÿ“‹ Current servers:" + abra server ls || echo "No servers configured" + + # Add local server if not exists (default name is "default") + if ! abra server ls 2>/dev/null | grep -q "default"; then + echo "๐Ÿ— Adding local server context..." abra server add --local + sleep 2 fi - #echo "๐Ÿš€ Deploying Traefik..." - #abra app new traefik -S --domain=traefik.workshop.local --server=workshop-local - #abra app deploy traefik.workshop.local - - # Wait for Traefik to be ready - echo "โณ Waiting for Traefik to start..." - for i in {1..30}; do - if curl -s http://traefik.workshop.local >/dev/null 2>&1; then - echo "โœ… Traefik deployed! Dashboard: http://traefik.workshop.local" - echo "๐Ÿš€ Now you can deploy apps with 'deploy '" + + # Verify server is accessible + echo "๐Ÿ“‹ Servers after setup:" + abra server ls + + # Check if Traefik app already exists + if abra app ls 2>/dev/null | grep -q "traefik"; then + echo "โ„น๏ธ Traefik already configured" + traefik_domain=$(abra app ls | grep traefik | awk \'{print $1}\' | head -1) + echo "๐Ÿ“ Existing Traefik: $traefik_domain" + else + echo "๐Ÿš€ Creating new Traefik app..." + + # Use proper server context (default, not workshop-local) + abra app new traefik --domain=traefik.workshop.local --server=default + + # Configure Traefik environment + echo "โš™๏ธ Configuring Traefik..." + traefik_env_file="$HOME/.abra/servers/default/traefik.workshop.local.env" + + if [ -f "$traefik_env_file" ]; then + # Set required environment variables + if ! grep -q "LETS_ENCRYPT_EMAIL" "$traefik_env_file"; then + echo "LETS_ENCRYPT_EMAIL=workshop@local.dev" >> "$traefik_env_file" + fi + if ! grep -q "DASHBOARD_ENABLED" "$traefik_env_file"; then + echo "DASHBOARD_ENABLED=true" >> "$traefik_env_file" + fi + else + echo "โš ๏ธ Traefik env file not found at: $traefik_env_file" + fi + + echo "๐Ÿ“ฆ Deploying Traefik..." + abra app deploy traefik.workshop.local + + traefik_domain="traefik.workshop.local" + fi + + # Wait for Traefik to be ready + echo "โณ Waiting for Traefik to be ready..." + for i in {1..60}; do + if curl -s --connect-timeout 3 --max-time 5 http://traefik.workshop.local/ping >/dev/null 2>&1; then + echo "โœ… Traefik is ready! Dashboard: http://traefik.workshop.local" + echo "๐Ÿš€ You can now deploy apps with: deploy " return 0 fi + sleep 2 done - - echo "โš ๏ธ Traefik deployed but may still be starting..." - echo "๐Ÿ” Debug: docker service ls | curl -I http://traefik.workshop.local" + + echo "โš ๏ธ Traefik deployment timed out but may still be starting..." + echo "" + echo "๐Ÿ” Debug commands:" + echo " abra app ps traefik.workshop.local" + echo " abra app logs traefik.workshop.local" + echo " docker service ls" + echo " docker service logs \$(docker service ls --filter name=traefik -q)" } deploy() { if [ -z "$1" ]; then echo "Usage: deploy " - echo "Example: deploy wordpress" echo "Available recipes: $ALL_RECIPES" - echo "" - echo "๐Ÿ” Use tab completion or run 'recipes' for categorized list" return 1 fi - local recipe="$1" local domain="$recipe.workshop.local" - echo "๐Ÿš€ Deploying $recipe locally..." echo "Domain: $domain" - - if ! command -v abra &> /dev/null; then - echo "โŒ Abra not found. Run 'sudo systemctl restart workshop-abra-setup'" - return 1 + # Ensure Traefik is running first + if ! curl -s --max-time 3 http://traefik.workshop.local/ping >/dev/null 2>&1; then + echo "โš ๏ธ Traefik not responding. Setting up..." + setup-traefik || return 1 fi - - # Check if Traefik is running - if ! curl -s http://traefik.workshop.local >/dev/null 2>&1; then - echo "โš ๏ธ Traefik not detected. Running setup first..." - setup-traefik - fi - echo "๐Ÿ“ฆ Creating app: $recipe" - abra app new "$recipe" -S --domain="$domain" --server=workshop-local - + # Use correct server name + abra app new "$recipe" --domain="$domain" --server=default -S 2>/dev/null || \ + abra app new "$recipe" --domain="$domain" --server=default echo "๐Ÿš€ Deploying app: $domain" abra app deploy "$domain" - echo "โณ Waiting for deployment..." for i in {1..60}; do - if curl -s http://$domain >/dev/null 2>&1; then + if curl -s --max-time 3 http://$domain >/dev/null 2>&1; then echo "โœ… Deployed! Access at: http://$domain" - echo "๐ŸŒ Quick launch: browser $recipe" return 0 fi sleep 3 done - - echo "โš ๏ธ Deployment completed but app may still be starting..." - echo "๐Ÿ” Debug: docker service ls | dig @127.0.0.1 $domain +short" - echo "๐ŸŒ Try: browser $recipe (in a few moments)" + + echo "โš ๏ธ Deployment may still be starting..." + echo "๐Ÿ” Debug: abra app ps $domain" } connect() { @@ -337,7 +410,7 @@ isoConfig // { browser() { local target_url="about:blank" - + if [ -n "$1" ]; then # Specific app requested target_url="http://$1.workshop.local" @@ -345,7 +418,7 @@ isoConfig // { else echo "๐ŸŒ Opening Firefox browser" fi - + if [ -n "$DISPLAY" ]; then firefox "$target_url" & else @@ -360,7 +433,7 @@ isoConfig // { echo "โญ Tier 1 - Production Ready (Score 5):" echo " gitea mealie nextcloud" echo "" - echo "๐Ÿ”ง Tier 2 - Stable (Score 4):" + echo "๐Ÿ”ง Tier 2 - Stable (Score 4):" echo " gotosocial wordpress" echo "" echo "๐Ÿงช Tier 3 - Community (Score 3):" diff --git a/flake.nix b/flake.nix index 5a139cd..82a54c4 100644 --- a/flake.nix +++ b/flake.nix @@ -17,7 +17,7 @@ # Server names for cloud connections cloudServerNames = [ "hopper" - "curie" + "curie" "lovelace" "noether" "hamilton" @@ -34,8 +34,8 @@ ]; # Common configuration - commonConfig = { isLiveIso ? false }: - import ./common.nix { + commonConfig = { isLiveIso ? false }: + import ./common.nix { inherit pkgs cloudServerNames isLiveIso; }; in @@ -64,39 +64,41 @@ inherit system; modules = [ "${nixpkgs}/nixos/modules/virtualisation/qemu-vm.nix" - + (commonConfig { isLiveIso = false; }) - + ({ config, pkgs, lib, ... }: { - boot.loader.grub.enable = false; - boot.loader.generic-extlinux-compatible.enable = true; + boot.loader.grub.enable = false; + boot.loader.generic-extlinux-compatible.enable = true; - # Enable networking for VM - networking.hostName = "workshop-vm"; - networking.networkmanager.enable = true; - networking.firewall.enable = false; + # Enable networking for VM + networking.hostName = "workshop-vm"; + networking.networkmanager.enable = true; + networking.firewall.enable = false; - # Hybrid console configuration - serial primary, GUI available - boot.kernelParams = [ "console=ttyS0,115200" "console=tty1" ]; + # Hybrid console configuration - serial primary, GUI available + boot.kernelParams = [ "console=ttyS0,115200" "console=tty1" ]; - # VM specific settings - virtualisation.memorySize = 4096; - virtualisation.diskSize = 40000; + # VM specific settings + virtualisation.memorySize = 4096; + virtualisation.diskSize = 40000; - # Hybrid mode: GUI available but serial console primary - virtualisation.qemu.options = [ - "-display" "gtk" - "-monitor" "stdio" - ]; - # Fix the auto-login conflict with mkForce - services.displayManager.autoLogin = lib.mkForce { - enable = true; - user = "workshop"; - }; - # Keep GUI session commands for when GUI is used - services.xserver.displayManager.sessionCommands = '' - ${pkgs.xfce.xfce4-terminal}/bin/xfce4-terminal --fullscreen --title="Workshop Terminal" & - ''; + # Hybrid mode: GUI available but serial console primary + virtualisation.qemu.options = [ + "-display" + "gtk" + "-monitor" + "stdio" + ]; + # Fix the auto-login conflict with mkForce + services.displayManager.autoLogin = lib.mkForce { + enable = true; + user = "workshop"; + }; + # Keep GUI session commands for when GUI is used + services.xserver.displayManager.sessionCommands = '' + ${pkgs.xfce.xfce4-terminal}/bin/xfce4-terminal --fullscreen --title="Workshop Terminal" & + ''; }) ]; };