diff --git a/dev/Untitled-1.sh b/dev/Untitled-1.sh new file mode 100755 index 0000000..a92ec0e --- /dev/null +++ b/dev/Untitled-1.sh @@ -0,0 +1,462 @@ +#!/usr/bin/env bash +# docker_offline_tool.sh +# Assistant interactif (whiptail) pour préparer un package Docker + docker-compose + images + dockerfiles pour usage hors-ligne. +# Usage: chmod +x docker_offline_tool.sh && ./docker_offline_tool.sh +set -euo pipefail + +# ---------- Configuration ---------- +WORKDIR="${PWD}/docker_offline_package" +TMPDIR="${WORKDIR}/tmp" +OUT_TARBALL="${PWD}/docker_offline_bundle_$(date +%Y%m%d_%H%M%S).tar.gz" +WHIPTAIL="$(command -v whiptail || true)" +CURL="$(command -v curl || true)" +TAR="$(command -v tar || true)" +DOCKER_CMD="$(command -v docker || true)" + +mkdir -p "$WORKDIR" "$TMPDIR" "$WORKDIR/images" "$WORKDIR/dockerfiles" "$WORKDIR/docker_binaries" + +if [ -z "$WHIPTAIL" ]; then + echo "Le paquet 'whiptail' est requis. Installez-le (ex: apt install whiptail) puis relancez." + exit 1 +fi +if [ -z "$CURL" ] || [ -z "$TAR" ]; then + "$WHIPTAIL" --msgbox "Le script requiert 'curl' et 'tar'." 10 60 + exit 1 +fi + +# ---------- Helpers ---------- +info_box() { "$WHIPTAIL" --title "${2:-Info}" --msgbox "$1" 12 70; } +error_box() { "$WHIPTAIL" --title "Erreur" --msgbox "$1" 12 70; } + +detect_arch() { + arch=$(uname -m) + case "$arch" in + x86_64) echo "x86_64";; + aarch64|arm64) echo "aarch64";; + armv7l|armv7) echo "armv7";; + ppc64le) echo "ppc64le";; + s390x) echo "s390x";; + *) echo "$arch";; + esac +} + +# ---------- Dockerfile templates ---------- +create_dockerfile_menu() { + CHOICE=$("$WHIPTAIL" --title "Dockerfile" --menu "Choisir un template ou créer personnalisé" 18 70 8 \ + 1 "nginx (site statique)" \ + 2 "node (express sample)" \ + 3 "python (wheels offline)" \ + 4 "java (jar)" \ + 5 "personnalisé" 3>&1 1>&2 2>&3) || return + + DST="$WORKDIR/dockerfiles/$(date +%Y%m%d_%H%M%S)_$CHOICE" + mkdir -p "$DST" + case "$CHOICE" in + 1) + cat >"$DST/Dockerfile" <<'EOF' +FROM nginx:stable-alpine +COPY ./html /usr/share/nginx/html:ro +EXPOSE 80 +CMD ["nginx","-g","daemon off;"] +EOF + ;; + 2) + cat >"$DST/Dockerfile" <<'EOF' +FROM node:18-alpine +WORKDIR /app +COPY package*.json ./ +# place npm tarballs or node_modules in ./npm_offline for offline use +RUN npm ci --production || true +COPY . . +EXPOSE 3000 +CMD ["node","server.js"] +EOF + ;; + 3) + cat >"$DST/Dockerfile" <<'EOF' +FROM python:3.11-slim +WORKDIR /app +# place wheels in ./wheels +COPY ./wheels ./wheels +COPY requirements.txt . +RUN pip install --no-index --find-links=./wheels -r requirements.txt +COPY . . +CMD ["python","app.py"] +EOF + ;; + 4) + cat >"$DST/Dockerfile" <<'EOF' +FROM eclipse-temurin:17-jre-jammy +WORKDIR /app +COPY app.jar ./app.jar +EXPOSE 8080 +CMD ["java","-jar","/app/app.jar"] +EOF + ;; + 5) + BASE=$("$WHIPTAIL" --inputbox "Image de base (ex: debian:bookworm)" 10 60 "debian:bookworm" 3>&1 1>&2 2>&3) + RUNS=$("$WHIPTAIL" --inputbox "Commandes RUN (séparées par &&) - ex: apt update && apt install -y curl" 12 60 "apt update && apt install -y curl" 3>&1 1>&2 2>&3) + START=$("$WHIPTAIL" --inputbox "Commande de démarrage (CMD) - ex: /bin/bash" 8 60 "/bin/bash" 3>&1 1>&2 2>&3) + cat >"$DST/Dockerfile" <&1 1>&2 2>&3) || return + + # collect Dockerfile choice + DF_CHOICES=() + for d in "$WORKDIR"/dockerfiles/*; do + [ -d "$d" ] || continue + DF_CHOICES+=("$d" "$(basename "$d")") + done + DF_CHOICES+=("Autre" "Choisir un dossier manuellement") + DF_SELECTED=$("$WHIPTAIL" --title "Sélection Dockerfile" --menu "Choisir" 18 70 12 "${DF_CHOICES[@]}" 3>&1 1>&2 2>&3) || return + if [ "$DF_SELECTED" = "Autre" ]; then + DF_SELECTED=$("$WHIPTAIL" --fselect "$PWD/" 20 70 3>&1 1>&2 2>&3) || return + fi + if [ ! -f "$DF_SELECTED/Dockerfile" ]; then + error_box "Aucun Dockerfile trouvé dans $DF_SELECTED" + return + fi + + IMAGE_NAME=$("$WHIPTAIL" --inputbox "Nom de l'image (ex: myapp:offline)" 10 60 "myapp:offline" 3>&1 1>&2 2>&3) || return + + # include extra dependencies (local or URL) + if "$WHIPTAIL" --yesno "Inclure des fichiers/dépendances dans le contexte (wheels, npm tarballs, .deb/.rpm, binaires) ?" 10 60; then + EXTRA_FILES=() + while true; do + entry=$("$WHIPTAIL" --inputbox "Chemin local ou URL (laisser vide pour terminer)" 10 70 "" 3>&1 1>&2 2>&3) || break + [ -z "$entry" ] && break + EXTRA_FILES+=("$entry") + done + else + EXTRA_FILES=() + fi + + # prepare context + CONTEXT_DIR="$TMPDIR/build_ctx_$(date +%s)" + rm -rf "$CONTEXT_DIR" + mkdir -p "$CONTEXT_DIR" + cp -r "$DF_SELECTED"/* "$CONTEXT_DIR/" 2>/dev/null || true + + # download/copy extras with gauge + for f in "${EXTRA_FILES[@]:-}"; do + if [[ "$f" =~ ^https?:// ]]; then + BASENAME="$(basename "$f")" + # download with curl and show progress in gauge by polling bytes + TEMP_DL="$CONTEXT_DIR/${BASENAME}" + # Use curl --progress-bar and parse percentages - simpler to show a small gauge animation + ( + echo "0"; echo "# Téléchargement: $BASENAME" + if ! curl -L --fail -o "$TEMP_DL" "$f" 2>/tmp/curl_err; then + echo "0"; echo "# Erreur téléchargement: $BASENAME (voir /tmp/curl_err)"; sleep 1 + else + echo "100"; echo "# Téléchargement terminé: $BASENAME" + fi + ) | "$WHIPTAIL" --gauge "Téléchargement..." 10 70 0 + else + if [ -e "$f" ]; then + cp -r "$f" "$CONTEXT_DIR/" || true + else + (echo "0"; echo "# Fichier introuvable: $f") | "$WHIPTAIL" --gauge "Attention" 8 60 0 + fi + fi + done + + # check docker present for local build + if [ -z "$DOCKER_CMD" ]; then + if ! "$WHIPTAIL" --yesno "docker n'est pas installé sur la machine préparatrice. Voulez-vous seulement préparer le contexte et sauvegarder (sans builder) ?" 10 70; then + info_box "Le build local nécessite docker. Installez docker ou choisissez 'préparer seulement'." + return + else + # just prepare context and return + info_box "Contexte préparé dans $CONTEXT_DIR. Vous pouvez transférer ce dossier vers une machine avec docker pour build." + return + fi + fi + + LOG="$TMPDIR/docker_build_$(date +%s).log" + : >"$LOG" + + # run docker build in background + # capture build progress by writing to logfile + ( + echo "=== Build started: $(date) ===" >>"$LOG" + if ! docker build -t "$IMAGE_NAME" "$CONTEXT_DIR" >>"$LOG" 2>&1; then + echo "=== Build FAILED at $(date) ===" >>"$LOG" + exit 2 + fi + echo "=== Build success: $(date) ===" >>"$LOG" + echo "=== Saving image to tar ===" >>"$LOG" + mkdir -p "$WORKDIR/images" + if ! docker save -o "$WORKDIR/images/$(echo "$IMAGE_NAME" | tr '/:' '__').tar" "$IMAGE_NAME" >>"$LOG" 2>&1; then + echo "=== Save FAILED: $(date) ===" >>"$LOG" + exit 3 + fi + echo "=== Image saved ===" >>"$LOG" + ) & + BUILD_PID=$! + + # show progress or console + if [ "$DISPLAY" = "progress" ]; then + # staged progress: 0->70 build, 70->95 save, 95->100 finalize + pct=0 + while kill -0 "$BUILD_PID" 2>/dev/null; do + # estimate: if logfile contains "Step X/X", try to infer progress + steps_done=$(grep -oE "Step [0-9]+/[0-9]+" "$LOG" | tail -n1 | grep -oE "[0-9]+/[0-9]+" || true) + if [ -n "$steps_done" ]; then + cur=$(echo "$steps_done" | cut -d/ -f1) + tot=$(echo "$steps_done" | cut -d/ -f2) + # scale to 0..70 + pct=$(( 1 + (cur * 69 / (tot == 0 ? 1 : tot)) )) + else + # fallback: increase slowly to show activity + pct=$(( (pct + 3) % 65 + 1 )) + fi + # show last lines as message + tail_msg=$(tail -n 6 "$LOG" 2>/dev/null || true) + { + echo "$pct" + echo "# Build en cours..." + echo "$tail_msg" + } | "$WHIPTAIL" --gauge "Construction: $IMAGE_NAME" 15 70 "$pct" + sleep 1 + done + + # After process ends, show final gauge to 95..100 while saving maybe + wait "$BUILD_PID" || RC=$? || true + # finalize + { + echo "95"; echo "# Finalisation..." + sleep 1 + echo "100"; echo "# Terminé" + } | "$WHIPTAIL" --gauge "Finalisation..." 8 60 0 + + # show full log + "$WHIPTAIL" --title "Log build" --textbox "$LOG" 25 90 + else + # console mode: show last lines live inside a repeated tailbox (keeps whiptail visible) + # We'll show a live-updating gauge that contains last lines (works similarly to console). + while kill -0 "$BUILD_PID" 2>/dev/null; do + tail_msg=$(tail -n 20 "$LOG" 2>/dev/null || true) + # compute a visual percentage heuristic: presence of "Saving" or "Successfully built" + if grep -q "Saving" "$LOG" 2>/dev/null || grep -q "Image is up to date" "$LOG" 2>/dev/null; then + pct=85 + elif grep -q "Successfully built" "$LOG" 2>/dev/null || grep -q "BUILD SUCCESS" "$LOG" 2>/dev/null; then + pct=70 + else + pct=30 + fi + { + echo "$pct" + echo "# Logs (mise à jour automatique):" + echo "$tail_msg" + } | "$WHIPTAIL" --gauge "Build live: $IMAGE_NAME (mode console)" 20 90 "$pct" + sleep 1 + done + wait "$BUILD_PID" || true + "$WHIPTAIL" --title "Log build complet" --textbox "$LOG" 25 90 + fi + + # check final result + if grep -q "Build FAILED" "$LOG" 2>/dev/null || grep -q "FAILED" "$LOG" 2>/dev/null; then + error_box "Le build a échoué. Consultez le log complet." + else + info_box "Image buildée et sauvegardée dans : $WORKDIR/images" + fi +} + +# ---------- Prepare offline docker + compose package ---------- +prepare_docker_offline_installer() { + arch_default=$(detect_arch) + ARCH=$("$WHIPTAIL" --inputbox "Architecture cible (détectée: $arch_default)" 10 60 "$arch_default" 3>&1 1>&2 2>&3) || return + + # ask Docker static version or latest + DOCKER_VER=$("$WHIPTAIL" --inputbox "Version Docker static (laisser vide pour latest disponible sur download.docker.com)" 10 70 "" 3>&1 1>&2 2>&3) || return + + BIN_DIR="$WORKDIR/docker_binaries" + mkdir -p "$BIN_DIR" + + # fetch docker static tarball + index_url="https://download.docker.com/linux/static/stable/${ARCH}/" + if [ -z "$DOCKER_VER" ]; then + # try to fetch listing and get latest docker-*.tgz + listing=$($CURL -fsSL "$index_url" || true) + # find docker-.tgz - fallback to first match + docker_tgz=$(echo "$listing" | grep -oE 'docker-[0-9]+\.[0-9]+\.[0-9]+\.tgz' | sort -V | tail -n1 || true) + if [ -z "$docker_tgz" ]; then + # fallback generic + error_box "Impossible de détecter automatiquement la version Docker sur $index_url. Indique manuellement le nom du fichier ou une version." + DOCKER_VER=$("$WHIPTAIL" --inputbox "Spécifie le nom exact du tarball (ex: docker-24.0.5.tgz)" 10 70 "" 3>&1 1>&2 2>&3) || return + docker_tgz="$DOCKER_VER" + fi + else + docker_tgz="$DOCKER_VER" + fi + + docker_url="${index_url}${docker_tgz}" + # download with progress gauge + ( + echo "0"; echo "# Téléchargement $docker_tgz ..." + if $CURL -fSL -o "$BIN_DIR/$docker_tgz" "$docker_url" 2>/tmp/docker_dl_err; then + echo "80"; echo "# Téléchargement terminé" + else + echo "0"; echo "# Erreur téléchargement: $docker_url (voir /tmp/docker_dl_err)" + "$WHIPTAIL" --msgbox "Erreur téléchargement Docker static. Vérifiez l'URL: $docker_url\nVoir /tmp/docker_dl_err" 12 70 + return + fi + echo "90"; echo "# Extraction test..." + sleep 1 + echo "100"; echo "# OK" + ) | "$WHIPTAIL" --gauge "Téléchargement des binaires Docker..." 12 70 0 + + # docker-compose plugin (CLI) - ask version or latest + COMPOSE_VER=$("$WHIPTAIL" --inputbox "Version docker-compose plugin (ex: v2.24.1) - laisser vide pour latest" 10 70 "" 3>&1 1>&2 2>&3) || return + if [ -z "$COMPOSE_VER" ]; then + # query GitHub API for latest release assets + api_json=$($CURL -fsSL "https://api.github.com/repos/docker/compose/releases/latest" || true) + compose_asset=$( + echo "$api_json" | grep -oP '"browser_download_url":\s*"\K[^"]*' | grep "docker-compose-linux-${ARCH}" | head -n1 || true + ) + if [ -z "$compose_asset" ]; then + # try generic naming + compose_asset=$( + echo "$api_json" | grep -oP '"browser_download_url":\s*"\K[^"]*' | grep "docker-compose-linux" | head -n1 || true + ) + fi + else + # build download url + case "$ARCH" in + x86_64) arch_name="x86_64";; + aarch64) arch_name="aarch64";; + armv7) arch_name="armv7";; + *) arch_name="$ARCH";; + esac + compose_asset="https://github.com/docker/compose/releases/download/${COMPOSE_VER}/docker-compose-linux-${arch_name}" + fi + + if [ -z "$compose_asset" ]; then + error_box "Impossible de déterminer l'asset docker-compose à télécharger. Donne une version manuellement." + return + fi + + ( + echo "0"; echo "# Téléchargement docker-compose..." + if $CURL -fSL -o "$BIN_DIR/docker-compose" "$compose_asset" 2>/tmp/compose_dl_err; then + chmod +x "$BIN_DIR/docker-compose" + echo "100"; echo "# docker-compose OK" + else + echo "0"; echo "# Erreur téléchargement docker-compose (voir /tmp/compose_dl_err)" + "$WHIPTAIL" --msgbox "Erreur téléchargement docker-compose: $compose_asset\nVoir /tmp/compose_dl_err" 12 70 + return + fi + ) | "$WHIPTAIL" --gauge "Téléchargement docker-compose..." 10 70 0 + + # create installer script for offline server + INSTALLER="$WORKDIR/install_docker_offline.sh" + cat >"$INSTALLER" <<'INSTALLER_EOF' +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +# Install static docker binaries included in this package +if [ "$(id -u)" -ne 0 ]; then + echo "Exécutez en root: sudo ./install_docker_offline.sh" + exit 1 +fi +echo "Installation des binaires Docker depuis le package..." +# find docker-*.tgz +shopt -s nullglob +for t in docker_binaries/docker-*.tgz; do + echo "Extraction $t ..." + tar xzf "$t" -C /tmp/docker_offline_extract || true + # copy binaries (best-effort) + DIR=$(ls -d /tmp/docker_offline_extract/* 2>/dev/null | head -n1 || true) + if [ -n "$DIR" ]; then + cp "$DIR"/* /usr/bin/ 2>/dev/null || true + chmod +x /usr/bin/docker* /usr/bin/dockerd* /usr/bin/containerd* 2>/dev/null || true + fi +done +# install docker-compose plugin if present +if [ -f docker_binaries/docker-compose ]; then + mkdir -p /usr/libexec/docker/cli-plugins 2>/dev/null || true + cp docker_binaries/docker-compose /usr/libexec/docker/cli-plugins/docker-compose 2>/dev/null || true + chmod +x /usr/libexec/docker/cli-plugins/docker-compose 2>/dev/null || true +fi +# Load saved images +if [ -d images ]; then + for img in images/*.tar; do + [ -f "$img" ] || continue + echo "Chargement image $img ..." + docker load -i "$img" || true + done +fi +echo "Installation offline terminée. Vérifiez 'docker --version' et 'docker compose version'." +INSTALLER_EOF + chmod +x "$INSTALLER" + + # assemble lightweight package structure + PKG_DIR="$WORKDIR/offline_bundle" + rm -rf "$PKG_DIR" + mkdir -p "$PKG_DIR/docker_binaries" "$PKG_DIR/images" "$PKG_DIR/dockerfiles" + cp -a "$WORKDIR/docker_binaries/"* "$PKG_DIR/docker_binaries/" 2>/dev/null || true + cp -a "$WORKDIR/images/"* "$PKG_DIR/images/" 2>/dev/null || true + cp -a "$WORKDIR/dockerfiles/"* "$PKG_DIR/dockerfiles/" 2>/dev/null || true + cp "$INSTALLER" "$PKG_DIR/" + + # create tarball + ( + echo "0"; echo "# Compression du bundle..." + sleep 1 + tar czf "$OUT_TARBALL" -C "$PKG_DIR" . + echo "100"; echo "# Bundle créé" + ) | "$WHIPTAIL" --gauge "Création du bundle offline..." 10 70 0 + + info_box "Bundle prêt" "Bundle créé : $OUT_TARBALL\nTransférez-le sur la machine hors-ligne et exécutez install_docker_offline.sh en root." +} + +# ---------- Assemble full bundle (if needed) ---------- +assemble_bundle() { + PKG_DIR="$WORKDIR/offline_bundle" + if [ ! -d "$PKG_DIR" ]; then + error_box "Aucun bundle préparé. Lancez d'abord la préparation (Préparer Docker offline)." + return + fi + tar czf "$OUT_TARBALL" -C "$PKG_DIR" . + info_box "Archive créée" "Archive: $OUT_TARBALL" +} + +# ---------- Menu principal ---------- +main_menu() { + while true; do + CHOICE=$("$WHIPTAIL" --title "Docker Offline Tool" --menu "Que voulez-vous faire ?" 18 70 10 \ + 1 "Créer / builder image Docker (préparer offline)" \ + 2 "Préparer package d'installation Docker + Compose (offline)" \ + 3 "Créer Dockerfile (templates ou perso)" \ + 4 "Assembler bundle final (.tar.gz)" \ + 5 "Quitter" 3>&1 1>&2 2>&3) || break + + case "$CHOICE" in + 1) build_image_prepare_offline ;; + 2) prepare_docker_offline_installer ;; + 3) create_dockerfile_menu ;; + 4) assemble_bundle ;; + 5) break ;; + *) error_box "Choix invalide" ;; + esac + done +} + +main_menu diff --git a/dev/docker_offline_bundle_20251021_231642.tar.gz b/dev/docker_offline_bundle_20251021_231642.tar.gz new file mode 100644 index 0000000..3f6e80b Binary files /dev/null and b/dev/docker_offline_bundle_20251021_231642.tar.gz differ diff --git a/dev/docker_offline_package/docker_binaries/docker-28.5.1.tgz b/dev/docker_offline_package/docker_binaries/docker-28.5.1.tgz new file mode 100644 index 0000000..afeb67c Binary files /dev/null and b/dev/docker_offline_package/docker_binaries/docker-28.5.1.tgz differ diff --git a/dev/docker_offline_package/docker_binaries/docker-compose b/dev/docker_offline_package/docker_binaries/docker-compose new file mode 100755 index 0000000..ea13ab3 Binary files /dev/null and b/dev/docker_offline_package/docker_binaries/docker-compose differ diff --git a/dev/docker_offline_package/install_docker_offline.sh b/dev/docker_offline_package/install_docker_offline.sh new file mode 100755 index 0000000..174a899 --- /dev/null +++ b/dev/docker_offline_package/install_docker_offline.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +# Install static docker binaries included in this package +if [ "$(id -u)" -ne 0 ]; then + echo "Exécutez en root: sudo ./install_docker_offline.sh" + exit 1 +fi +echo "Installation des binaires Docker depuis le package..." +# find docker-*.tgz +shopt -s nullglob +for t in docker_binaries/docker-*.tgz; do + echo "Extraction $t ..." + tar xzf "$t" -C /tmp/docker_offline_extract || true + # copy binaries (best-effort) + DIR=$(ls -d /tmp/docker_offline_extract/* 2>/dev/null | head -n1 || true) + if [ -n "$DIR" ]; then + cp "$DIR"/* /usr/bin/ 2>/dev/null || true + chmod +x /usr/bin/docker* /usr/bin/dockerd* /usr/bin/containerd* 2>/dev/null || true + fi +done +# install docker-compose plugin if present +if [ -f docker_binaries/docker-compose ]; then + mkdir -p /usr/libexec/docker/cli-plugins 2>/dev/null || true + cp docker_binaries/docker-compose /usr/libexec/docker/cli-plugins/docker-compose 2>/dev/null || true + chmod +x /usr/libexec/docker/cli-plugins/docker-compose 2>/dev/null || true +fi +# Load saved images +if [ -d images ]; then + for img in images/*.tar; do + [ -f "$img" ] || continue + echo "Chargement image $img ..." + docker load -i "$img" || true + done +fi +echo "Installation offline terminée. Vérifiez 'docker --version' et 'docker compose version'." diff --git a/dev/docker_offline_package/offline_bundle/docker_binaries/docker-28.5.1.tgz b/dev/docker_offline_package/offline_bundle/docker_binaries/docker-28.5.1.tgz new file mode 100644 index 0000000..afeb67c Binary files /dev/null and b/dev/docker_offline_package/offline_bundle/docker_binaries/docker-28.5.1.tgz differ diff --git a/dev/docker_offline_package/offline_bundle/docker_binaries/docker-compose b/dev/docker_offline_package/offline_bundle/docker_binaries/docker-compose new file mode 100755 index 0000000..ea13ab3 Binary files /dev/null and b/dev/docker_offline_package/offline_bundle/docker_binaries/docker-compose differ diff --git a/dev/docker_offline_package/offline_bundle/install_docker_offline.sh b/dev/docker_offline_package/offline_bundle/install_docker_offline.sh new file mode 100755 index 0000000..174a899 --- /dev/null +++ b/dev/docker_offline_package/offline_bundle/install_docker_offline.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +# Install static docker binaries included in this package +if [ "$(id -u)" -ne 0 ]; then + echo "Exécutez en root: sudo ./install_docker_offline.sh" + exit 1 +fi +echo "Installation des binaires Docker depuis le package..." +# find docker-*.tgz +shopt -s nullglob +for t in docker_binaries/docker-*.tgz; do + echo "Extraction $t ..." + tar xzf "$t" -C /tmp/docker_offline_extract || true + # copy binaries (best-effort) + DIR=$(ls -d /tmp/docker_offline_extract/* 2>/dev/null | head -n1 || true) + if [ -n "$DIR" ]; then + cp "$DIR"/* /usr/bin/ 2>/dev/null || true + chmod +x /usr/bin/docker* /usr/bin/dockerd* /usr/bin/containerd* 2>/dev/null || true + fi +done +# install docker-compose plugin if present +if [ -f docker_binaries/docker-compose ]; then + mkdir -p /usr/libexec/docker/cli-plugins 2>/dev/null || true + cp docker_binaries/docker-compose /usr/libexec/docker/cli-plugins/docker-compose 2>/dev/null || true + chmod +x /usr/libexec/docker/cli-plugins/docker-compose 2>/dev/null || true +fi +# Load saved images +if [ -d images ]; then + for img in images/*.tar; do + [ -f "$img" ] || continue + echo "Chargement image $img ..." + docker load -i "$img" || true + done +fi +echo "Installation offline terminée. Vérifiez 'docker --version' et 'docker compose version'."