refactor: Enhance local development environment and documentation

- .gitignore: Ignore result* files.
- Makefile: Refactored to improve clarity, remove unused targets, add new testing targets (test-usb, test-vm), and streamline cloud deployment commands.
- README.md: Significantly updated to reflect the new single-participant environment focus, refine the learning flow, update commands, and improve overall readability.
- flake.nix: Modified to enable Docker for local development, adjust desktop environment behavior (manual start), and implement improved systemd services for container setup and health monitoring, including retry logic.
This commit is contained in:
2025-08-15 04:33:23 +02:00
parent d295e2b91d
commit cbbebbd35d
4 changed files with 342 additions and 345 deletions

318
flake.nix
View File

@@ -54,11 +54,14 @@
networking.networkmanager.enable = true;
networking.hostName = "workshop-live";
# Enable Docker for local development
virtualisation.docker.enable = true;
services.getty.autologinUser = "workshop";
users.users.workshop = {
isNormalUser = true;
shell = pkgs.zsh;
extraGroups = [ "networkmanager" "wheel" ];
extraGroups = [ "networkmanager" "wheel" "docker" ];
password = "";
};
@@ -66,18 +69,98 @@
environment.systemPackages = with pkgs; [
openssh curl git networkmanager firefox xterm
docker docker-compose
# For local abra installation
bash wget jq tree nano
];
# Auto-install abra on boot
systemd.services.workshop-abra-setup = {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" "docker.service" ];
wants = [ "network-online.target" ];
script = ''
export HOME=/home/workshop
# Wait for network
for i in {1..10}; do
if ${pkgs.curl}/bin/curl -s --max-time 5 google.com >/dev/null 2>&1; then
break
fi
sleep 3
done
# Install abra for workshop user (DO NOT change installation method)
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 local Docker Swarm
${pkgs.docker}/bin/docker swarm init --advertise-addr 127.0.0.1 2>/dev/null || true
# Add workshop user to docker group
usermod -aG docker workshop
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = "root";
};
};
programs.zsh = {
enable = true;
interactiveShellInit = ''
echo "CODE CRISPIES Workshop Environment"
echo "Available servers:"
${builtins.concatStringsSep "\n" (map (name:
"echo \" - ${name}.codecrispi.es\""
) allParticipantNames)}
echo "Mode: Local Development + Cloud Access"
echo ""
echo "Commands: connect <name> | recipes | help"
echo "🏠 Local Development:"
echo " recipes - Show available app recipes"
echo " deploy <recipe> - Deploy app locally (e.g., deploy wordpress)"
echo " browser - Launch Firefox"
echo " desktop - Start GUI session"
echo ""
echo " Cloud Access:"
echo " Available servers:"
${builtins.concatStringsSep "\n" (map (name:
"echo \" - ${name}.codecrispi.es\""
) allParticipantNames)}
echo " connect <name> - SSH to cloud server"
echo ""
echo "📚 Commands: recipes | deploy | connect | browser | desktop | help"
# Ensure abra is in PATH
export PATH="$HOME/.local/bin:$PATH"
deploy() {
if [ -z "$1" ]; then
echo "Usage: deploy <recipe>"
echo "Example: deploy wordpress"
echo "Run 'recipes' to see available options"
return 1
fi
local recipe="$1"
local domain="$recipe.workshop.local"
echo "🚀 Deploying $recipe locally..."
echo "Domain: $domain"
# Check if abra is available
if ! command -v abra &> /dev/null; then
echo " Abra not found. Run 'sudo systemctl restart workshop-abra-setup'"
return 1
fi
# Deploy with abra
abra app new "$recipe" -S --domain="$domain"
abra app deploy "$domain"
echo " Deployed! Access at: http://$domain"
echo "🌐 Open browser with: browser"
}
connect() {
[ -z "$1" ] && { echo "Usage: connect <name>"; return 1; }
@@ -88,44 +171,77 @@
recipes() {
echo "Available Co-op Cloud Recipes:"
echo ""
echo "Content Management:"
echo "📝 Content Management:"
echo " wordpress ghost hedgedoc dokuwiki mediawiki"
echo ""
echo "File & Collaboration:"
echo "📁 File & Collaboration:"
echo " nextcloud seafile collabora onlyoffice"
echo ""
echo "Communication:"
echo "💬 Communication:"
echo " jitsi-meet matrix-synapse rocketchat mattermost"
echo ""
echo "E-commerce & Business:"
echo "🛒 E-commerce & Business:"
echo " prestashop invoiceninja kimai pretix"
echo ""
echo "Development & Tools:"
echo " Development & Tools:"
echo " gitea drone n8n gitlab jupyter-lab"
echo ""
echo "Analytics & Monitoring:"
echo "📊 Analytics & Monitoring:"
echo " plausible matomo uptime-kuma grafana"
echo ""
echo "Media & Social:"
echo "🎵 Media & Social:"
echo " peertube funkwhale mastodon pixelfed jellyfin"
echo ""
echo "Deploy: abra app new <recipe> -S --domain=myapp.<name>.codecrispi.es"
echo "Browse all: https://recipes.coopcloud.tech"
echo "🚀 Local Deploy: deploy <recipe>"
echo " Cloud Deploy: connect <server> then use abra commands"
echo "📖 Browse all: https://recipes.coopcloud.tech"
}
browser() {
echo "🌐 Starting Firefox..."
if [ -n "$DISPLAY" ]; then
firefox &
else
echo " No GUI session. Run 'desktop' first"
fi
}
desktop() {
echo "🖥 Starting GUI session..."
if [ -z "$DISPLAY" ]; then
startx &
export DISPLAY=:0
sleep 3
echo " GUI started. Run 'browser' to open Firefox"
else
echo " GUI already running"
fi
}
help() {
echo "CODE CRISPIES Workshop Commands:"
echo ""
echo "connect <name> - SSH to your assigned server"
echo "recipes - Show available app recipes"
echo "sudo nmcli dev wifi connect SSID password PASSWORD"
echo "🏠 Local Development:"
echo " recipes - Show all available app recipes"
echo " deploy <recipe> - Deploy app locally (e.g., deploy wordpress)"
echo " browser - Launch Firefox browser"
echo " desktop - Start GUI desktop session"
echo ""
echo "Examples:"
echo " connect hopper"
echo " sudo nmcli dev wifi connect CODE_CRISPIES_GUEST password workshop2024"
echo " Cloud Access:"
echo " connect <name> - SSH to cloud server (e.g., connect hopper)"
echo ""
echo "🔧 System:"
echo " sudo nmcli dev wifi connect SSID password PASSWORD"
echo " sudo systemctl restart workshop-abra-setup # Reinstall abra"
echo ""
echo "📚 Learning Flow:"
echo " 1. Try local: recipes deploy wordpress browser"
echo " 2. Try cloud: connect hopper same abra commands"
echo ""
echo "Available servers: ${builtins.concatStringsSep " " allParticipantNames}"
}
export -f connect recipes help
export -f deploy connect recipes browser desktop help
'';
};
@@ -134,16 +250,17 @@
desktopManager.xfce.enable = true;
displayManager = {
lightdm.enable = true;
autoLogin.enable = true;
autoLogin.user = "workshop";
autoLogin.enable = false; # Manual desktop start
};
};
# Don't auto-start GUI, let user choose
systemd.user.services.workshop-welcome = {
wantedBy = [ "graphical-session.target" ];
after = [ "graphical-session.target" ];
script = "${pkgs.xterm}/bin/xterm -title 'CODE CRISPIES Workshop' -e 'zsh' &";
serviceConfig.Type = "forking";
wantedBy = [ "default.target" ];
script = ''
echo "Welcome! Run 'desktop' to start GUI session"
'';
serviceConfig.Type = "oneshot";
};
})
];
@@ -317,7 +434,7 @@
) (builtins.length participantNames));
};
# Dynamic container generation
# Dynamic container generation with improved stability
containers = builtins.listToAttrs (builtins.genList
(i:
let
@@ -331,6 +448,9 @@
privateNetwork = true;
hostAddress = "192.168.100.1";
localAddress = ip;
# Add restart policy for container itself
restartIfChanged = true;
config = {
system.stateVersion = "25.05";
@@ -363,61 +483,111 @@
docker curl git wget jq bash nano tree
];
# Improved workshop setup with retry logic
systemd.services.workshop-setup = {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" "docker.service" ];
wants = [ "network-online.target" ];
script = ''
echo "Setting up ${name} container..."
# Wait for network connectivity
for i in {1..15}; do
if ${pkgs.curl}/bin/curl -s --max-time 5 google.com >/dev/null 2>&1; then
echo "Network ready"
break
fi
echo "Waiting for network... ($i/15)"
sleep 3
done
# Initialize Docker Swarm
${pkgs.docker}/bin/docker swarm init --advertise-addr ${ip} || echo "Swarm already initialized or failed"
# Install abra
export HOME=/root
if [ ! -f /root/.local/bin/abra ]; then
echo "Installing abra..."
${pkgs.curl}/bin/curl -fsSL https://install.abra.coopcloud.tech | ${pkgs.bash}/bin/bash
echo "Abra installed to /root/.local/bin/abra"
fi
# Setup PATH in .bashrc
if ! grep -q "/.local/bin" /root/.bashrc 2>/dev/null; then
echo 'export PATH="$HOME/.local/bin:$PATH"' >> /root/.bashrc
fi
# Create system symlink for abra
if [ -f /root/.local/bin/abra ]; then
ln -sf /root/.local/bin/abra /usr/local/bin/abra 2>/dev/null || true
fi
# Add abra server config
if [ -f /root/.local/bin/abra ]; then
export PATH="/root/.local/bin:$PATH"
/root/.local/bin/abra server add ${name}.local 2>/dev/null || echo "Server already added or command failed"
fi
echo "${name} container ready!"
echo "SSH: ssh root@${ip} (password: root)"
echo "Workshop user: ssh workshop@${ip} (password: workshop)"
echo "Abra: Available via 'abra' command"
'';
# Add restart capability for failed setups
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
StandardOutput = "journal";
StandardError = "journal";
TimeoutStartSec = "300";
Restart = "on-failure";
RestartSec = "30s";
StartLimitBurst = 3;
StartLimitIntervalSec = "10min";
};
script = ''
set -e
echo "Setting up ${name} container (attempt started)..."
# Wait for network connectivity with timeout
network_ready=false
for i in {1..20}; do
if ${pkgs.curl}/bin/curl -s --max-time 5 google.com >/dev/null 2>&1; then
echo "Network ready after $i attempts"
network_ready=true
break
fi
echo "Waiting for network... ($i/20)"
sleep 5
done
if [ "$network_ready" != "true" ]; then
echo " Network failed after 20 attempts"
exit 1
fi
# Initialize Docker Swarm (idempotent)
if ! ${pkgs.docker}/bin/docker info | grep -q "Swarm: active"; then
echo "Initializing Docker Swarm..."
${pkgs.docker}/bin/docker swarm init --advertise-addr ${ip} || {
echo "Swarm init failed, but continuing..."
}
else
echo "Docker Swarm already active"
fi
# Install abra (DO NOT change installation method - keep curl approach)
export HOME=/root
if [ ! -f /root/.local/bin/abra ]; then
echo "Installing abra..."
${pkgs.curl}/bin/curl -fsSL https://install.abra.coopcloud.tech | ${pkgs.bash}/bin/bash
echo "Abra installed to /root/.local/bin/abra"
else
echo "Abra already installed"
fi
# Setup PATH in .bashrc (idempotent)
if ! grep -q "/.local/bin" /root/.bashrc 2>/dev/null; then
echo 'export PATH="$HOME/.local/bin:$PATH"' >> /root/.bashrc
fi
# Create system symlink for abra (idempotent)
if [ -f /root/.local/bin/abra ]; then
ln -sf /root/.local/bin/abra /usr/local/bin/abra 2>/dev/null || true
fi
# Add abra server config (idempotent)
if [ -f /root/.local/bin/abra ]; then
export PATH="/root/.local/bin:$PATH"
/root/.local/bin/abra server add ${name}.local 2>/dev/null || echo "Server config handled"
fi
echo " ${name} container setup completed successfully!"
echo "SSH: ssh root@${ip} (password: root)"
echo "Workshop user: ssh workshop@${ip} (password: workshop)"
echo "Abra: Available via 'abra' command"
'';
};
# Add container health monitoring
systemd.services.workshop-health = {
wantedBy = [ "multi-user.target" ];
after = [ "workshop-setup.service" ];
serviceConfig = {
Type = "simple";
Restart = "always";
RestartSec = "60s";
ExecStart = "${pkgs.writeScript "health-check" ''
#!/bin/bash
while true; do
# Check if abra is accessible
if [ -f /root/.local/bin/abra ]; then
export PATH="/root/.local/bin:$PATH"
/root/.local/bin/abra --version >/dev/null 2>&1 || {
echo " Abra health check failed, triggering restart"
systemctl restart workshop-setup.service
}
fi
sleep 300 # Check every 5 minutes
done
''}";
};
};