Files
altsp-downloader/altsp-download-repo.sh
T
a.gritsenko 929939341c Add -a/--archive option to package the built repo as a single tar.gz
Convenient for transferring the offline repo as one file instead of a
directory tree; excludes the internal .cache/ (download cache + fakeroot
rpm db), which the target machine doesn't need.
2026-09-22 03:39:52 +03:00

178 lines
6.8 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Скачивает пакеты ALT Linux вместе со всеми зависимостями и собирает
# из них автономный локальный apt-rpm репозиторий (схема "classic"),
# готовый для переноса на машину без интернета.
set -euo pipefail
SCRIPT_NAME=$(basename "$0")
OUTDIR="./altsp-repo"
SKIP_UPDATE=0
ARCHIVE=0
PACKAGES=()
ORIGINAL_ARGS=("$@")
usage() {
cat <<EOF
Использование: $SCRIPT_NAME [-o|--output DIR] [--skip-update] [-a|--archive] PKG1 [PKG2 ...]
Опции:
-o, --output DIR Каталог репозитория (по умолчанию: $OUTDIR)
--skip-update Не выполнять apt-get update перед скачиванием
-a, --archive Дополнительно упаковать готовый репозиторий в
<каталог>.tar.gz (для переноса одним файлом)
-h, --help Показать эту справку
Повторные запуски с другим набором пакетов дополняют существующий
каталог репозитория, не скачивая заново уже имеющиеся пакеты.
Пример:
sudo $SCRIPT_NAME -o /srv/offline-repo -a bash mc htop
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
-o|--output)
OUTDIR="$2"; shift 2 ;;
--skip-update)
SKIP_UPDATE=1; shift ;;
-a|--archive)
ARCHIVE=1; shift ;;
-h|--help)
usage; exit 0 ;;
--)
shift; PACKAGES+=("$@"); break ;;
-*)
echo "Неизвестная опция: $1" >&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")
ARCHIVE_PATH=""
if [[ $ARCHIVE -eq 1 ]]; then
ARCHIVE_PATH="$OUTDIR.tar.gz"
echo "Упаковка репозитория в архив..." >&2
tar -czf "$ARCHIVE_PATH" \
-C "$(dirname "$OUTDIR")" \
--exclude="$(basename "$OUTDIR")/.cache" \
"$(basename "$OUTDIR")"
fi
echo
echo "Готово."
echo "Репозиторий: $OUTDIR"
echo "Пакетов (rpm): $rpm_count"
echo "Суммарный размер: $human_size"
echo "Инструкция: $README"
if [[ -n "$ARCHIVE_PATH" ]]; then
echo "Архив: $ARCHIVE_PATH"
fi