Пост

Forgejo. Миграции, реестр пакетов, CI/CD

Переносим проекты из внешних систем, поднимаем свой реестр Docker образов и настраиваем CI/CD внутри одного LXC

В предыдущей статье мы запустили сервер Forgejo в LXC Proxmox и остановились на странице первоначальной настройки.

forgejo-screenshot-6

Большинство настроек оставляем по умолчанию, включая использование однофайловой субд SQLite - для небольшой команды или homelab вполне достаточно. Заполняем параметры учётной записи администратора и создаём сервер.

Особенности

Рассматривать стандартные для системы контроля версий сценарии работы я не стану - wiki, ветки, тэги, issues, pull requests, работа в команде, права доступа и т.д. - всё это понятно любому пользователю, который на постоянной основе с подобными решениями взаимодействует.

Отмечу то, что бросается в глаза сразу после начала работы:

  1. Быстрый и предсказуемый интерфейс, который не пестрит и не отвлекает.
  2. Отсутствие рекламных блоков, опций с намёком на социальную сеть (не считая follow и star) и AI фичей.
  3. Базовые по функциональности проекты, issues, поиск по коду - заметно проще по сравнению с GitHub или GitLab.
  4. Отсутствие встроенных сканеров безопасности - придётся подключать что-то извне.

Далее пройдёмся по некоторым практическим фичам.

Миграция репозитория

Для быстрого переноса своих проектов из GitHub / GitLab и других аналогичных сервисов в Forgejo предусмотрели миграции.

Создать... -> Выполнить перенос forgejo-2-screenshot-2

Для примера клонировал свой публичный проект website-builder-template с GitHub. Все ветки, коммиты и прочие параметры были перенесены, репозиторий готов к работе локально. Для миграции приватного хранилища требуется токен доступа. forgejo-2-screenshot-3

Pull-зеркало

Сценарий использования: Основная работа ведётся, например, на GitHub/GitLab, а Forgejo выступает как локальный бэкап или точка входа для вашей внутренней инфраструктуры (CI, Registry, локальные скрипты).

Как включить forgejo-2-screenshot-4 Среди параметров миграции есть чекбокс “Этот репозиторий будет зеркалом” - это вариант настройки pull-зеркала, т.е. ваша локальная версия будет периодически синхронизироваться с внешним репозиторием и подтягивать все изменения.

Push-зеркало

Сценарий использования: Вы работаете в Forgejo, но хотите, чтобы копия репозитория автоматически отправлялась на GitHub / GitLab.

Важно! Это способ односторонней синхронизации локального хранилища с внешним. Если кто-то сделает коммит напрямую во внешний репозиторий, а не в Forgejo, возникнет рассинхронизация. Forgejo не сможет смержить конфликты автоматически и зеркало “упадёт” со статусом ошибки.

Как включить

  • Создаём Personal Access Token с доступом к репозиторию на внешнем ресурсе.
  • Возвращаемся в репу в Forgejo: Настройки -> Репозиторий -> Зеркалирование
  • Указываем ссылку на внешнюю репу с добавлением секции авторизации.
    1
    2
    
    # GitHub
    https://oauth2:GITHUB_TOKEN@github.com/username/repo.git
    
  • Задаём интервал.
  • “Добавить push-зеркало”.

forgejo-2-screenshot-5

Встроенный Package Registry

Не нужно поднимать отдельные машины для хранения Docker образов, Go модулей или Python пакетов, т.к. в Forgejo уже встроен реестр с поддержкой множества популярных форматов, включая Docker, npm, NuGet, Maven, Conda, PyPI, Go, Helm и другие.

Настройка собственного Docker-хаба

1) Создаём токен доступа для приложений с правами на чтение и запись пакетов.

Настройки -> Приложения -> Новый токен доступа forgejo-2-screenshot-6

2) Если Forgejo работает по http, то нужно прописать адрес сервера в разрешенные реестры Docker Engine. Для https пропускаем.

1
2
3
4
5
6
7
8
9
10
11
12
13
# на ubuntu/debian с установленным Docker
# редактируем или создаём файл daemon.json
sudo vi /etc/docker/daemon.json

# прописываем адрес forgejo
{
  "insecure-registries": [
    "10.60.60.101:3000"
  ]
}

# перезапускаем docker
sudo systemctl restart docker

3) Авторизуемся через Docker в Forgejo.

1
2
3
4
5
6
docker login 10.60.60.101:3000

Username: desoft # имя пользователя в Forgejo
Password:	 # созданный ранее токен

Login Succeeded

4) Собираем и публикуем образ

Клонируем репозиторий, для которого будем собирать образ

1
2
git clone http://10.60.60.101:3000/desoft/website-builder-template-local.git
cd website-builder-template-local

Добавляем Dockerfile и тестовую html страницу

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Dockerfile
vi Dockerfile

FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80

# html
vi index.html

<!DOCTYPE html>
<html>
<head><title>Forgejo Docker Registry Test</title></head>
<body>
  <h1>Hello from Forgejo Docker Registry!</h1>
  <p>Образ собран и запушен в registry.</p>
</body>
</html>

Сборка и публикация

1
2
3
4
5
6
# сборка с правильным тегом
# формат: 10.60.60.101:3000/OWNER/REPO_NAME:TAG
docker build -t 10.60.60.101:3000/desoft/website-builder-template-local:1.0.0 .

# публикация
docker push 10.60.60.101:3000/desoft/website-builder-template-local:1.0.0
  • Docker упаковывает образ
  • Отправляет его в Forgejo
  • Forgejo сохраняет образ в хранилище (по умолчанию в локальную файловую систему, но можно подключить и s3, если планируется большой объём артефактов).
  • Для очистки устаревших пакетов и сбора мусора как реестров так и самих репозиториев в административных настройках существуют заготовленные автозадачи, которые могут быть исполнены по расписанию или вручную.

forgejo-2-screenshot-7

Загрузка образа

1
2
3
4
# не забываем предварительно подключиться к реестру
# через docker login, если новая машина

docker pull 10.60.60.101:3000/desoft/website-builder-template-local:1.0.0

CI/CD на Forgejo Actions

На данном этапе у нас имеются репозиторий с кодом и собственный реестр Docker образов. Осталось связать все этапы работы над проектом автоматическим конвейером, чтобы обойтись без ручной сборки и публикации.

push в репозиторий → сборка образа → публикация в Registry

Forgejo Actions

Forgejo Actions — это система непрерывной интеграции, совместимая с GitHub Actions по синтаксису YAML.

Особенности:

  • Runner внутри LXC с Forgejo - отдельная виртуалка не требуется (runner можно и следует разместить на отдельной машине в случае работы даже небольшой команды, но для тестов / редких запусков хватит и такого исполнения).
  • Сборка прямо в контейнере - локально, безопасно, но надо учитывать повышение нагрузки в связи с этим.
  • Секреты и переменные хранятся в настройках репы / организации.
  • Кэш хранится локально. Автоматической очистки не происходит и задач под это еще добавлено не было, поэтому нужно держать на контроле и периодически чистить cache-каталог (обычно $HOME/.cache/actcache).

Устанавливаем Runner

Поскольку Forgejo у меня крутится в Docker-контейнере внутри LXC, то runner я размещу рядом в отдельном Docker-контейнере.

  • Готовим compose.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
networks:
  forgejo:
    external: false

services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:15
    container_name: forgejo
    environment:
      - USER_UID=1000
      - USER_GID=1000
    restart: unless-stopped
    networks:
      - forgejo
    volumes:
      - ./forgejo/data:/data
      - /etc/localtime:/etc/localtime:ro
    ports:
      - '3000:3000'
      - '222:22'

  dind:
    image: docker:dind
    container_name: forgejo-dind
    privileged: true
    command: ['dockerd', '-H', 'tcp://0.0.0.0:2375', '--tls=false']
    networks:
      - forgejo
    volumes:
      - dind-data:/var/lib/docker
      # если forgejo работает по http - добавляем конфигурацию
      - ./dind-daemon.json:/etc/docker/daemon.json:ro
    ports:
      - '2375:2375'

  runner:
    image: data.forgejo.org/forgejo/runner:4.0.0
    container_name: forgejo-runner
    depends_on:
      - dind
      - forgejo
    environment:
      DOCKER_HOST: tcp://10.60.60.101:2375
    networks:
      - forgejo
    volumes:
      - ./runner-data:/data
    command: '/bin/sh -c "while :; do sleep 1; done"'

volumes:
  dind-data:

forgejo — сам Git-сервер с веб-интерфейсом, реестром пакетов и системой управления репозиториями.

dind — отдельный Docker-демон внутри контейнера, который предоставляет runner’у API для сборки и пуша образов в изолированной среде.

runner — агент Forgejo Actions, который получает задачи от сервера и выполняет CI-пайплайны, обращаясь к dind для Docker-операций.

  • Создаём новый глобальный runner в веб интерфейсе Forgejo

Где: Панель управления -> Действия -> Исполнители -> Добавить нового исполнителя

Имя: runner-lxc

Описание: оставить пустым

После создания видим UUID и токен runner’а, однако с этим токеном зарегистрировать runner не получилось. На той же странице Исполнители есть вкладка Показать токен регистрации, вот его и запоминаем. forgejo-2-screenshot-8

  • Стартуем контейнеры и регистрируем runner
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# создаём директорию для данных runner
mkdir runner-data
chown -R 1000:1000 ./runner-data

# если forgejo работает по http, то для dind надо разрешить http реестр
# т.к. по умолчанию он требует https
vi dind-daemon.json

{
  "insecure-registries": ["10.60.60.101:3000"]
}

# запускаем контейнеры
docker compose up -d

# заходим в runner и регистрируем
docker exec -it forgejo-runner /bin/sh

forgejo-runner register
# Instance URL: http://10.60.60.101:3000
# Token: токен регистрации, который сохранили выше
# Name: runner-lxc
# Labels: оставить пустым

INFO Registering runner, name=runner-lxc, instance=http://10.60.60.101:3000, labels=[docker:docker://node:20-bullseye].
DEBU Successfully pinged the Forgejo instance server
INFO Runner registered successfully.

exit

# останавливаем контейнер runner'а
docker compose down runner

# меняем в compose.yaml строку command на
# command: '/bin/sh -c "sleep 5; forgejo-runner daemon"'

# запускаем контейнер runner'а
docker compose up -d runner

После этого в веб интерфейсе Forgejo среди исполнителей появится runner со статусом Простаивает (Idle).

Добавляем секреты

Pipeline будет пушить образ в Registry от имени Forgejo. Для этого нужен токен приложения, который мы создавали в предыдущем разделе для docker login.

  • Идём в настройки репы: Настройки -> Действия -> Секреты -> Добавить секрет.
  • Заполняем и сохраняем.
    • Название: REGISTRY_TOKEN
    • Значение: токен приложения

Для удобства можно добавить переменную с адресом реестра.

  • Настройки -> Действия -> Переменные -> Добавить переменную
  • Заполняем и сохраняем.
    • Название: REGISTRY_URL
    • Значение: адрес реестра (в моём случае 10.60.60.101:3000).

Готовим структуру workflow

В корне репозитория создаём директорию .forgejo

1
2
3
4
5
6
7
repo/
├── .forgejo/
│   └── workflows/
│       └── build.yml
├── Dockerfile
├── index.html
└── ...

Пишем workflow

Создаём файл .forgejo/workflows/build.yml в репозитории следующего содержания:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
name: Build and Push Docker Image

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  build:
    runs-on: docker
    env:
      DOCKER_HOST: tcp://10.60.60.101:2375
    steps:
      - uses: actions/checkout@v4

      - name: Install Docker CLI
        run: |
          apt-get update && apt-get install -y docker.io

      - name: Login to Registry
        run: |
          echo "$" | docker login $ -u $ --password-stdin

      - name: Build and push
        run: |
          TAG=${GITHUB_REF_NAME:-latest}
          docker build -t $/$:${TAG} .
          docker push $/$:${TAG}

Что тут происходит:

Шаг Действие Что происходит
on: push: branches: [main] / tags: ['v*'] Триггер Pipeline запускается при push в ветку main или при пуше git-тега v*
runs-on: docker Выбор runner Job выполняется внутри Docker-контейнера node:20-bullseye через наш Forgejo Runner
env: DOCKER_HOST: tcp://dind:2375 Переменная окружения Все docker-команды внутри job обращаются к демону dind по TCP вместо локального сокета
actions/checkout@v4 Клонирование кода Скачивает содержимое репозитория во временную рабочую директорию внутри job-контейнера
Install Docker CLI Установка пакета Внутри node:20-bullseye ставится docker.io, чтобы появилась команда docker
Login to Registry Авторизация Docker CLI логинится в ваш Forgejo Registry используя токен из Secrets
Build and push Сборка и публикация Определяется тег (имя ветки или тег git), собирается образ из Dockerfile, пушится в Registry
  • github.repository — автоматически подставит OWNER/REPO_NAME.
  • github.actor — логин пользователя, который запушил.
  • Для тегов v1.0.0 образ получит тег 1.0.0.

Запускаем и проверяем

Ранее мы в репу уже добавили Dockerfile и index.html для тестирования сборки образа. Сейчас коммитим и пушим файл workflow.

1
2
3
git add .forgejo/workflows/build.yml
git commit -m "ci: add docker build pipeline"
git push origin main
  • Forgejo ловит событие push.
  • Создаёт задачу (job) во вкладке Действия (Actions).
  • Внутри LXC поднимается временный контейнер (runner).
  • Выполняются шаги по порядку.

forgejo-2-screenshot-9 forgejo-2-screenshot-10

✅ Пакет собран и опубликован в реестре репозитория.

P. S. Часто вставляем ip адрес самого сервера forgejo в конфигурации, workflow, compose. Переход на https частично решает этот вопрос, а также вынос REGISTRY_URL в переменные окружения системы, а не только репозитория.

Таким образом, мы перенесли репозиторий с GitHub в собственный Forgejo, рассмотрели способы зеркалирования, развернули встроенный Package Registry и опубликовали в него первый Docker-образ, а затем связали всё в единый поток: теперь каждый push в main автоматически собирает образ и отправляет его в наш приватный реестр - все на одном LXC в Proxmox и под полным контролем.

В следующей части CI/CD рассмотрим еще глубже, runner’у выделим отдельный сервер, настроим работу с бэкапами и подключим внешние уведомления.

Больше полезной информации в Telegram-канале