ci: Gitea Actions instead of Drone (workflow, README, secrets)
This commit is contained in:
parent
519903b930
commit
4f254d9faa
67
.drone.yml
67
.drone.yml
@ -1,67 +0,0 @@
|
||||
# Drone CI/CD бэкенда (сервер подключён к Gitea; см. README, «CI/CD»).
|
||||
#
|
||||
# Push/PR — только проверки и сборка образа. Деплой — ТОЛЬКО ручным
|
||||
# запуском пайплайна из UI Drone (событие custom, кнопка Run Pipeline):
|
||||
# шаг deploy выполняется исключительно при нём.
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: ci-cd
|
||||
|
||||
clone:
|
||||
depth: 1
|
||||
|
||||
# Сервисная база для интеграционных тестов: схему накатывает сам шаг
|
||||
# test (cmd/migrate), тесты выполняют сценарии в транзакции и откатывают
|
||||
# их — данные в базе не остаются.
|
||||
services:
|
||||
- name: postgres
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: building
|
||||
POSTGRES_PASSWORD: building
|
||||
POSTGRES_DB: building_mgmt_test
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
# Полный golang-образ, не alpine: -race требует CGO и C-компилятора.
|
||||
image: golang:1.27
|
||||
environment:
|
||||
DATABASE_URL: postgres://building:building@postgres:5432/building_mgmt_test?sslmode=disable
|
||||
commands:
|
||||
- go vet ./...
|
||||
# Postgres в сервисе поднимается параллельно со сборкой шага:
|
||||
# повторяем накат, пока база не готова (30 попыток по 2 секунды).
|
||||
- ok=0; for i in $(seq 1 30); do if go run ./cmd/migrate; then ok=1; break; fi; sleep 2; done; test "$ok" = 1
|
||||
- go test -race -count=1 ./...
|
||||
- go build ./...
|
||||
|
||||
- name: lint
|
||||
image: ghcr.io/golangci/golangci-lint:v2.14.0
|
||||
commands:
|
||||
- golangci-lint run ./...
|
||||
|
||||
- name: build-api
|
||||
# Образ и два тега: latest (его поднимает compose.prod.yaml) и SHA
|
||||
# (для отката: собрать нужный коммит и перетегнуть latest вручную).
|
||||
image: plugins/docker:27
|
||||
settings:
|
||||
repo: building-mgmt-api
|
||||
tags:
|
||||
- latest
|
||||
- ${DRONE_COMMIT_SHA}
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
- name: deploy
|
||||
image: docker:27-cli
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
# -p закрепляет имя проекта: у Drone каталог клонирования свой,
|
||||
# без -p имя сети стека разъехалось бы между запусками, и web
|
||||
# перестал бы находить api. env-файл — постоянный файл на сервере.
|
||||
- docker compose version
|
||||
- docker compose -p building-mgmt --env-file /opt/building-mgmt/.env -f compose.prod.yaml up -d --wait
|
||||
when:
|
||||
event:
|
||||
- custom
|
||||
@ -3,7 +3,7 @@
|
||||
# mkdir -p /opt/building-mgmt && cp .env.prod.example /opt/building-mgmt/.env
|
||||
# chmod 600 /opt/building-mgmt/.env # секреты, только root
|
||||
#
|
||||
# Заполняется один раз и правится осознанно: стек и Drone читают его
|
||||
# Заполняется один раз и правится осознанно: стек и CI читают его
|
||||
# по пути /opt/building-mgmt/.env. Пароли и секреты в git не входят.
|
||||
|
||||
# --- Обязательные переменные (compose упадёт без них: :? фейл-фаст) ---
|
||||
|
||||
90
.gitea/workflows/ci.yml
Normal file
90
.gitea/workflows/ci.yml
Normal file
@ -0,0 +1,90 @@
|
||||
# CI/CD бэкенда через Gitea Actions (встроенный в Gitea, раннер на сервере).
|
||||
#
|
||||
# push/PR: go vet → накат миграций в сервисную postgres → go test -race →
|
||||
# go build; параллельно golangci-lint; затем сборка прод-образа api
|
||||
# (локальные теги latest и короткий SHA, реестр не используется).
|
||||
# Деплой — ТОЛЬКО ручной запуск workflow (кнопка Run workflow): compose-файлы
|
||||
# берутся из дерева этого же коммита (actions/checkout), секреты не нужны.
|
||||
# web перезапускает свой workflow в репо веба — репозитории независимы.
|
||||
#
|
||||
# Требования к раннеру (см. README, раздел «CI/CD (Gitea Actions)»):
|
||||
# runner.labels: [docker] — совпадает с runs-on ниже;
|
||||
# container.valid_volumes: /var/run/docker.sock, /opt/building-mgmt —
|
||||
# сокет docker для сборки/деплоя и каталог стека с .env.
|
||||
name: backend
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: docker
|
||||
container: golang:1.27
|
||||
# Сервисная база для интеграционных тестов: схему накатывает шаг
|
||||
# test (cmd/migrate), тесты выполняют сценарии в транзакции и
|
||||
# откатывают их — данные в базе не остаются.
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
env:
|
||||
POSTGRES_USER: building
|
||||
POSTGRES_PASSWORD: building
|
||||
POSTGRES_DB: building_mgmt_test
|
||||
env:
|
||||
DATABASE_URL: postgres://building:building@postgres:5432/building_mgmt_test?sslmode=disable
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: go vet
|
||||
run: go vet ./...
|
||||
- name: Накат миграций (ждём сервисную postgres)
|
||||
# Postgres поднимается параллельно с контейнером джобы:
|
||||
# повторяем накат, пока база не готова (30 попыток по 2 секунды).
|
||||
run: |
|
||||
ok=0; for i in $(seq 1 30); do if go run ./cmd/migrate; then ok=1; break; fi; sleep 2; done; test "$ok" = 1
|
||||
- name: Тесты
|
||||
# -race требует CGO — полный golang-образ, не alpine.
|
||||
run: go test -race -count=1 ./...
|
||||
- name: Сборка
|
||||
run: go build ./...
|
||||
|
||||
lint:
|
||||
runs-on: docker
|
||||
container: ghcr.io/golangci/golangci-lint:v2.14.0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: golangci-lint
|
||||
run: golangci-lint run ./...
|
||||
|
||||
build-api:
|
||||
runs-on: docker
|
||||
container: docker:27-cli
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Сборка прод-образа api
|
||||
# Образ и два тега: latest (его поднимает compose.prod.yaml) и
|
||||
# короткий SHA (для отката: перетегнуть latest вручную).
|
||||
run: |
|
||||
short="$(echo "$GITHUB_SHA" | cut -c1-7)"
|
||||
docker build -t building-mgmt-api:latest -t "building-mgmt-api:${short}" .
|
||||
|
||||
deploy:
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
needs: build-api
|
||||
runs-on: docker
|
||||
container: docker:27-cli
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Подъём db/minio/api стека
|
||||
# compose-CLI ставим в джоб-контейнер; docker — через смонтированный
|
||||
# сокет хоста (valid_volumes у раннера). -p закрепляет имя проекта:
|
||||
# оно задаёт имя сети, и web из своего workflow находит api.
|
||||
# web не трогаем — его перезапускает workflow репо веба.
|
||||
run: |
|
||||
apk add --no-cache docker-cli-compose
|
||||
docker compose -p building-mgmt \
|
||||
--env-file /opt/building-mgmt/.env \
|
||||
-f compose.prod.yaml \
|
||||
up -d --wait db minio api
|
||||
81
README.md
81
README.md
@ -892,13 +892,13 @@ Handlers не отдают тексты ошибок БД наружу — он
|
||||
golangci-lint run ./... # или: make lint
|
||||
```
|
||||
|
||||
- **CI** — Drone (`.drone.yml`, сервер подключён к Gitea): `vet` → накат
|
||||
миграций на сервисный PostgreSQL → `go test -race ./...` → `go build` →
|
||||
`golangci-lint` → сборка docker-образа. Тесты в CI идут **на живой
|
||||
базе**, а не скипаются: без `DATABASE_URL` интеграционные тесты
|
||||
пропускаются молча. `-race` на живой базе дороже, зато ловит гонки
|
||||
между пулом соединений, лимитером и горутинами хендлеров. Деплой в
|
||||
прод — ручным запуском того же пайплайна (см. «CI/CD (Drone + Gitea)»).
|
||||
- **CI** — Gitea Actions (`.gitea/workflows/ci.yml`, встроенный в Gitea):
|
||||
`vet` → накат миграций на сервисный PostgreSQL → `go test -race ./...` →
|
||||
`go build` → `golangci-lint` → сборка docker-образа. Тесты в CI идут
|
||||
**на живой базе**, а не скипаются: без `DATABASE_URL` интеграционные
|
||||
тесты пропускаются молча. `-race` на живой базе дороже, зато ловит
|
||||
гонки между пулом соединений, лимитером и горутинами хендлеров. Деплой
|
||||
в прод — ручным запуском того же workflow (см. «CI/CD (Gitea Actions)»).
|
||||
|
||||
- **Команды** — `Makefile`: `run`, `build`, `test`, `race`, `vet`, `lint`,
|
||||
`fmt`, `swag`, `migrate`, `seed`, `up`, `down`, `tidy`. На Windows без
|
||||
@ -1028,7 +1028,7 @@ cp /opt/building-mgmt-repo/.env.prod.example /opt/building-mgmt/.env
|
||||
chmod 600 /opt/building-mgmt/.env
|
||||
$EDITOR /opt/building-mgmt/.env # пароли, секреты, домен — см. комментарии
|
||||
|
||||
# 2. Образы (Drone собирает их сам; вручную — если CI ещё не подключён)
|
||||
# 2. Образы (CI собирает их сам; вручную — если CI ещё не подключён)
|
||||
docker build -t building-mgmt-api:latest /opt/building-mgmt-repo
|
||||
docker build -t building-mgmt-web:latest <клон репо web>
|
||||
|
||||
@ -1038,7 +1038,7 @@ docker compose -p building-mgmt \
|
||||
-f /opt/building-mgmt-repo/compose.prod.yaml up -d --wait
|
||||
```
|
||||
|
||||
`-p building-mgmt` обязателен и в ручных запусках, и в Drone: имя
|
||||
`-p building-mgmt` обязателен и в ручных запусках, и в CI: имя
|
||||
проекта задаёт имя сети, разные имена разнесут web и api по разным
|
||||
сетям, и прокси перестанет находить api. Проверка: `http://<домен>/ping`
|
||||
→ `{"message":"pong"}`. `APP_ENV=production` (задан в шаблоне) блокирует
|
||||
@ -1076,50 +1076,59 @@ docker run --rm -v building-mgmt_miniodata:/data -v "$PWD":/backup alpine \
|
||||
|
||||
---
|
||||
|
||||
## CI/CD (Drone + Gitea)
|
||||
## CI/CD (Gitea Actions)
|
||||
|
||||
CI живёт в Drone: сервер подключён к Gitea, пайплайны — `.drone.yml` в
|
||||
обоих репозиториях (бэкенд и веб). GitHub Actions больше нет.
|
||||
CI/CD живёт во встроенном Gitea Actions: пайплайны —
|
||||
`.gitea/workflows/ci.yml` в обоих репозиториях (бэкенд и веб), сервер —
|
||||
тот же `axe.engir.by`. GitHub Actions и Drone не используются.
|
||||
|
||||
### Установка (однократно, на прод-сервере)
|
||||
|
||||
```bash
|
||||
# В Gitea: Настройки → Applications → OAuth2-приложение,
|
||||
# Redirect URI: http://<хост-drone>:8081/authorize
|
||||
mkdir -p /opt/drone && cp deploy/drone/compose.yaml /opt/drone/
|
||||
# /opt/drone/.env: GITEA_SERVER, DRONE_GITEA_CLIENT_ID,
|
||||
# DRONE_GITEA_CLIENT_SECRET, DRONE_GITEA_ADMIN_USERNAME,
|
||||
# DRONE_RPC_SECRET= # openssl rand -hex 32
|
||||
docker compose -p drone --env-file /opt/drone/.env \
|
||||
-f /opt/drone/compose.yaml up -d
|
||||
```ini
|
||||
# app.ini Gitea (Gitea ≥ 1.21), затем перезапуск Gitea:
|
||||
[actions]
|
||||
ENABLED = true
|
||||
```
|
||||
|
||||
Drone UI — `http://<хост>:8081` (за TLS-терминалом); оттуда же Gitea
|
||||
шлёт webhook. Runner исполняет шаги пайплайнов в docker на этом же
|
||||
сервере, одна задача за раз (`capacity=1` — деплои не пересекаются).
|
||||
Раннер исполняет задачи в docker на этом же сервере. Регистрация —
|
||||
токеном из UI Gitea (Администрирование → Раннеры → Создать
|
||||
регистрационный токен, метка `docker`), каталог `/opt/act-runner`; в
|
||||
`config.yaml` раннера обязательны две вещи:
|
||||
|
||||
### Секреты пайплайна (UI Drone, репозиторий веба)
|
||||
```yaml
|
||||
runner:
|
||||
labels: [docker] # совпадает с runs-on: docker в workflow
|
||||
container:
|
||||
valid_volumes: # сокет docker для сборки/деплоя образов и
|
||||
- /var/run/docker.sock # каталог стека с постоянным .env
|
||||
- /opt/building-mgmt
|
||||
```
|
||||
|
||||
- `gitea_host`, `gitea_user`, `gitea_token` — доступ к приватному репо
|
||||
бэкенда: web-пайплайн клонирует его за токен, чтобы перезапустить
|
||||
`web` тем же `compose.prod.yaml` (один стек, один проект).
|
||||
### Секреты (Настройки репо → Секреты → Actions)
|
||||
|
||||
Секреты стека (`/opt/building-mgmt/.env`) в Drone не хранятся: пайплайн
|
||||
- бэкенд (`axe`) — секреты не нужны: деплой берёт `compose.prod.yaml`
|
||||
из дерева того же коммита (`actions/checkout`);
|
||||
- веб (`axe-web`): `CI_HOST` (`axe.engir.by`), `CI_USER` (`edadmin`),
|
||||
`CI_TOKEN` (PAT с правом `write:repository`) — web-workflow скачивает
|
||||
тарбол репо бэкенда, чтобы перезапустить `web` тем же
|
||||
`compose.prod.yaml` (один стек, один проект). Имена с префиксом
|
||||
`GITEA_` Gitea запрещает — префикс зарезервирован.
|
||||
|
||||
Секреты стека (`/opt/building-mgmt/.env`) в Actions не хранятся: деплой
|
||||
только читает файл на сервере флагом `--env-file`.
|
||||
|
||||
### Что делает каждый пайплайн
|
||||
### Что делает каждый workflow
|
||||
|
||||
- бэкенд (push/PR и ручной запуск): `vet` → `migrate` → `test -race` →
|
||||
`go build` → `golangci-lint` → сборка образа
|
||||
`building-mgmt-api:latest` + тег по SHA;
|
||||
- веб (push/PR и ручной запуск): `node --check` js → сборка образа
|
||||
`building-mgmt-web:latest` + тег по SHA;
|
||||
- **деплой — только ручным запуском** (кнопка **Run Pipeline** в UI,
|
||||
событие `custom`): бэкенд — `docker compose -p building-mgmt
|
||||
--env-file /opt/building-mgmt/.env -f compose.prod.yaml up -d --wait`
|
||||
(пересоздаёт api новым образом, данные не трогает); веб — то же
|
||||
с `--no-deps web`. Push в `main` образы собирает, но на сервер ничего
|
||||
не ставит.
|
||||
- **деплой — только ручным запуском** (кнопка **Run workflow**): бэкенд —
|
||||
`docker compose -p building-mgmt --env-file /opt/building-mgmt/.env
|
||||
-f compose.prod.yaml up -d --wait db minio api` (пересоздаёт api новым
|
||||
образом, данные не трогает); веб — то же с `--no-deps web`. Push в
|
||||
`main` образы собирает, но на сервер ничего не ставит.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# web (порт 80). Postgres и MinIO портов наружу не пробрасывают вовсе —
|
||||
# они видны только контейнерам приложения.
|
||||
#
|
||||
# Образы api и web заранее собраны Drone (или вручную):
|
||||
# Образы api и web заранее собраны CI (или вручную):
|
||||
# docker build -t building-mgmt-api:latest . # в этом репо
|
||||
# docker build -t building-mgmt-web:latest . # в репо web
|
||||
#
|
||||
@ -11,7 +11,7 @@
|
||||
# --env-file /opt/building-mgmt/.env \
|
||||
# -f compose.prod.yaml up -d --wait
|
||||
#
|
||||
# Имя проекта (-p) закреплено: по нему считается имя сети. Drone и
|
||||
# Имя проекта (-p) закреплено: по нему считается имя сети. CI и
|
||||
# ручной запуск обязаны давать одно и то же имя, иначе web не найдёт
|
||||
# api и стек разъедется на два.
|
||||
#
|
||||
@ -71,7 +71,7 @@ services:
|
||||
|
||||
api:
|
||||
image: building-mgmt-api:latest
|
||||
# build: — на случай ручной сборки без Drone: если образа нет,
|
||||
# build: — на случай ручной сборки без CI: если образа нет,
|
||||
# compose соберёт его из этого же репо; готовый образ не трогает.
|
||||
build:
|
||||
context: .
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
# Drone CI: server (интерфейс, webhook от Gitea) и docker-runner
|
||||
# (исполняет шаги .drone.yml в контейнерах этого хоста).
|
||||
#
|
||||
# Перед запуском — в Gitea: Настройки → Applications → OAuth2-приложение,
|
||||
# Redirect URI: http://<хост-drone>:8081/authorize
|
||||
#
|
||||
# .env рядом с этим файлом (в git не входит):
|
||||
# GITEA_SERVER=https://gitea.example.com
|
||||
# DRONE_GITEA_CLIENT_ID=...
|
||||
# DRONE_GITEA_CLIENT_SECRET=...
|
||||
# DRONE_GITEA_ADMIN_USERNAME=admin # аккаунт Gitea, станет админом Drone
|
||||
# DRONE_SERVER_HOST=drone.example.com
|
||||
# DRONE_SERVER_PROTO=https
|
||||
# DRONE_RPC_SECRET= # openssl rand -hex 32
|
||||
#
|
||||
# Запуск: docker compose -p drone --env-file .env -f compose.yaml up -d
|
||||
|
||||
services:
|
||||
drone:
|
||||
image: drone/drone:2
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
# Веб-интерфейс и цель webhook от Gitea; за TLS-терминалом.
|
||||
- "8081:80"
|
||||
environment:
|
||||
DRONE_GITEA_SERVER: ${GITEA_SERVER:?GITEA_SERVER is required}
|
||||
DRONE_GITEA_CLIENT_ID: ${DRONE_GITEA_CLIENT_ID:?DRONE_GITEA_CLIENT_ID is required}
|
||||
DRONE_GITEA_CLIENT_SECRET: ${DRONE_GITEA_CLIENT_SECRET:?DRONE_GITEA_CLIENT_SECRET is required}
|
||||
# Общий секрет server ↔ runner: openssl rand -hex 32.
|
||||
DRONE_RPC_SECRET: ${DRONE_RPC_SECRET:?DRONE_RPC_SECRET is required}
|
||||
# Первый залогинившийся через Gitea становится админом Drone.
|
||||
DRONE_USER_CREATE_OAUTH: username=${DRONE_GITEA_ADMIN_USERNAME:?DRONE_GITEA_ADMIN_USERNAME is required},admin=true
|
||||
# Внешний адрес интерфейса: его видят Gitea (webhook) и браузер.
|
||||
DRONE_SERVER_HOST: ${DRONE_SERVER_HOST:?DRONE_SERVER_HOST is required}
|
||||
DRONE_SERVER_PROTO: ${DRONE_SERVER_PROTO:-https}
|
||||
volumes:
|
||||
- drone:/data
|
||||
networks:
|
||||
- drone
|
||||
|
||||
runner:
|
||||
image: drone/drone-runner-docker:1
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- drone
|
||||
environment:
|
||||
DRONE_RPC_PROTO: http
|
||||
DRONE_RPC_HOST: drone
|
||||
DRONE_RPC_SECRET: ${DRONE_RPC_SECRET:?DRONE_RPC_SECRET is required}
|
||||
# Один конвейер за раз: деплои не должны пересекаться.
|
||||
DRONE_RUNNER_CAPACITY: "1"
|
||||
DRONE_RUNNER_NAME: prod-server
|
||||
# Шаги пайплайнов (и их сервисы — postgres для тестов) поднимаются
|
||||
# именно в этой сети: имя сети указано явно (name: drone), чтобы
|
||||
# совпадало со значением DRONE_RUNNER_NETWORKS.
|
||||
DRONE_RUNNER_NETWORKS: drone
|
||||
volumes:
|
||||
# Доступ к docker-демону: сборка образов, compose, деплой.
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
networks:
|
||||
- drone
|
||||
|
||||
networks:
|
||||
# Имя фиксируем: DRONE_RUNNER_NETWORKS ссылается на него буквально,
|
||||
# а префикс проекта (drone_drone) runner бы не узнал.
|
||||
drone:
|
||||
name: drone
|
||||
|
||||
volumes:
|
||||
drone:
|
||||
Loading…
Reference in New Issue
Block a user