diff --git a/README.md b/README.md index 6616e37..256bb04 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,187 @@ # 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. +- `-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 \ + --privileged \ + -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 \ + --privileged \ + -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/apk-build b/apk-build deleted file mode 100644 index 673a17e..0000000 --- a/apk-build +++ /dev/null @@ -1,193 +0,0 @@ -#!/bin/sh - -# Функция для вывода справки -usage() { - echo "Использование: $0 -p <путь_к_APKBUILD> [-s -n ] [-r <приватный_ключ> -u <публичный_ключ>] [-d <путь_к_репозиторию>]" - echo "Параметры:" - echo " -p <путь_к_APKBUILD> Путь к директории с APKBUILD (обязательный)" - echo " -s IP или имя NFS-сервера (опционально)" - echo " -n Путь к NFS-ресурсу (опционально, требуется с -s)" - echo " -r <приватный_ключ> Содержимое файла приватного ключа (опционально)" - echo " -u <публичный_ключ> Содержимое файла публичного ключа (опционально)" - echo " -d <путь_к_репозиторию> Путь к директории репозитория в ~/packages (опционально)" - echo "Пример: $0 -p /package -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 -} - -# Устанавливаем стандартные значения из переменных окружения или аргументов сборки -: "${PACKAGE_DIR:=/package}" -: "${NFS_SERVER:=}" -: "${NFS_PATH:=}" -: "${PRIVATE_KEY:=}" -: "${PUBLIC_KEY:=}" -: "${REPODEST_DIR:=}" -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 ;; - *) echo "🚫 Ошибка: Неизвестный флаг $1"; usage ;; - esac -done - -# Проверка наличия обязательного аргумента -p -if [ -z "$PACKAGE_DIR" ]; then - echo "🚫 Ошибка: Не указан обязательный параметр -p" - usage -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 - -# Проверка и настройка ключей abuild -if [ -n "$PRIVATE_KEY" ] || [ -n "$PUBLIC_KEY" ]; then - echo "🔑 Настраиваю переданные ключи abuild..." - # Проверяем, что переданы все три компонента - if [ -z "$PRIVATE_KEY" ] || [ -z "$PUBLIC_KEY" ]; then - echo "🚫 Ошибка: Для использования ключей нужны все флаги: -r, -u" - exit 1 - fi - # Создаем директорию .abuild - mkdir -p /home/builder/.abuild - # Записываем abuild.conf - echo 'PACKAGER_PRIVKEY="/home/builder/.abuild/packager.rsa"' > /home/builder/.abuild/abuild.conf - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при записи abuild.conf" - exit 1 - fi - # Записываем приватный ключ - echo "$PRIVATE_KEY" > /home/builder/.abuild/packager.rsa - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при записи приватного ключа" - exit 1 - fi - # Записываем публичный ключ - echo "$PUBLIC_KEY" > /home/builder/.abuild/packager.rsa.pub - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при записи публичного ключа" - exit 1 - fi - # Устанавливаем права доступа - chmod 600 /home/builder/.abuild/packager.rsa - chmod 644 /home/builder/.abuild/packager.rsa.pub /home/builder/.abuild/abuild.conf - echo "✅ Ключи abuild успешно настроены" -else - # Если ключи не переданы, проверяем существующие или генерируем новые - if [ ! -f "/home/builder/.abuild/abuild.conf" ]; then - echo "🔑 Генерирую новые ключи для abuild..." - abuild-keygen -a -i -n - if [ $? -ne 0 ]; then - echo "🚫 Ошибка при генерации ключей abuild" - exit 1 - fi - echo "✅ Ключи abuild сгенерированы" - else - echo "✅ Использую существующие ключи abuild" - fi -fi - -cat /home/builder/.abuild/*.rsa.pub -cat /home/builder/.abuild/*.rsa -cat /home/builder/.abuild/abuild.conf - -# Копирование ключа для apk менеджера -sudo cp -v /home/builder/.abuild/*.rsa.pub /etc/apk/keys/ - -# Монтируем NFS-ресурс, если включен -if [ "$NFS_ENABLED" -eq 1 ]; then - echo "🔌 Монтирую NFS-ресурс $NFS_SERVER:$NFS_PATH в $PACKAGES_DIR..." - sudo mkdir -p "$PACKAGES_DIR" - 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" -fi - -# Копируем содержимое директории в рабочую директорию -echo "📂 Копирую файлы проекта в рабочую директорию..." -cp -r "$PACKAGE_DIR"/* /home/builder/package/ -if [ $? -ne 0 ]; then - echo "🚫 Ошибка при копировании файлов проекта" - if [ "$NFS_ENABLED" -eq 1 ]; then - sudo umount "$PACKAGES_DIR" - fi - exit 1 -fi -echo "✅ Файлы успешно скопированы" - -# Переходим в директорию пакета -cd /home/builder/package || { - echo "🚫 Ошибка: Не удалось перейти в директорию /home/builder/package" - if [ "$NFS_ENABLED" -eq 1 ]; then - sudo umount "$PACKAGES_DIR" - fi - exit 1 -} -echo "✅ Перешел в директорию проекта" - -# Проверяем зависимости и собираем пакет -echo "🔨 Проверяю зависимости и собираю APK-пакет..." - -# Устанавливаем флаг NFS_ENABLED, если указаны оба флага -s и -n -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" - 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 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..ae7edb9 --- /dev/null +++ b/build.sh @@ -0,0 +1,188 @@ +#!/bin/sh + +usage() { + 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 " -b, --base-image-version Версия базового образа Alpine Linux (обязательно)" + echo " -o, --old Использовать метод сборки для устаревшей версии Docker" + echo " -R, --repositories Список репозиториев для сборки (через точку с запятой)" + echo " -r, --push-to-registry Адрес registry для отправки образов" + echo " -p, --port Порт registry" + 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)" + usage 1 + } +} + +validate_address() { + if [ -z "$1" ]; then + echo "❌ Ошибка: Адрес registry не может быть пустым" + usage 1 + fi +} + +validate_port() { + if [ -n "$1" ]; then + echo "$1" | grep -Eq '^[0-9]+$' || { + echo "❌ Ошибка: Порт должен быть числом" + usage 1 + } + fi +} + +check_dockerfile() { + if [ ! -f "$1" ]; then + echo "❌ Ошибка: Файл $1 не найден в директории docker" + usage 1 + fi +} + +push_to_registry=false +name="" +version="" +base_image_version="" +registry_address="" +port="" +use_old=false +dockerfile="Dockerfile.27.3.1" +repositories="" + +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 "$base_image_version" ]; then + echo "❌ Ошибка: Не указаны обязательные аргументы (-n, -v и -b)" + usage 1 +fi + +validate_version "$version" + +if [ "$push_to_registry" = true ]; then + if [ -z "$registry_address" ]; then + echo "❌ Ошибка: Для отправки в registry необходимо указать адрес с -r" + usage 1 + fi + + validate_address "$registry_address" + validate_port "$port" +fi + +echo "📂 Переход в директорию docker..." +cd docker || { + echo "❌ Ошибка: Не удалось перейти в директорию docker" + exit 1 +} + +if [ "$use_old" = true ]; then + dockerfile="Dockerfile.20.10.20" +fi + +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 + if [ -z "$port" ]; then + registry="$registry_address" + else + registry="$registry_address:$port" + fi + + echo "🏷 Создание тегов для registry..." + + docker tag "${name}:${version}" "${registry}/${name}:latest" || { + echo "❌ Ошибка при создании тега latest" + exit 1 + } + + major=$(echo "$version" | cut -d. -f1) + minor=$(echo "$version" | cut -d. -f1-2) + full="$version" + + docker tag "${name}:${version}" "${registry}/${name}:$full" || { + echo "❌ Ошибка при создании тега $full" + exit 1 + } + docker tag "${name}:${version}" "${registry}/${name}:$minor" || { + echo "❌ Ошибка при создании тега $minor" + exit 1 + } + docker tag "${name}:${version}" "${registry}/${name}:$major" || { + echo "❌ Ошибка при создании тега $major" + exit 1 + } + + echo " ✅ Созданы теги: latest, $full, $minor, $major" + + echo "🚀 Отправка образов в registry..." + docker push "${registry}/${name}:latest" || { + echo "❌ Ошибка при отправке тега latest" + exit 1 + } + docker push "${registry}/${name}:$full" || { + echo "❌ Ошибка при отправке тега $full" + exit 1 + } + docker push "${registry}/${name}:$minor" || { + echo "❌ Ошибка при отправке тега $minor" + exit 1 + } + docker push "${registry}/${name}:$major" || { + echo "❌ Ошибка при отправке тега $major" + exit 1 + } +fi + +echo "🎉 Готово! Образ ${name}:${version} успешно:" +echo " ✅ Собран" +[ "$push_to_registry" = true ] && echo " ✅ Размечен" +[ "$push_to_registry" = true ] && echo " ✅ Отправлен в registry" diff --git a/docker/Dockerfile.20.10.20 b/docker/Dockerfile.20.10.20 new file mode 100644 index 0000000..d56ef37 --- /dev/null +++ b/docker/Dockerfile.20.10.20 @@ -0,0 +1,31 @@ +ARG BASE_IMAGE_VERSION=latest + +FROM alpine:${BASE_IMAGE_VERSION} + +LABEL maintainer="Alexander Zhirov " +LABEL version="0.1.0" +LABEL description="Адаптированный для Docker <=20.10.20 образ сборщика APK-файлов для Alpine Linux" + +ENV BUILDER_USER=builder \ + REPOSITORY_DIR=/repository \ + KEYS_DIR=/keys \ + SOURCE_DIR=/source \ + REPOSITORY_LIST= \ + REPOSITORY_NAME_DIR= \ + NFS_SERVER= \ + NFS_PORT=5000 \ + NFS_PATH= + +COPY apk-builder /usr/bin/apk-builder + +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 nodejs npm git util-linux && \ + chmod 0755 /usr/bin/apk-builder + +ENTRYPOINT ["/usr/bin/apk-builder"] diff --git a/docker/Dockerfile.27.3.1 b/docker/Dockerfile.27.3.1 new file mode 100644 index 0000000..0b491a8 --- /dev/null +++ b/docker/Dockerfile.27.3.1 @@ -0,0 +1,32 @@ +ARG BASE_IMAGE_VERSION=latest + +FROM alpine:${BASE_IMAGE_VERSION} + +LABEL maintainer="Alexander Zhirov " +LABEL version="0.1.0" +LABEL description="Адаптированный для Docker >=27.3.1 образ сборщика APK-файлов для Alpine Linux" + +ENV BUILDER_USER=builder \ + REPOSITORY_DIR=/repository \ + KEYS_DIR=/keys \ + SOURCE_DIR=/source \ + REPOSITORY_LIST= \ + REPOSITORY_NAME_DIR= \ + NFS_SERVER= \ + NFS_PORT=5000 \ + NFS_PATH= + +COPY --chmod=0755 apk-builder /usr/bin/apk-builder + +ARG REPOSITORIES + +RUN < /etc/apk/repositories + fi + apk update + apk upgrade --no-cache + apk add --no-cache build-base alpine-sdk abuild sudo git nfs-utils nodejs npm git util-linux +EOF + +ENTRYPOINT ["/usr/bin/apk-builder"] diff --git a/docker/apk-builder b/docker/apk-builder new file mode 100644 index 0000000..33ca993 --- /dev/null +++ b/docker/apk-builder @@ -0,0 +1,148 @@ +#!/bin/sh + +: "${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 +} + +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 + +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 + +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 + log error "Недопустимое имя ключа: $NAME_KEY 😕 Используйте только буквы, цифры, _ и -" + exit 1 +fi + +if [ -n "$PRIVATE_KEY" ] || [ -n "$PUBLIC_KEY" ]; then + 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 + log error "Неверный формат приватного ключа 😕" + exit 1 + fi + if ! echo "$PUBLIC_KEY" | grep -q "BEGIN PUBLIC KEY"; then + log error "Неверный формат публичного ключа 😕" + exit 1 + fi + + 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 "$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 + su - "$BUILDER_USER" -c "echo \"$KEYS_DIR/$NAME_KEY.rsa\" | abuild-keygen -a -i" || { log error "Не удалось сгенерировать ключи 😞"; exit 1; } + log success "Новые ключи успешно сгенерированы! 🆕" + 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; } + +log success "Сборка пакета успешно завершена! 🎉" diff --git a/v.3.21/Dockerfile b/v.3.21/Dockerfile deleted file mode 100644 index 2aee809..0000000 --- a/v.3.21/Dockerfile +++ /dev/null @@ -1,42 +0,0 @@ -FROM alpine:3.16.9 - -# Метаданные для CI/CD -LABEL maintainer="Alexander Zhirov " -LABEL version="0.1.0" -LABEL description="Адаптированный для Docker >=27.3.1 образ сборщика APK-файлов для Alpine Linux 3.16.9" - -# Задаем стандартные значения через переменные окружения -ENV USER=builder \ - PACKAGE_DIR=/package \ - NFS_SERVER= \ - NFS_PATH= \ - PRIVATE_KEY= \ - PUBLIC_KEY= \ - REPODEST_DIR= - -# Копируем скрипт запуска в контейнер -COPY --chmod=0755 ../apk-build /usr/bin/apk-build - -# Устанавливаем необходимые пакеты для сборки APK и NFS -RUN < /etc/apk/repositories - echo "http://mirror.yandex.ru/mirrors/alpine/v3.16/community" >> /etc/apk/repositories - 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 -EOF - -# Переключаемся на пользователя builder -USER builder - -# Создаем рабочую директорию -WORKDIR /home/builder/package - -# Указываем точку входа для CI/CD -ENTRYPOINT ["/usr/bin/apk-build"] - -# Указываем команду по умолчанию с переменными окружения -CMD ["-p", "/package"] diff --git a/v3.16/Dockerfile b/v3.16/Dockerfile deleted file mode 100644 index c5a46b9..0000000 --- a/v3.16/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -FROM alpine:3.16.9 - -# Метаданные для CI/CD -LABEL maintainer="Alexander Zhirov " -LABEL version="0.1.0" -LABEL description="Адаптированный для Docker <=20.10.20 образ сборщика APK-файлов для Alpine Linux 3.16.9" - -# Задаем стандартные значения через переменные окружения -ENV USER=builder \ - PACKAGE_DIR=/package \ - NFS_SERVER= \ - NFS_PATH= \ - PRIVATE_KEY= \ - PUBLIC_KEY= \ - REPODEST_DIR= - -# Копируем скрипт запуска в контейнер -COPY ../apk-build /usr/bin/apk-build - -# Устанавливаем необходимые пакеты для сборки 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 && \ - 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/apk-build && \ - mkdir /home/builder/package && \ - chown builder:builder /home/builder/package - -# Переключаемся на пользователя builder -USER builder - -# Создаем рабочую директорию -WORKDIR /home/builder/package - -# Указываем точку входа для CI/CD -ENTRYPOINT ["/usr/bin/apk-build"] - -# Указываем команду по умолчанию с переменными окружения -CMD ["-p", "/package"]