#!/usr/bin/env bash # Lab-AI installer — curl -fsSL https://get.lab-ai.laby.io | sudo bash # Asks a few questions, then installs the `lab-ai` tool and one Lab-AI instance on this server. # Published by `lab-ai publish-get` for version 0d4a3607; the template is deploy/lab-ai/get.sh. set -euo pipefail IMAGE="registry.laby.io/lab-ai/sirocco" VERSION="0d4a3607" ROOT="${LAB_AI_ROOT:-/srv/lab-ai}" NAME=main # the one instance of a client server say() { echo "[lab-ai] $*"; } die() { echo "[lab-ai] $*" >&2; exit 1; } # The script itself arrives on stdin (curl | bash): the answers are read from the terminal. exec 3< "${LAB_AI_TTY:-/dev/tty}" || die "Lancez cette commande dans un terminal : elle pose des questions." ask() { # ask [default] [secret] local prompt="$2" answer [[ -n "${3:-}" ]] && prompt="$prompt [$3]" if [[ -n "${4:-}" ]]; then read -rsp "$prompt : " -u 3 answer; echo >&2; else read -rp "$prompt : " -u 3 answer; fi printf -v "$1" '%s' "${answer:-${3:-}}" } # Set KEY=VALUE in the server settings; the value travels by environment, never in argv. set_conf() { KEY="$1" VALUE="$2" python3 - "$ROOT/lab-ai.conf" <<'PY' import os, sys path, key, value = sys.argv[1], os.environ["KEY"], os.environ["VALUE"] lines = [l for l in open(path, encoding="utf-8").read().splitlines() if not l.startswith(key + "=")] open(path, "w", encoding="utf-8").write("\n".join([*lines, f"{key}={value}"]) + "\n") PY } # Does this name point at this machine (then the instance reaches it through the host)? is_local() { local ip; ip="$(getent ahostsv4 "$1" | awk 'NR == 1 {print $1}')" [[ -n "$ip" ]] && { [[ "$ip" == 127.* ]] || [[ " $(hostname -I) " == *" $ip "* ]]; } } # One function, called on the last line: a truncated download runs nothing, and no command # can read the rest of the script from stdin. main() { [[ "$(id -u)" == 0 ]] || die "Lancez avec sudo : curl -fsSL https://get.lab-ai.laby.io | sudo bash" missing="" for c in docker nginx certbot python3 curl; do command -v "$c" >/dev/null || missing+=" $c"; done docker compose version >/dev/null 2>&1 || missing+=" docker-compose-v2" [[ -z "$missing" ]] || die "À installer d'abord :$missing (Ubuntu / Debian : sudo apt install docker.io docker-compose-v2 nginx certbot python3 curl)" [[ ! -d "$ROOT/instances/$NAME" ]] || die "Lab-AI est déjà installé sur ce serveur. État : lab-ai list ; mise à jour : sudo lab-ai update " say "Installation de Lab-AI $VERSION. Cinq questions, puis tout est automatique." ask user "Identifiant fourni par KYLI (robot\$…)" ask token "Jeton fourni par KYLI (rien ne s'affiche)" "" secret ask host "Adresse de Lab-AI pour vos utilisateurs (ex. lab-ai.votre-domaine.fr)" ask laby "Adresse de votre Laby (https://…)" ask email "E-mail pour le certificat HTTPS (facultatif)" [[ "$host" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$ ]] \ || die "Adresse invalide : $host (attendu : lab-ai.votre-domaine.fr)" if ! is_local "$host"; then # Behind a NAT the public address is not on this machine: the certificate step decides. local go ask go "$host ne semble pas pointer vers ce serveur (DNS à créer ?). Continuer quand même ? o/N" [[ "$go" == [oOyY]* ]] || die "Créez l'enregistrement DNS de $host vers ce serveur, puis relancez." fi [[ "$laby" =~ ^https://[A-Za-z0-9.-]+(:[0-9]+)?/?$ ]] || die "Adresse de Laby invalide : $laby (attendu : https://…)" laby_host="${laby#https://}"; laby_host="${laby_host%%[:/]*}" getent ahostsv4 "$laby_host" >/dev/null || die "$laby_host est introuvable depuis ce serveur." say "Connexion au registre…" printf '%s' "$token" | docker login "${IMAGE%%/*}" -u "$user" --password-stdin >/dev/null \ || die "Identifiant ou jeton refusé. Vérifiez-les auprès de KYLI." say "Téléchargement de Lab-AI $VERSION (quelques minutes)…" docker pull "$IMAGE:$VERSION" >/dev/null kit="$(mktemp -d)" trap 'rm -rf "$kit"' EXIT docker run --rm --entrypoint tar "$IMAGE:$VERSION" -c -C /app deploy/lab-ai scripts/init_env.py | tar -x -C "$kit" install_opts=() # An nginx that already serves sites keeps answering every name that is not Lab-AI. # Read whole first: `grep -q` stopping early would fail the pipe (pipefail) and hide the sites. local sites; sites="$(nginx -T 2>/dev/null || true)" grep -Eq '^[[:space:]]*server[[:space:]]*\{' <<< "$sites" && install_opts+=(--no-default-site) bash "$kit/deploy/lab-ai/lab-ai" install ${install_opts[@]+"${install_opts[@]}"} set_conf IMAGE "$IMAGE" set_conf REGISTRY_USER "$user" set_conf REGISTRY_TOKEN "$token" [[ -n "$email" ]] && set_conf CERT_EMAIL "$email" add_opts=(--host "$host" --laby "$laby") is_local "$laby_host" && add_opts+=(--laby-local) "$ROOT/kit/lab-ai" add "$NAME" "${add_opts[@]}" "$ROOT/kit/lab-ai" deploy "$NAME" "$VERSION" \ || die "Lab-AI est installé mais n'a pas démarré. Journaux : sudo lab-ai logs $NAME ; nouvel essai : sudo lab-ai update $VERSION" say "Lab-AI est prêt : https://$host" say "Connexion avec un compte Laby. Les administrateurs de Lab-AI sont les membres du groupe" say "Laby « admin_ai » : créez-le dans Laby, puis saisissez la clé du modèle d'IA dans l'écran Admin." say "Mises à jour : sudo lab-ai update . Aide : lab-ai" } main "$@"