diff --git a/README.md b/README.md index 6616e37..d994741 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,174 @@ # apk-builder -Образ сборщика APK-файлов для Alpine Linux. +Образ предназначен для автоматизации настройки окружения для сборки пакетов в Alpine Linux. Он создаёт пользователя, настраивает права, подключает NFS-сервер (если указан), управляет ключами для подписи пакетов и выполняет сборку пакетов с помощью утилиты abuild. + +## Сборка образа + +Скрипт `build.sh` предназначен для автоматизации сборки и, при необходимости, отправки Docker-образов в указанный registry. Скрипт поддерживает настройку имени образа, версии, базового образа, выбор Dockerfile, а также указание дополнительных репозиториев и параметров registry. + +### Использование: +Скрипт запускается с обязательными и опциональными аргументами. Общий синтаксис: + +```bash +./build.sh -n <имя> -v <версия> -b <версия_базового_образа> [-R <репозитории>] [-o] [-r <адрес_registry>] [-p <порт>] [-h] +``` + +### Опции: +- `-n, --name` (обязательно): Имя собираемого образа. +- `-v, --version` (обязательно): Версия собираемого образа в формате `X.Y.Z`. +- `-b, --base-image-version` (обязательно): Версия базового образа Alpine Linux. +- `-R, --repositories`: Список репозиториев через точку с запятой для использования в Docker образе. +- `-o, --old`: Использовать метод сборки для устаревшей версии Docker. +- `-r, --push-to-registry`: Адрес registry для отправки образов. +- `-p, --port`: Порт registry (по умолчанию `5000`). +- `-h, --help`: Показать справку. + +### Примеры использования: + +1. **Простая сборка образа без отправки в registry**: + ```bash + ./build.sh -n my-app -v 1.0.0 -b 3.21 + ``` + - Скрипт соберет образ `my-app:1.0.0` на основе базового образа Alpine Linux версии `3.21`. + +2. **Сборка с использованием метода сборки для устаревшей версии Docker**: + ```bash + ./build.sh -n my-app -v 1.0.0 -b 3.21 -o + ``` + - Скрипт соберет образ `my-app:1.0.0` на основе базового образа Alpine Linux версии `3.21`. +3. **Сборка и отправка в registry**: + ```bash + ./build.sh -n my-app -v 1.0.0 -b 3.21 -r registry.example.com -p 5000 + ``` + - Скрипт соберет образ `my-app:1.0.0`. + - Создаст теги: `registry.example.com:5000/my-app:latest`, `registry.example.com:5000/my-app:1.0.0`, `registry.example.com:5000/my-app:1.0`, `registry.example.com:5000/my-app:1`. + - Отправит все теги в registry по адресу `registry.example.com:5000`. + +4. **Сборка с дополнительными репозиториями**: + ```bash + ./build.sh -n my-app -v 1.0.0 -b 3.21 -R "https://mirror.yandex.ru/mirrors/alpine/v3.21/main;https://mirror.yandex.ru/mirrors/alpine/v3.21/community" + ``` + - Скрипт соберет образ `my-app:1.0.0` на основе базового образа Alpine Linux версии `3.21` с использованием списка переданных репозиториев. + +--- + +## Использование образа + +Для запуска контейнера используйте команду `docker run` с необходимыми параметрами и монтированием директорий. Пример команды: + +```bash +docker run -it --rm \ + -v /path/to/source:/source \ + -v /path/to/repository:/repository \ + -e BUILDER_USER="myuser" \ + -e REPOSITORY_LIST="https://mirror.yandex.ru/mirrors/alpine/v3.18/main;https://mirror.yandex.ru/mirrors/alpine/v3.18/community" \ + -e NFS_SERVER="192.168.1.100" \ + -e NFS_PORT="2049" \ + -e NFS_PATH="/nfs/repository" \ + -e REPOSITORY_NAME_DIR="myrepo" \ + -e NAME_KEY="mykey" \ + -e PUBLIC_KEY="$(cat /path/to/mykey.rsa.pub)" \ + -e PRIVATE_KEY="$(cat /path/to/mykey.rsa)" \ + my-app:1.0.0 +``` + +### Параметры запуска +Вы можете передать следующие параметры через переменные окружения (`-e`) или аргументы командной строки: + +| Параметр | Описание | По умолчанию | Формат | +|----------|----------|--------------|--------| +| `BUILDER_USER` | Имя пользователя для сборки | `builder` | Строка (буквы, цифры, `_`, `-`) | +| `REPOSITORY_DIR` | Директория для хранения собранных пакетов | `/repository` | Путь в контейнере | +| `KEYS_DIR` | Директория для хранения ключей | `/keys` | Путь в контейнере | +| `SOURCE_DIR` | Директория с исходными файлами (`APKBUILD`) | `/source` | Путь в контейнере | +| `REPOSITORY_LIST` | Список репозиториев, разделённых `;` | Пусто | Например: `http://url1;http://url2` | +| `REPOSITORY_NAME_DIR` | Имя поддиректории для репозитория | Версия Alpine (например, `v3.18`) | Строка (буквы, цифры, `_`, `-`) | +| `NFS_SERVER` | Адрес NFS-сервера | Пусто | IP-адрес или доменное имя | +| `NFS_PORT` | Порт NFS-сервера | `5000` | Число от 1 до 65535 | +| `NFS_PATH` | Путь на NFS-сервере | Пусто | Путь, начинающийся с `/` | +| `NAME_KEY` | Имя ключа для подписи | Имя пользователя | Строка (буквы, цифры, `_`, `-`) | +| `PUBLIC_KEY` | Публичный RSA-ключ | Пусто | Формат `BEGIN PUBLIC KEY` | +| `PRIVATE_KEY` | Приватный RSA-ключ | Пусто | Формат `BEGIN RSA PRIVATE KEY` | + +### Монтирование директорий +- `-v /path/to/source:/source`: Локальная директория с файлами `APKBUILD` и другими исходниками. +- `-v /path/to/repository:/repository`: Локальная директория для хранения собранных пакетов. +- `-v /path/to/keys:/keys`: Локальная директория для хранения ключей (если используются). + +### Что происходит при запуске + +1. **Создание пользователя**: + - Создаётся пользователь с именем `` и добавляется в группу `abuild`. + - Настраиваются права `sudo` для этого пользователя. + +2. **Создание директорий**: + - Создаются директории: `/home//package`, `/keys`, `/source`, `/repository`. + - Все директории принадлежат пользователю ``. + +3. **Настройка NFS (если указан)**: + - Монтируется NFS-сервер (`NFS_SERVER:NFS_PATH`) в `/repository` на указанном порту (`NFS_PORT`). + - Проверяется корректность адреса, порта и пути. + +4. **Настройка репозиториев**: + - Если указан `REPOSITORY_LIST`, он записывается в `/etc/apk/repositories`. + +5. **Настройка ключей для подписи**: + - Если переданы `PUBLIC_KEY` и `PRIVATE_KEY`: + - Ключи сохраняются в `/keys/.rsa` (приватный) и `/keys/.rsa.pub` (публичный). + - Настраивается файл `/home//.abuild/abuild.conf` с указанием приватного ключа. + - Публичный ключ копируется в `/etc/apk/keys`. + - Если ключи не переданы, но существуют в `/keys/.rsa` и `/keys/.rsa.pub`: + - Используются существующие ключи. + - Если ключи отсутствуют: + - Генерируются новые ключи с помощью `abuild-keygen`. + +6. **Сборка пакета**: + - Исходные файлы из `/source` копируются в `/home//package`. + - Выполняется команда `abuild -r` для сборки пакета. + - Собранные пакеты сохраняются в `/repository/`. + +### Примеры использования + +#### 1. Минимальный запуск (без NFS и ключей) +Сборка пакета с использованием стандартных репозиториев и автоматической генерации ключей (без сохранения ключей): +```bash +docker run -it --rm \ + -v /path/to/source:/source \ + -v /path/to/repository:/repository \ + -e BUILDER_USER="builder" \ + -e REPOSITORY_LIST="https://mirror.yandex.ru/mirrors/alpine/v3.18/main;https://mirror.yandex.ru/mirrors/alpine/v3.18/community" \ + my-app:1.0.0 +``` + +#### 2. Запуск с NFS и пользовательскими ключами +Сборка пакета с монтированием NFS и использованием собственных ключей: +```bash +docker run -it --rm \ + -v /path/to/source:/source \ + -e BUILDER_USER="myuser" \ + -e NFS_SERVER="192.168.1.100" \ + -e NFS_PORT="2049" \ + -e NFS_PATH="/nfs/repository" \ + -e REPOSITORY_NAME_DIR="myrepo" \ + -e NAME_KEY="mykey" \ + -e PUBLIC_KEY="$(cat /path/to/mykey.rsa.pub)" \ + -e PRIVATE_KEY="$(cat /path/to/mykey.rsa)" \ + my-app:1.0.0 +``` + +#### 3. Запуск с пользовательскими ключами +Сборка пакета с использованием собственных ключей: +```bash +docker run -it --rm \ + -v /path/to/source:/source \ + -v /path/to/keys:/keys \ + -v /path/to/repository:/repository \ + -e BUILDER_USER="myuser" \ + my-app:1.0.0 +``` + +--- + +- **Сохранение ключей**: Если вы генерируете ключи внутри контейнера, сохраните их из директории `/keys` для повторного использования. +- **Повторное использование контейнера**: Используйте одну и ту же директорию `/keys` и `/repository` для сохранения ключей и пакетов между запусками. +- **Подпись пакетов**: Для подписи пакетов и хранения их в существующем репозитории необходимо использовать именования ключей, соответствующих тем наименованиям, которые ранее имели ключи при подписи существующих пакетов в репозитории. Таким образом по умолчанию, если имя для генерации новых ключей не было указано - оно заимствует имя пользователя. Таким образом `имя пользователя` == `имена ключей`. diff --git a/build.sh b/build.sh index 91dd7b5..8286cc9 100755 --- a/build.sh +++ b/build.sh @@ -1,76 +1,91 @@ #!/bin/sh usage() { - echo "✏️ Использование: $0 -n <имя> -v <версия> -d <версия_docker> -b <версия_базового_образа> [-r <адрес_registry>] [-p <порт>]" + local exit_code="$1" + echo "✏️ Использование: $0 -n <имя> -v <версия> -b <версия_базового_образа> [-o] [-R <репозитории>] [-r <адрес_registry>] [-p <порт>] [-h]" echo "🔧 Опции:" - echo " -n, --name Имя образа (обязательно)" - echo " -v, --version Версия образа в формате X.Y.Z (обязательно)" - echo " -d, --docker-version Версия Docker для сборки (обязательно)" - echo " -b, --base-image-version Версия базового образа (обязательно)" - echo " -r, --push-to-registry Адрес registry для отправки образов (например, registry.example.com)" - echo " -p, --port Порт registry (по умолчанию 5000)" - echo "" - echo "📌 Примеры:" - echo " Только сборка: $0 -n myapp -v 2.5.7 -d 20.10 -b 3.8" - echo " Сборка и отправка: $0 -n myapp -v 2.5.7 -d 20.10 -b 3.8 -r registry.example.com" - echo " Сборка и отправка с другим портом: $0 -n myapp -v 2.5.7 -d 20.10 -b 3.8 -r registry.example.com -p 8080" - exit 1 + echo " -n, --name Имя собираемого образа (обязательно)" + echo " -v, --version Версия собираемого образа в формате X.Y.Z (обязательно)" + echo " -b, --base-image-version Версия базового образа (обязательно)" + echo " -o, --old Использовать Dockerfile.20.10.20 (по умолчанию Dockerfile.27.3.1)" + echo " -R, --repositories Список репозиториев для сборки (через точку с запятой)" + echo " -r, --push-to-registry Адрес registry для отправки образов (например, registry.example.com)" + echo " -p, --port Порт registry (по умолчанию 5000)" + echo " -h, --help Показать эту справку" + exit "$exit_code" } validate_version() { echo "$1" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || { echo "❌ Ошибка: Версия должна быть в формате X.Y.Z (например, 2.5.7)" - exit 1 + usage 1 } } validate_address() { if [ -z "$1" ]; then echo "❌ Ошибка: Адрес registry не может быть пустым" - exit 1 - } + usage 1 + fi } validate_port() { if [ -n "$1" ]; then echo "$1" | grep -Eq '^[0-9]+$' || { echo "❌ Ошибка: Порт должен быть числом" - exit 1 + usage 1 } fi } check_dockerfile() { - if [ ! -f "Dockerfile.$1" ]; then - echo "❌ Ошибка: Файл Dockerfile.$1 не найден в директории docker" - exit 1 + if [ ! -f "$1" ]; then + echo "❌ Ошибка: Файл $1 не найден в директории docker" + usage 1 fi } push_to_registry=false name="" version="" -docker_version="" base_image_version="" registry_address="" port="5000" +use_old=false +dockerfile="Dockerfile.27.3.1" +repositories="" -while getopts ":n:v:d:b:p:r:" opt; do - case $opt in - n) name="$OPTARG" ;; - v) version="$OPTARG" ;; - d) docker_version="$OPTARG" ;; - b) base_image_version="$OPTARG" ;; - p) port="$OPTARG" ;; - r) push_to_registry=true; registry_address="$OPTARG" ;; - \?) echo "❌ Неверный аргумент -$OPTARG" >&2; usage ;; - :) echo "❌ Опция -$OPTARG требует аргумент" >&2; usage ;; +if ! command -v getopt >/dev/null 2>&1; then + echo "❌ Ошибка: утилита getopt не найдена" + usage 1 +fi + +opts=$(getopt -o n:v:b:oR:r:p:h -l name:,version:,base-image-version:,old,repositories:,push-to-registry:,port:,help -n "$0" -- "$@") +if [ $? -ne 0 ]; then + echo "❌ Ошибка парсинга аргументов" + usage 1 +fi + +eval set -- "$opts" + +while true; do + case "$1" in + -n|--name) name="$2"; shift 2 ;; + -v|--version) version="$2"; shift 2 ;; + -b|--base-image-version) base_image_version="$2"; shift 2 ;; + -r|--push-to-registry) push_to_registry=true; registry_address="$2"; shift 2 ;; + -p|--port) port="$2"; shift 2 ;; + -o|--old) use_old=true; shift ;; + -R|--repositories) repositories="$2"; shift 2 ;; + -h|--help) usage 0 ;; + --) shift; break ;; + *) echo "❌ Неверный аргумент $1" >&2; usage 1 ;; esac done -if [ -z "$name" ] || [ -z "$version" ] || [ -z "$docker_version" ] || [ -z "$base_image_version" ]; then - echo "❌ Ошибка: Не указаны обязательные аргументы (-n, -v, -d и -b)" - usage +if [ -z "$name" ] || [ -z "$version" ] || [ -z "$base_image_version" ]; then + echo "❌ Ошибка: Не указаны обязательные аргументы (-n, -v и -b)" + usage 1 fi validate_version "$version" @@ -78,7 +93,7 @@ validate_version "$version" if [ "$push_to_registry" = true ]; then if [ -z "$registry_address" ]; then echo "❌ Ошибка: Для отправки в registry необходимо указать адрес с -r" - usage + usage 1 fi validate_address "$registry_address" @@ -91,14 +106,29 @@ cd docker || { exit 1 } -echo "🔍 Проверка наличия Dockerfile..." -check_dockerfile "$docker_version" +if [ "$use_old" = true ]; then + dockerfile="Dockerfile.20.10.20" +fi -echo "🐳 Сборка образа $name:$version с использованием Dockerfile.$docker_version и базового образа версии $base_image_version..." -docker build . -f "Dockerfile.$docker_version" -t "${name}:${version}" --build-arg BASE_IMAGE_VERSION="$base_image_version" || { - echo "❌ Ошибка при сборке образа" - exit 1 -} +echo "🔍 Проверка наличия Dockerfile..." +check_dockerfile "$dockerfile" + +echo "🐳 Сборка образа $name:$version с использованием $dockerfile и базового образа версии $base_image_version..." +if [ -n "$repositories" ]; then + echo "📦 Используемые репозитории:" + echo "$repositories" | tr ';' '\n' | while read -r repo; do + echo -e " 🌐 $repo" + done + docker build . -f "$dockerfile" -t "${name}:${version}" --build-arg BASE_IMAGE_VERSION="$base_image_version" --build-arg REPOSITORIES="$repositories" || { + echo "❌ Ошибка при сборке образа" + exit 1 + } +else + docker build . -f "$dockerfile" -t "${name}:${version}" --build-arg BASE_IMAGE_VERSION="$base_image_version" || { + echo "❌ Ошибка при сборке образа" + exit 1 + } +fi if [ "$push_to_registry" = true ]; then registry="$registry_address:$port" diff --git a/docker/Dockerfile.20.10.20 b/docker/Dockerfile.20.10.20 index e6ce26e..9f75040 100644 --- a/docker/Dockerfile.20.10.20 +++ b/docker/Dockerfile.20.10.20 @@ -1,45 +1,31 @@ ARG BASE_IMAGE_VERSION=latest + FROM alpine:${BASE_IMAGE_VERSION} -# Метаданные для CI/CD LABEL maintainer="Alexander Zhirov " LABEL version="0.1.0" -LABEL description="Адаптированный для Docker <=20.10.20 образ сборщика APK-файлов для Alpine Linux 3.16.9" +LABEL description="Адаптированный для Docker <=20.10.20 образ сборщика APK-файлов для Alpine Linux" -# Задаем стандартные значения через переменные окружения -ENV USER=builder \ - PACKAGE_DIR=/package \ +ENV BUILDER_USER=builder \ + REPOSITORY_DIR=/repository \ + KEYS_DIR=/keys \ + SOURCE_DIR=/source \ + REPOSITORY_LIST= \ + REPOSITORY_NAME_DIR= \ NFS_SERVER= \ - NFS_PATH= \ - PRIVATE_KEY= \ - PUBLIC_KEY= \ - REPODEST_DIR= \ - NAME_KEY=builder + NFS_PORT=5000 \ + NFS_PATH= -# Копируем скрипт запуска в контейнер COPY build-apk /usr/bin/build-apk -# Устанавливаем необходимые пакеты для сборки APK и NFS -RUN echo "http://mirror.yandex.ru/mirrors/alpine/v3.16/main" > /etc/apk/repositories && \ - echo "http://mirror.yandex.ru/mirrors/alpine/v3.16/community" >> /etc/apk/repositories && \ +ARG REPOSITORIES + +RUN if [ ! -z "${REPOSITORIES}" ]; then \ + echo "${REPOSITORIES}" | tr ';' '\n' > /etc/apk/repositories; \ + fi && \ apk update && \ apk upgrade --no-cache && \ apk add --no-cache build-base alpine-sdk abuild sudo git nfs-utils && \ - adduser -D builder && \ - addgroup builder abuild && \ - echo "builder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/builder && \ - chmod 0755 /usr/bin/build-apk && \ - mkdir /home/builder/package && \ - chown builder:builder /home/builder/package + chmod 0755 /usr/bin/build-apk -# Переключаемся на пользователя builder -USER builder - -# Создаем рабочую директорию -WORKDIR /home/builder/package - -# Указываем точку входа для CI/CD ENTRYPOINT ["/usr/bin/build-apk"] - -# Указываем команду по умолчанию с переменными окружения -CMD ["-p", "/package"] diff --git a/docker/Dockerfile.27.3.1 b/docker/Dockerfile.27.3.1 index b0ea592..eabd732 100644 --- a/docker/Dockerfile.27.3.1 +++ b/docker/Dockerfile.27.3.1 @@ -1,44 +1,32 @@ ARG BASE_IMAGE_VERSION=latest + FROM alpine:${BASE_IMAGE_VERSION} -# Метаданные для CI/CD LABEL maintainer="Alexander Zhirov " LABEL version="0.1.0" -LABEL description="Адаптированный для Docker >=27.3.1 образ сборщика APK-файлов для Alpine Linux 3.16.9" +LABEL description="Адаптированный для Docker >=27.3.1 образ сборщика APK-файлов для Alpine Linux" -# Задаем стандартные значения через переменные окружения -ENV USER=builder \ - PACKAGE_DIR=/package \ +ENV BUILDER_USER=builder \ + REPOSITORY_DIR=/repository \ + KEYS_DIR=/keys \ + SOURCE_DIR=/source \ + REPOSITORY_LIST= \ + REPOSITORY_NAME_DIR= \ NFS_SERVER= \ - NFS_PATH= \ - PRIVATE_KEY= \ - PUBLIC_KEY= \ - REPODEST_DIR= \ - NAME_KEY=builder + NFS_PORT=5000 \ + NFS_PATH= -# Копируем скрипт запуска в контейнер COPY --chmod=0755 build-apk /usr/bin/build-apk -# Устанавливаем необходимые пакеты для сборки APK и NFS +ARG REPOSITORIES + RUN < /etc/apk/repositories - echo "http://mirror.yandex.ru/mirrors/alpine/v3.16/community" >> /etc/apk/repositories + if [ ! -z "$REPOSITORIES" ]; then + echo "$REPOSITORIES" | tr ';' '\n' > /etc/apk/repositories + fi apk update apk upgrade --no-cache - apk add --no-cache alpine-sdk abuild sudo git nfs-utils - adduser -D builder - addgroup builder abuild - echo "builder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/builder + apk add --no-cache build-base alpine-sdk abuild sudo git nfs-utils EOF -# Переключаемся на пользователя builder -USER builder - -# Создаем рабочую директорию -WORKDIR /home/builder/package - -# Указываем точку входа для CI/CD ENTRYPOINT ["/usr/bin/build-apk"] - -# Указываем команду по умолчанию с переменными окружения -CMD ["-p", "/package"] diff --git a/docker/build-apk b/docker/build-apk index 987d1de..33ca993 100644 --- a/docker/build-apk +++ b/docker/build-apk @@ -1,243 +1,148 @@ #!/bin/sh -# Функция для вывода справки -usage() { - echo "Использование: $0 -p <путь_к_APKBUILD> -k <наименование_ключа> [-s -n ] [-r <приватный_ключ> -u <публичный_ключ>] [-d <путь_к_репозиторию>]" - echo "Параметры:" - echo " -p <путь_к_APKBUILD> Путь к директории с APKBUILD (обязательный)" - echo " -k <наименование_ключа> Наименование файла ключа (обязательный)" - echo " -s IP или имя NFS-сервера (опционально)" - echo " -n Путь к NFS-ресурсу (опционально, требуется с -s)" - echo " -r <приватный_ключ> Содержимое файла приватного ключа (опционально)" - echo " -u <публичный_ключ> Содержимое файла публичного ключа (опционально)" - echo " -d <путь_к_репозиторию> Путь к директории репозитория в ~/packages (опционально)" - echo "Пример: $0 -p /package -k mykey -s 192.168.1.100 -n /nfs/share -r \"<содержимое_ключа.rsa>\" -u \"<содержимое_ключа.rsa.pub>\" -d /repo" - echo "Флаги -s и -n включают монтирование NFS, должны использоваться вместе." - echo "Флаги -r и -u содержат текст приватного и публичного ключей для abuild." - echo "Флаг -d задаёт директорию для сохранения пакетов." - exit 1 +: "${BUILDER_USER:=builder}" +: "${REPOSITORY_DIR:=/repository}" +: "${KEYS_DIR:=/keys}" +: "${SOURCE_DIR:=/source}" +: "${REPOSITORY_LIST:=}" +: "${REPOSITORY_NAME_DIR:=}" +: "${NFS_SERVER:=}" +: "${NFS_PORT:=5000}" +: "${NFS_PATH:=}" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' +BOLD='\033[1m' +RESET='\033[0m' + +log() { + local type=$1 + local message=$2 + case $type in + "success") printf "${GREEN}${BOLD}✅ %s${RESET}\n" "$message" ;; + "error") printf "${RED}${BOLD}❌ Ошибка: %s${RESET}\n" "$message" ;; + "info") printf "${YELLOW}${BOLD}ℹ️ %s${RESET}\n" "$message" ;; + esac } -# Устанавливаем стандартные значения из переменных окружения или аргументов сборки -: "${PACKAGE_DIR:=/package}" -: "${NFS_SERVER:=}" -: "${NFS_PATH:=}" -: "${PRIVATE_KEY:=}" -: "${PUBLIC_KEY:=}" -: "${REPODEST_DIR:=}" -: "${NAME_KEY:=}" -PACKAGES_DIR="/home/builder/packages" -NFS_ENABLED=0 - -# Обработка флагов -while [ $# -gt 0 ]; do - case "$1" in - -p) PACKAGE_DIR="$2"; shift 2 ;; - -s) NFS_SERVER="$2"; shift 2 ;; - -n) NFS_PATH="$2"; shift 2 ;; - -r) PRIVATE_KEY="$2"; shift 2 ;; - -u) PUBLIC_KEY="$2"; shift 2 ;; - -d) REPODEST_DIR="$2"; shift 2 ;; - -k) NAME_KEY="$2"; shift 2 ;; - *) echo "🚫 Ошибка: Неизвестный флаг $1"; usage ;; +while getopts "u:r:k:p:n:s:t:m:d:" opt; do + case $opt in + u) BUILDER_USER="$OPTARG" ;; + r) REPOSITORY_LIST="$OPTARG" ;; + k) PUBLIC_KEY="$OPTARG" ;; + p) PRIVATE_KEY="$OPTARG" ;; + n) NAME_KEY="$OPTARG" ;; + s) NFS_SERVER="$OPTARG" ;; + t) NFS_PORT="$OPTARG" ;; + m) NFS_PATH="$OPTARG" ;; + d) REPOSITORY_NAME_DIR="$OPTARG" ;; + \?) log error "Неверный аргумент! 😕 Использование: $0 [-u имя_пользователя] [-r список_репозиториев] [-k публичный_ключ] [-p приватный_ключ] [-n имя_ключа] [-s адрес_NFS_сервера] [-t порт_NFS_сервера] [-m путь_NFS] [-d имя_репозитория]" + exit 1 ;; esac done -# Проверка наличия обязательного аргумента -p -if [ -z "$PACKAGE_DIR" ]; then - echo "🚫 Ошибка: Не указан обязательный параметр -p" - usage +if [ -n "$REPOSITORY_NAME_DIR" ]; then + REPOSITORY_NAME_DIR=$(echo "$REPOSITORY_NAME_DIR" | sed 's|^./||;s|^/||;s|/$||') + if ! echo "$REPOSITORY_NAME_DIR" | grep -qE '^[a-zA-Z0-9_-]+$'; then + log error "Недопустимое имя директории репозитория: $REPOSITORY_NAME_DIR 😕 Используйте только буквы, цифры, _ и -" + exit 1 + fi fi -# Проверка наличия обязательного аргумента -k -if [ -z "$NAME_KEY" ]; then - echo "🚫 Ошибка: Не указан обязательный параметр -k" - usage +adduser -D "$BUILDER_USER" || { log error "Не удалось создать пользователя $BUILDER_USER 😞"; exit 1; } +addgroup "$BUILDER_USER" abuild || { log error "Не удалось добавить пользователя в группу abuild 😞"; exit 1; } +log success "Пользователь $BUILDER_USER успешно создан! 🥳" + +echo "$BUILDER_USER ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/$BUILDER_USER" +chmod 0640 "/etc/sudoers.d/$BUILDER_USER" || { log error "Не удалось настроить sudo для $BUILDER_USER 😞"; exit 1; } +log success "Sudo успешно настроен для $BUILDER_USER! 🔒" + +DIRS="/home/$BUILDER_USER/package $KEYS_DIR $SOURCE_DIR $REPOSITORY_DIR" +for dir in $DIRS; do + if [ ! -d "$dir" ]; then + mkdir -p "$dir" || { log error "Не удалось создать директорию $dir 😞"; exit 1; } + chown "$BUILDER_USER:$BUILDER_USER" "$dir" + log success "Директория $dir успешно создана! 📁" + fi +done + +if [ -n "$NFS_SERVER" ]; then + if [ -z "$NFS_PATH" ]; then + log error "Путь NFS (-m) обязателен при указании NFS-сервера (-s) 😕" + exit 1 + fi + if ! echo "$NFS_PORT" | grep -qE '^[0-9]+$' || [ "$NFS_PORT" -lt 1 ] || [ "$NFS_PORT" -gt 65535 ]; then + log error "Недопустимый порт NFS-сервера: $NFS_PORT 😕 Используйте число от 1 до 65535" + exit 1 + fi + if ! echo "$NFS_PATH" | grep -qE '^/([a-zA-Z0-9_-]+/)*[a-zA-Z0-9_-]*$'; then + log error "Недопустимый путь NFS: $NFS_PATH 😕 Путь должен начинаться с / и содержать только буквы, цифры, _, - и /" + exit 1 + fi + log info "Настройка подключения к NFS-серверу: $NFS_SERVER:$NFS_PATH на порту $NFS_PORT 🌐" + mount -t nfs -o port="$NFS_PORT" "$NFS_SERVER:$NFS_PATH" "$REPOSITORY_DIR" || { log error "Не удалось подключиться к NFS-серверу $NFS_SERVER:$NFS_PATH 😞"; exit 1; } + log success "NFS-сервер $NFS_SERVER:$NFS_PATH успешно смонтирован в $REPOSITORY_DIR на порту $NFS_PORT! 📡" fi -# Проверка валидности имени ключа +if [ -n "$REPOSITORY_LIST" ]; then + echo "$REPOSITORY_LIST" | tr ';' '\n' > /etc/apk/repositories; + log success "Репозитории успешно обновлены! 📦" +fi + +NAME_KEY="${NAME_KEY:=$BUILDER_USER}" + if ! echo "$NAME_KEY" | grep -qE '^[a-zA-Z0-9_-]+$'; then - echo "🚫 Ошибка: Имя ключа должно содержать только буквы, цифры, символы '_' или '-'" + log error "Недопустимое имя ключа: $NAME_KEY 😕 Используйте только буквы, цифры, _ и -" exit 1 fi -# Проверка, что если указан один из -s или -n, то указан и другой -if [ -n "$NFS_SERVER" ] && [ -z "$NFS_PATH" ] || [ -z "$NFS_SERVER" ] && [ -n "$NFS_PATH" ]; then - echo "🚫 Ошибка: Флаги -s и -n должны быть указаны вместе" - usage -fi - -# Устанавливаем флаг NFS_ENABLED, если указаны оба флага -s и -n -if [ -n "$NFS_SERVER" ] && [ -n "$NFS_PATH" ]; then - NFS_ENABLED=1 -fi - -# Проверка существования директории с APKBUILD -if [ ! -d "$PACKAGE_DIR" ]; then - echo "🚫 Ошибка: Директория $PACKAGE_DIR не существует" - exit 1 -fi - -# Проверка наличия APKBUILD -if [ ! -f "$PACKAGE_DIR/APKBUILD" ]; then - echo "🚫 Ошибка: Файл APKBUILD не найден в $PACKAGE_DIR" - exit 1 -fi - -# Проверка прав доступа к директории -if [ ! -r "$PACKAGE_DIR" ] || [ ! -x "$PACKAGE_DIR" ]; then - echo "🚫 Ошибка: Недостаточно прав для доступа к директории $PACKAGE_DIR" - exit 1 -fi - -# Проверка и настройка ключей abuild if [ -n "$PRIVATE_KEY" ] || [ -n "$PUBLIC_KEY" ]; then - echo "🔑 Настраиваю переданные ключи abuild..." - # Проверяем, что переданы все компоненты - if [ -z "$PRIVATE_KEY" ] || [ -z "$PUBLIC_KEY" ]; then - echo "🚫 Ошибка: Для использования ключей нужны оба флага: -r, -u" + if [ -z "$PRIVATE_KEY" ] || [ -z "$PUBLIC_KEY" ] || [ -z "$NAME_KEY" ]; then + log error "Необходимо указать оба ключа (публичный и приватный) и имя ключа 😞" exit 1 fi - # Проверка формата ключей + if ! echo "$PRIVATE_KEY" | grep -q "BEGIN RSA PRIVATE KEY"; then - echo "🚫 Ошибка: Приватный ключ имеет неверный формат" + log error "Неверный формат приватного ключа 😕" exit 1 fi - if ! echo "$PUBLIC_KEY" | grep -q "ssh-rsa"; then - echo "🚫 Ошибка: Публичный ключ имеет неверный формат" + if ! echo "$PUBLIC_KEY" | grep -q "BEGIN PUBLIC KEY"; then + log error "Неверный формат публичного ключа 😕" exit 1 fi - # Создаем директорию .abuild - mkdir -p /home/builder/.abuild - if [ $? -ne 0 ]; then - echo "🚫 Ошибка: Не удалось создать директорию /home/builder/.abuild" - exit 1 - fi - # Записываем abuild.conf - echo "PACKAGER_PRIVKEY=\"/home/builder/.abuild/$NAME_KEY.rsa\"" > /home/builder/.abuild/abuild.conf - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при записи abuild.conf" - exit 1 - fi - # Записываем приватный ключ - echo "$PRIVATE_KEY" > /home/builder/.abuild/$NAME_KEY.rsa - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при записи приватного ключа" - exit 1 - fi - # Записываем публичный ключ - echo "$PUBLIC_KEY" > /home/builder/.abuild/$NAME_KEY.rsa.pub - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при записи публичного ключа" - exit 1 - fi - # Устанавливаем права доступа - chmod 600 /home/builder/.abuild/$NAME_KEY.rsa - chmod 644 /home/builder/.abuild/$NAME_KEY.rsa.pub /home/builder/.abuild/abuild.conf - # Копирование ключа для apk менеджера - sudo cp -v /home/builder/.abuild/$NAME_KEY.rsa.pub /etc/apk/keys/ - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при копировании публичного ключа в /etc/apk/keys/" - exit 1 - fi - echo "✅ Ключи abuild успешно настроены" + + mkdir -p "/home/$BUILDER_USER/.abuild" || { log error "Не удалось создать .abuild директорию 😞"; exit 1; } + echo "PACKAGER_PRIVKEY=\"$KEYS_DIR/$NAME_KEY.rsa\"" > "/home/$BUILDER_USER/.abuild/abuild.conf" + + echo "$PRIVATE_KEY" > "$KEYS_DIR/$NAME_KEY.rsa" + echo "$PUBLIC_KEY" > "$KEYS_DIR/$NAME_KEY.rsa.pub" + + chmod 600 "$KEYS_DIR/$NAME_KEY.rsa" || { log error "Не удалось установить права для приватного ключа 😞"; exit 1; } + chmod 644 "$KEYS_DIR/$NAME_KEY.rsa.pub" "/home/$BUILDER_USER/.abuild/abuild.conf" || { log error "Не удалось установить права для файлов 😞"; exit 1; } + + chown "$BUILDER_USER:$BUILDER_USER" "$KEYS_DIR/$NAME_KEY.rsa" "$KEYS_DIR/$NAME_KEY.rsa.pub" "/home/$BUILDER_USER/.abuild/abuild.conf" + + cp "$KEYS_DIR/$NAME_KEY.rsa.pub" /etc/apk/keys/ || { log error "Не удалось скопировать публичный ключ 😞"; exit 1; } + log success "Ключи успешно настроены! 🔑" else - # Если ключи не переданы, проверяем существующие или генерируем новые - if [ ! -f "/home/builder/.abuild/abuild.conf" ]; then - echo "🔑 Генерирую новые ключи для abuild..." - echo "/home/builder/.abuild/$NAME_KEY.rsa" | abuild-keygen -a -i - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при генерации ключей abuild" - exit 1 - fi - echo "✅ Ключи abuild сгенерированы" + if [ -f "$KEYS_DIR/$NAME_KEY.rsa" ] && [ -f "$KEYS_DIR/$NAME_KEY.rsa.pub" ]; then + mkdir -p "/home/$BUILDER_USER/.abuild" + chown "$BUILDER_USER:$BUILDER_USER" "/home/$BUILDER_USER/.abuild" + echo "PACKAGER_PRIVKEY=\"$KEYS_DIR/$NAME_KEY.rsa\"" > "/home/$BUILDER_USER/.abuild/abuild.conf" + cp "$KEYS_DIR/$NAME_KEY.rsa.pub" /etc/apk/keys/ + log success "Использованы существующие ключи! 🔄" else - if [ ! -f "/home/builder/.abuild/$NAME_KEY.rsa" ] || [ ! -f "/home/builder/.abuild/$NAME_KEY.rsa.pub" ]; then - echo "🚫 Ошибка: Указанное имя ключа $NAME_KEY не соответствует существующим ключам" - exit 1 - fi - sudo cp /home/builder/.abuild/$NAME_KEY.rsa.pub /etc/apk/keys/ - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при копировании существующего публичного ключа" - exit 1 - fi - echo "✅ Использую существующие ключи abuild" + su - "$BUILDER_USER" -c "echo \"$KEYS_DIR/$NAME_KEY.rsa\" | abuild-keygen -a -i" || { log error "Не удалось сгенерировать ключи 😞"; exit 1; } + log success "Новые ключи успешно сгенерированы! 🆕" fi fi -# Монтируем NFS-ресурс, если включен -if [ "$NFS_ENABLED" -eq 1 ]; then - echo "🔌 Монтирую NFS-ресурс $NFS_SERVER:$NFS_PATH в $PACKAGES_DIR..." - sudo mkdir -p "$PACKAGES_DIR" - if [ $? -ne 0 ]; then - echo "🚫 Ошибка: Не удалось создать директорию $PACKAGES_DIR" - exit 1 - fi - sudo mount -t nfs "$NFS_SERVER:$NFS_PATH" "$PACKAGES_DIR" - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при монтировании NFS-ресурса" - exit 1 - fi - echo "✅ NFS-ресурс успешно смонтирован" -else - echo "ℹ️ NFS не указан, пакеты будут сохранены локально в $PACKAGES_DIR" - mkdir -p "$PACKAGES_DIR" - if [ $? -ne 0 ]; then - echo "🚫 Ошибка: Не удалось создать директорию $PACKAGES_DIR" - exit 1 - fi -fi +su - "$BUILDER_USER" -c " + cp -r $SOURCE_DIR/* /home/$BUILDER_USER/package/ || exit 1 + cd /home/$BUILDER_USER/package || exit 1 + REPODEST=\"$REPOSITORY_DIR/\${REPOSITORY_NAME_DIR:-v\$(cat /etc/alpine-release | cut -d. -f1,2)}\" abuild -r +" || { log error "Не удалось собрать пакет 😞"; exit 1; } -# Копируем содержимое директории в рабочую директорию -echo "📂 Копирую файлы проекта в рабочую директорию..." -mkdir -p /home/builder/package -cp -r "$PACKAGE_DIR"/* /home/builder/package/ -if [ $? -ne 0 ]; then - echo "🚫 Ошибка при копировании файлов проекта" - if [ "$NFS_ENABLED" -eq 1 ]; then - sudo umount "$PACKAGES_DIR" || echo "🚫 Ошибка при размонтировании NFS" - fi - exit 1 -fi -echo "✅ Файлы успешно скопированы" - -# Переходим в директорию пакета -cd /home/builder/package || { - echo "🚫 Ошибка: Не удалось перейти в директорию /home/builder/package" - if [ "$NFS_ENABLED" -eq 1 ]; then - sudo umount "$PACKAGES_DIR" || echo "🚫 Ошибка при размонтировании NFS" - fi - exit 1 -} -echo "✅ Перешел в директорию проекта" - -# Проверяем зависимости и собираем пакет -echo "🔨 Проверяю зависимости и собираю APK-пакет..." - -# Устанавливаем REPODEST_DIR -if [ -n "$REPODEST_DIR" ]; then - REPODEST_DIR=$(echo "${REPODEST_DIR}" | sed 's|^./||;s|^/||') -fi - -REPODEST="$PACKAGES_DIR/${REPODEST_DIR:=v$(cat /etc/alpine-release | awk -F. '{print $1"."$2}')}" abuild -r -if [ $? -ne 0 ]; then - echo "🚫 Ошибка при сборке APK-пакета" - if [ "$NFS_ENABLED" -eq 1 ]; then - sudo umount "$PACKAGES_DIR" || echo "🚫 Ошибка при размонтировании NFS" - fi - exit 1 -fi -echo "✅ Сборка APK-пакета успешно завершена" - -# Размонтируем NFS, если использовался -if [ "$NFS_ENABLED" -eq 1 ]; then - sudo umount "$PACKAGES_DIR" - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при размонтировании NFS-ресурса" - exit 1 - fi - echo "✅ NFS-ресурс размонтирован" - echo "🎉 Сборка завершена, пакеты сохранены в $NFS_SERVER:$NFS_PATH" -else - echo "🎉 Сборка завершена, пакеты сохранены локально в $PACKAGES_DIR" -fi +log success "Сборка пакета успешно завершена! 🎉" diff --git a/edit b/edit deleted file mode 100644 index e69de29..0000000