#!/bin/bash # Скачивает пакеты ALT Linux вместе со всеми зависимостями и собирает # из них автономный локальный apt-rpm репозиторий (схема "classic"), # готовый для переноса на машину без интернета. set -euo pipefail SCRIPT_NAME=$(basename "$0") OUTDIR="./altsp-repo" SKIP_UPDATE=0 PACKAGES=() ORIGINAL_ARGS=("$@") usage() { cat <&2; usage >&2; exit 1 ;; *) PACKAGES+=("$1"); shift ;; esac done if [[ ${#PACKAGES[@]} -eq 0 ]]; then echo "Ошибка: не указано ни одного пакета." >&2 usage >&2 exit 1 fi if [[ $EUID -ne 0 ]]; then echo "Требуются права root, перезапуск через sudo..." >&2 exec sudo -- "$0" "${ORIGINAL_ARGS[@]}" fi if ! command -v genbasedir >/dev/null 2>&1; then echo "genbasedir не найден, устанавливаю пакет apt-repo-tools..." >&2 apt-get install -y apt-repo-tools fi if [[ $SKIP_UPDATE -eq 0 ]]; then echo "Обновление индексов apt-get..." >&2 apt-get update fi mkdir -p "$OUTDIR" OUTDIR=$(cd "$OUTDIR" && pwd) CACHE="$OUTDIR/.cache" FAKEROOT="$CACHE/fakeroot" ARCHIVES="$CACHE/archives" mkdir -p "$FAKEROOT" "$ARCHIVES/partial" # Пустая rpm-база => apt-get считает, что на целевой системе не установлено # вообще ничего, и скачивает ПОЛНОЕ дерево зависимостей, а не только то, # чего не хватает на этой (текущей) машине. if [[ ! -d "$FAKEROOT/var/lib/rpm" ]]; then rpm --root="$FAKEROOT" --initdb fi echo "Скачивание пакетов и зависимостей: ${PACKAGES[*]}" >&2 apt-get \ -o RPM::RootDir="$FAKEROOT" \ -o Dir::Cache::Archives="$ARCHIVES" \ -y -d install "${PACKAGES[@]}" echo "Раскладка пакетов по архитектурам..." >&2 shopt -s nullglob for rpmfile in "$ARCHIVES"/*.rpm; do arch=$(rpm -qp --qf '%{ARCH}' "$rpmfile" 2>/dev/null) || continue [[ -z "$arch" ]] && continue destdir="$OUTDIR/$arch/RPMS.classic" mkdir -p "$destdir" dest="$destdir/$(basename "$rpmfile")" [[ -e "$dest" ]] || ln -f "$rpmfile" "$dest" 2>/dev/null || cp -f "$rpmfile" "$dest" done shopt -u nullglob for archdir in "$OUTDIR"/*/RPMS.classic; do [[ -d "$archdir" ]] || continue arch=$(basename "$(dirname "$archdir")") echo "Генерация метаданных репозитория для $arch..." >&2 # --bloat: без этого флага genbasedir урезает данные о версии для # некоторых автоматических soname-зависимостей (Provides с "set:..." # хешем символьных версий), из-за чего apt-get на офлайн-машине не может # разрешить некоторые реальные зависимости (например libssh2 -> mc), # хотя с апстрим-репозиторием те же самые .rpm разрешаются нормально. genbasedir --progress --bloat --create --topdir="$OUTDIR" "$arch" classic done MANIFEST="$OUTDIR/manifest.txt" { echo "# $(date -u +%Y-%m-%dT%H:%M:%SZ)" printf '%s\n' "${PACKAGES[@]}" } >> "$MANIFEST" README="$OUTDIR/README.txt" { echo "Автономный apt-rpm репозиторий ALT Linux" echo "Собран: $(date -u +%Y-%m-%dT%H:%M:%SZ)" echo echo "Как подключить на машине без интернета:" echo echo "1. Скопируйте этот каталог целиком на целевую машину, например в /srv/offline-repo" echo " (rsync/scp/флешка — как удобно)." echo echo "2. Добавьте в /etc/apt/sources.list (или новый файл в /etc/apt/sources.list.d/) строки:" echo for archdir in "$OUTDIR"/*/RPMS.classic; do [[ -d "$archdir" ]] || continue arch=$(basename "$(dirname "$archdir")") echo " rpm file:/srv/offline-repo $arch classic" done echo echo " (замените /srv/offline-repo на реальный путь на целевой машине;" echo " тег подписи вроде [cert8] указывать не нужно, репозиторий не подписан)" echo echo "3. Выполните на целевой машине:" echo " apt-get update" echo " apt-get install <имена пакетов>" } > "$README" rpm_count=$(find "$OUTDIR" -path "$CACHE" -prune -o -name '*.rpm' -print | wc -l) total_bytes=0 for archdir in "$OUTDIR"/*/RPMS.classic; do [[ -d "$archdir" ]] || continue sz=$(du -sb "$archdir" | cut -f1) total_bytes=$((total_bytes + sz)) done human_size=$(numfmt --to=iec "$total_bytes" 2>/dev/null || echo "${total_bytes} bytes") echo echo "Готово." echo "Репозиторий: $OUTDIR" echo "Пакетов (rpm): $rpm_count" echo "Суммарный размер: $human_size" echo "Инструкция: $README"