d23ce1939e
Invert step 6 default: pwsh is no longer installed. Add --with-pwsh to opt in; keep --skip-pwsh as a deprecated no-op alias. Update usage/help. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
287 lines
12 KiB
Bash
Executable File
287 lines
12 KiB
Bash
Executable File
#!/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 <<EOF
|
|
Uso: sudo bash $(basename "$0") -d <disco> [opzioni]
|
|
|
|
Obbligatorio:
|
|
-d <disco> Partizione CI (es. /dev/sdf1 o /dev/sdf)
|
|
|
|
Opzionali:
|
|
-r <path> CI root (default: /var/lib/ci)
|
|
-u <utente> 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 <disco> è 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."
|