prvng_core/scripts/build-nixos-image-remote.sh
Jesús Pérez 316622a78f
merge(refactor/lazy-loading): ADR-025 lazy-loading complete
557 files merged. Conflicts resolved:
  - CHANGELOG.md: took refactor/lazy-loading (session changelog)
  - versions.ncl: took refactor/lazy-loading (adds typedialog entries)
2026-04-17 23:09:56 +01:00

197 lines
5.7 KiB
Bash
Executable file

#!/bin/bash
# Build NixOS image on remote Hetzner server (cross-platform builds)
# Usage: ./build-nixos-image-remote.sh [role] [location] [project_path]
# Output: SNAPSHOT_ID written to stdout on success
set -euo pipefail
# Configuration
ROLE="${1:-cp}"
LOCATION="${2:-nbg1}"
PROJECT_PATH="${3:-.}"
SSH_KEY="${SSH_KEY:-htz_ops}"
HCLOUD_TOKEN="${HCLOUD_TOKEN:?HCLOUD_TOKEN required}"
# Derived
TEMP_NAME="build-nixos-${ROLE}-$$"
FLAKE_DIR="workspaces/librecloud_hetzner/nixos"
TIMESTAMP=$(date -u +%Y-%m-%dT%H%M%SZ)
DESCRIPTION="nixos-${ROLE}-aarch64-${TIMESTAMP}"
echo "=== Building NixOS ${ROLE} image on Hetzner ==="
echo "Temp server: $TEMP_NAME | Role: $ROLE | Location: $LOCATION"
# Create temporary build server
echo "=== 1. Creating temp server $TEMP_NAME ==="
hcloud server create \
--name "$TEMP_NAME" \
--type cax11 \
--location "$LOCATION" \
--image debian-12 \
--ssh-key "$SSH_KEY" > /dev/null
SERVER_ID=$(hcloud server describe "$TEMP_NAME" -o format='{{.ID}}')
SERVER_IP=$(hcloud server describe "$TEMP_NAME" -o format='{{.PublicNet.IPv4.IP}}')
echo "Created: $TEMP_NAME (ID=$SERVER_ID, IP=$SERVER_IP)"
cleanup() {
echo "=== Cleanup: deleting server ==="
hcloud server delete "$SERVER_ID" 2>/dev/null || true
rm -f /tmp/build-remote-*.sh /tmp/project-build.tar.gz
}
trap cleanup EXIT
# Wait for SSH
echo "=== 2. Waiting for SSH connectivity ==="
SSH_READY=0
for i in $(seq 1 60); do
if ssh -o StrictHostKeyChecking=no -o ConnectTimeout=3 -o BatchMode=yes root@"${SERVER_IP}" true 2>/dev/null; then
echo "SSH ready after $((i*5)) seconds"
SSH_READY=1
break
fi
printf "."
sleep 5
done
if [ "$SSH_READY" -eq 0 ]; then
echo ""
echo "ERROR: SSH timeout after 300 seconds"
echo "Server: $SERVER_IP"
echo "Check: ssh -o StrictHostKeyChecking=no root@${SERVER_IP}"
exit 1
fi
echo ""
# Transfer project
echo "=== 3. Transferring project ==="
SSH_OPTS="-o StrictHostKeyChecking=no -o ServerAliveInterval=60 -o ServerAliveCountMax=10"
tar -czf /tmp/project-build.tar.gz \
--exclude='.git/objects' \
--exclude='.git/logs' \
--exclude='.nix' \
--exclude='result*' \
--exclude='*.img' \
--exclude='target' \
--exclude='.coder' \
-C "$PROJECT_PATH" .
SIZE=$(ls -lh /tmp/project-build.tar.gz | awk '{print $5}')
echo "Uploading $SIZE..."
scp $SSH_OPTS /tmp/project-build.tar.gz "root@${SERVER_IP}:/tmp/" || {
echo "ERROR: Failed to upload project"
exit 1
}
ssh $SSH_OPTS root@"${SERVER_IP}" "cd /tmp && tar -xzf project-build.tar.gz && rm project-build.tar.gz && echo 'Project extracted'" || {
echo "ERROR: Failed to extract project"
exit 1
}
echo "Project transferred"
# Install Nix and build
echo "=== 4. Installing Nix on server ==="
cat > /tmp/build-remote-install.sh << 'INSTALL_NIX'
#!/bin/bash
set -euo pipefail
apt-get update -qq
apt-get install -y -qq curl xz-utils
curl -L https://nixos.org/nix/install | bash -s -- --no-daemon --yes 2>/dev/null
export PATH="${HOME}/.nix-profile/bin:$PATH"
nix --version
INSTALL_NIX
scp $SSH_OPTS /tmp/build-remote-install.sh "root@${SERVER_IP}:/tmp/"
ssh $SSH_OPTS root@"${SERVER_IP}" bash /tmp/build-remote-install.sh
echo "=== 5. Building image ==="
cat > /tmp/build-remote-build.sh << BUILD_IMAGE
#!/bin/bash
set -euo pipefail
export PATH="\${HOME}/.nix-profile/bin:\$PATH"
export NIX_CONFIG="experimental-features = nix-command flakes"
cd /tmp
echo "Building ${ROLE} image..."
nix build "${FLAKE_DIR}#packages.aarch64-linux.${ROLE}-image" \
--out-link "/tmp/nixos-${ROLE}-image" \
--print-build-logs 2>&1 | tail -20
IMG=\$(find /tmp/nixos-${ROLE}-image -name "*.img" | head -1)
if [ -z "\$IMG" ]; then
echo "ERROR: image not found"
exit 1
fi
ls -lh "\$IMG"
echo "SUCCESS: Image built"
BUILD_IMAGE
scp $SSH_OPTS /tmp/build-remote-build.sh "root@${SERVER_IP}:/tmp/"
ssh $SSH_OPTS root@"${SERVER_IP}" bash /tmp/build-remote-build.sh
# Fetch image
echo "=== 6. Fetching image back ==="
mkdir -p /tmp/nixos-build
scp $SSH_OPTS "root@${SERVER_IP}:/tmp/nixos-${ROLE}-image/*.img" /tmp/nixos-build/ 2>/dev/null || {
echo "ERROR: Failed to fetch image"
exit 1
}
IMAGE_LOCAL=$(find /tmp/nixos-build -name "*.img" | head -1)
echo "Image: $(ls -lh "$IMAGE_LOCAL" | awk '{print $5, $9}')"
# Reboot and deploy
echo "=== 7. Rebooting into rescue ==="
hcloud server reboot "$SERVER_ID" --force
sleep 15
hcloud server enable-rescue "$SERVER_ID" --type linux64 --ssh-key "$SSH_KEY" > /dev/null
hcloud server reboot "$SERVER_ID"
echo "Waiting for rescue SSH..."
RESCUE_READY=0
for i in $(seq 1 60); do
if ssh $SSH_OPTS -o ConnectTimeout=3 -o BatchMode=yes root@"${SERVER_IP}" true 2>/dev/null; then
echo "Rescue ready"
RESCUE_READY=1
break
fi
printf "."
sleep 5
done
if [ "$RESCUE_READY" -eq 0 ]; then
echo ""
echo "ERROR: Rescue SSH timeout"
exit 1
fi
echo ""
# Write image to disk
echo "=== 8. Writing image to /dev/sda ==="
gzip -dc "$IMAGE_LOCAL" | ssh $SSH_OPTS root@"${SERVER_IP}" \
"dd of=/dev/sda bs=4M conv=fsync status=progress"
echo "=== 9. Powering off ==="
hcloud server poweroff "$SERVER_ID"
sleep 15
echo "=== 10. Creating snapshot ==="
SNAPSHOT_ID=$(hcloud server create-image "$SERVER_ID" \
--type snapshot \
--description "$DESCRIPTION" \
-o format='{{.ID}}')
echo ""
echo "════════════════════════════════════════"
echo "✓ BUILD SUCCESS"
echo "════════════════════════════════════════"
echo "SNAPSHOT_ID=$SNAPSHOT_ID"
echo ""
echo "Next: Update servers.ncl for role '$ROLE':"
echo " image = \"$SNAPSHOT_ID\""
echo "════════════════════════════════════════"
# Keep snapshot, delete server
trap - EXIT
hcloud server delete "$SERVER_ID"