#!/usr/bin/env bash # setup-host-linux.sh — Bootstrap CI host Linux Mint (Fase B, Step B1) # # Equivalente di Setup-Host.ps1 per l'host Linux. # Eseguire come root: sudo bash setup-host-linux.sh -d /dev/sdf1 [opzioni] # # Cosa fa: # 1. Crea l'utente di servizio ci-runner # 2. Monta la partizione CI su /var/lib/ci via fstab (boot automatico) # Se la partizione ha già una sottocartella ci/, la riorganizza # portando i contenuti alla root della partizione (nessun dato perso) # 3. Crea il layout directory e permessi # 4. Installa Python 3.11+ e crea venv produzione in /opt/ci/venv # 5. (Opzionale) Clona il repo e installa il package nel venv # 6. (Opt-in, --with-pwsh) Installa PowerShell Core # L'host Linux NON ha più bisogno di pwsh per il normale funzionamento # (orchestratore Python). Installalo solo se ti serve per script legacy. set -euo pipefail # ── Defaults ───────────────────────────────────────────────────────────────── CI_DISK="" CI_ROOT="/var/lib/ci" CI_USER="ci-runner" OPT_CI="/opt/ci" REPO_URL="https://gitea.emulab.it/Simone/local-ci-cd-system.git" SKIP_PYTHON=false WITH_PWSH=false SKIP_CLONE=false # ── Colori ─────────────────────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' info() { echo -e "${GREEN}==>${NC} $*"; } warn() { echo -e "${YELLOW} ! $*${NC}"; } die() { echo -e "${RED}ERROR: $*${NC}" >&2; exit 1; } # ── Uso ────────────────────────────────────────────────────────────────────── usage() { cat < [opzioni] Obbligatorio: -d Partizione CI (es. /dev/sdf1 o /dev/sdf) Opzionali: -r CI root (default: /var/lib/ci) -u Utente di servizio (default: ci-runner) --skip-python Salta installazione Python/venv --skip-clone Salta clone repo + pip install --with-pwsh Installa PowerShell Core (opt-in; NON installato di default). L'host Linux non ha più bisogno di pwsh per il normale funzionamento — usa questo flag solo per script legacy. --skip-pwsh DEPRECATO: no-op. pwsh non è più installato di default. Esempi: sudo bash setup-host-linux.sh -d /dev/sdf1 sudo bash setup-host-linux.sh -d /dev/sdf1 --with-pwsh sudo bash setup-host-linux.sh -d /dev/sdf1 --skip-clone EOF exit 1 } # ── Parse args ─────────────────────────────────────────────────────────────── [[ $# -eq 0 ]] && usage while [[ $# -gt 0 ]]; do case $1 in -d) CI_DISK="$2"; shift 2 ;; -r) CI_ROOT="$2"; shift 2 ;; -u) CI_USER="$2"; shift 2 ;; --skip-python) SKIP_PYTHON=true; shift ;; --with-pwsh) WITH_PWSH=true; shift ;; --skip-pwsh) echo "WARN: --skip-pwsh è deprecato e non ha più effetto: pwsh non è installato di default (usa --with-pwsh per installarlo)." >&2 shift ;; --skip-clone) SKIP_CLONE=true; shift ;; -h|--help) usage ;; *) die "Parametro sconosciuto: $1"; ;; esac done [[ -z "$CI_DISK" ]] && die "-d è obbligatorio" [[ $EUID -ne 0 ]] && die "Eseguire come root: sudo bash $0 ..." [[ ! -b "$CI_DISK" ]] && die "$CI_DISK non è un block device" echo "" echo "╔══════════════════════════════════════════════════════╗" echo "║ CI Host Setup — Linux Mint (Fase B / B1) ║" echo "╚══════════════════════════════════════════════════════╝" echo " Disco CI : $CI_DISK" echo " CI root : $CI_ROOT" echo " Utente : $CI_USER" echo "" # ── Step 1: utente di servizio ──────────────────────────────────────────────── info "[1/6] Utente di servizio $CI_USER" if id "$CI_USER" &>/dev/null; then warn "Utente $CI_USER esiste già — skip" else useradd -r -m -s /bin/bash "$CI_USER" echo " Creato utente $CI_USER" fi # ── Step 2: mount partizione CI ─────────────────────────────────────────────── info "[2/6] Mount $CI_DISK → $CI_ROOT" if mountpoint -q "$CI_ROOT" 2>/dev/null; then warn "$CI_ROOT già montato — skip riorganizzazione e fstab" else # Recupera UUID e filesystem UUID=$(blkid -s UUID -o value "$CI_DISK" 2>/dev/null) FS_TYPE=$(blkid -s TYPE -o value "$CI_DISK" 2>/dev/null) [[ -z "$UUID" ]] && die "Impossibile determinare UUID di $CI_DISK" [[ -z "$FS_TYPE" ]] && die "Impossibile determinare filesystem di $CI_DISK" echo " UUID=$UUID TYPE=$FS_TYPE" # Mount temporaneo per eventuale riorganizzazione TMP_MNT=$(mktemp -d /mnt/ci-setup-XXXXXX) mount "$CI_DISK" "$TMP_MNT" if [[ -d "$TMP_MNT/ci" ]]; then echo " Trovata sottocartella ci/ — riorganizzazione in corso..." # Sposta tutto il contenuto di ci/ alla root della partizione shopt -s dotglob nullglob for item in "$TMP_MNT/ci/"*; do name=$(basename "$item") if [[ -e "$TMP_MNT/$name" ]]; then warn " Conflitto: $name già esiste alla root — skip spostamento" else mv "$item" "$TMP_MNT/" echo " Spostato: ci/$name → /" fi done shopt -u dotglob nullglob if [[ -z "$(ls -A "$TMP_MNT/ci" 2>/dev/null)" ]]; then rmdir "$TMP_MNT/ci" echo " Rimossa cartella ci/ vuota" else warn " ci/ non è vuota dopo lo spostamento — controlla manualmente" fi else echo " Nessuna sottocartella ci/ trovata — nessuna riorganizzazione necessaria" fi umount "$TMP_MNT" rmdir "$TMP_MNT" # Mount point e fstab mkdir -p "$CI_ROOT" if grep -q "UUID=$UUID" /etc/fstab; then warn "Entry fstab per UUID=$UUID già presente — skip" else echo "UUID=$UUID $CI_ROOT $FS_TYPE defaults,nofail 0 2" >> /etc/fstab echo " Aggiunta entry fstab" fi mount -a mountpoint -q "$CI_ROOT" || die "mount -a fallito per $CI_ROOT" echo " Montato OK: $(df -h "$CI_ROOT" | tail -1)" fi # ── Step 3: layout directory e permessi ─────────────────────────────────────── info "[3/6] Layout directory e permessi" # Installa acl se mancante if ! command -v setfacl &>/dev/null; then echo " Installazione pacchetto acl..." apt-get install -y acl -qq fi mkdir -p \ "$CI_ROOT"/{build-vms,artifacts,templates,keys,logs,runner} \ "$OPT_CI" \ /etc/ci/keys chown -R "$CI_USER:$CI_USER" "$CI_ROOT" "$OPT_CI" chmod 750 "$CI_ROOT" "$OPT_CI" for d in build-vms artifacts templates keys logs runner; do chmod 750 "$CI_ROOT/$d" done chmod 700 /etc/ci/keys # ACL di default su build-vms (ci-runner eredita rwX sui nuovi file) setfacl -d -m "u:$CI_USER:rwX" "$CI_ROOT/build-vms" echo " Layout OK — ACL default su build-vms applicata" # ── Step 4: Python 3.11+ e venv ────────────────────────────────────────────── if [[ "$SKIP_PYTHON" == false ]]; then info "[4/6] Python 3.11+ e venv produzione" # Cerca la prima versione di Python >= 3.11 già installata PYTHON_BIN="" for cmd in python3 python3.13 python3.12 python3.11; do if command -v "$cmd" &>/dev/null; then ver=$("$cmd" -c 'import sys; v=sys.version_info; print(v.major*100+v.minor)' 2>/dev/null) if [[ "${ver:-0}" -ge 311 ]]; then PYTHON_BIN="$cmd" break fi fi done if [[ -n "$PYTHON_BIN" ]]; then echo " Python >= 3.11 già presente: $($PYTHON_BIN --version) ($PYTHON_BIN)" apt-get install -y "python3-venv" git -qq 2>/dev/null || true else echo " Nessun Python >= 3.11 trovato — installazione python3.11..." apt-get install -y python3.11 python3.11-venv git -qq PYTHON_BIN="python3.11" fi if [[ -d "$OPT_CI/venv" ]]; then warn "venv $OPT_CI/venv già esistente — skip" else "$PYTHON_BIN" -m venv "$OPT_CI/venv" chown -R "$CI_USER:$CI_USER" "$OPT_CI/venv" echo " venv creato in $OPT_CI/venv ($($PYTHON_BIN --version))" fi else info "[4/6] Python/venv — SKIPPED" fi # ── Step 5: Clone repo e pip install ───────────────────────────────────────── if [[ "$SKIP_CLONE" == false ]]; then info "[5/6] Clone repo e pip install" REPO_DIR="$OPT_CI/local-ci-cd-system" if [[ -d "$REPO_DIR/.git" ]]; then warn "Repo già presente in $REPO_DIR — skip clone (eseguire git pull manualmente)" else echo " Clone repo..." sudo -u "$CI_USER" git clone "$REPO_URL" "$REPO_DIR" fi echo " pip install..." sudo -u "$CI_USER" "$OPT_CI/venv/bin/pip" install -q -e "$REPO_DIR" verify_out=$(sudo -u "$CI_USER" "$OPT_CI/venv/bin/python" -m ci_orchestrator --help 2>&1) \ || die "ci_orchestrator non importabile" echo " Verifica OK: ${verify_out%%$'\n'*}" else info "[5/6] Clone repo — SKIPPED" fi # ── Step 6: PowerShell Core (opt-in via --with-pwsh) ────────────────────────── if [[ "$WITH_PWSH" == true ]]; then info "[6/6] PowerShell Core" if command -v pwsh &>/dev/null; then warn "pwsh già installato: $(pwsh --version) — skip" else echo " Installazione PowerShell Core..." apt-get install -y wget apt-transport-https software-properties-common -qq # Linux Mint riporta la propria versione in VERSION_ID; serve quella Ubuntu base if [[ -f /etc/upstream-release/lsb-release ]]; then UBUNTU_VER=$(grep DISTRIB_RELEASE /etc/upstream-release/lsb-release | cut -d= -f2) else source /etc/os-release UBUNTU_VER="$VERSION_ID" fi wget -q "https://packages.microsoft.com/config/ubuntu/${UBUNTU_VER}/packages-microsoft-prod.deb" \ -O /tmp/ms-prod.deb dpkg -i /tmp/ms-prod.deb apt-get update -qq 2>&1 || true # ignora errori di repo terzi (es. chiavi GPG scadute) apt-get install -y powershell -qq rm /tmp/ms-prod.deb echo " Installato: $(pwsh --version)" fi else info "[6/6] PowerShell Core — SKIPPED (opt-in con --with-pwsh)" fi # ── Riepilogo ───────────────────────────────────────────────────────────────── echo "" echo "╔══════════════════════════════════════════════════════╗" echo "║ Setup completato ║" echo "╚══════════════════════════════════════════════════════╝" echo "" echo " CI_ROOT : $CI_ROOT ($(df -h "$CI_ROOT" | awk 'NR==2{print $4}') liberi)" echo " CI_USER : $CI_USER" echo " venv : $OPT_CI/venv" echo "" echo "Passi successivi (manuali):" echo " B2 — Validare template VM in $CI_ROOT/templates/" echo " find $CI_ROOT/templates -name '*.vmx' -exec vmrun -T ws listSnapshots {} \\;" echo " B3 — Copiare chiavi SSH in /etc/ci/keys/ e configurare keyring" echo " B4 — Installare act_runner come systemd service" echo " B5 — Installare timer systemd da deploy/systemd/" echo "" echo "Vedi plans/PhaseB-user-checklist.md per i dettagli."