ci / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 46s
ci / ts (packages + web) (push) Successful in 38s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (apps/web/Dockerfile, web, ) (push) Successful in 44s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Successful in 51s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/recommender/Dockerfile, recommender, ) (push) Successful in 29s
ci / mobile (Metro-бандл Android) (push) Successful in 51s
ci / api (push) Successful in 1m23s
ci / recommender (push) Successful in 37s
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, api, api) (push) Successful in 1m33s
release / release (заметки) (push) Failing after 5s
release / android (APK в релиз) (push) Skipped
release / verify (push) Successful in 1m19s
The release job now creates the Gitea release right after verification with generated notes: Android build status, changes grouped by Conventional Commit type with commit links, server update steps and APK install/check notes. The android job attaches the APK and .sha256 and rewrites only the status block (building / built with size, versionCode and SHA-256 / failed with a log link). scripts/release.mjs is idempotent on re-runs and unit-tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
85 lines
6.1 KiB
Markdown
85 lines
6.1 KiB
Markdown
# CI/CD (Gitea Actions)
|
||
|
||
Workflows лежат в `.gitea/workflows/`. Синтаксис совместим с GitHub Actions; runner — существующий `act_runner`
|
||
с доступом к Docker.
|
||
|
||
Сборке APK нужно ~15 ГБ свободного места на хосте раннера (Android SDK, NDK ~2.5 ГБ, кэш Gradle,
|
||
C++-сборка React Native). APK собирается только под `arm64-v8a`. Если job падает с `No space left on device`,
|
||
освободите место на хосте раннера: `docker system prune -af` и старые кэши act в `~/.cache/act`.
|
||
|
||
## Версионирование
|
||
|
||
- SemVer, тег `vMAJOR.MINOR.PATCH` (предрелизы: `v1.2.0-rc.1` — помечаются как pre-release, мобильное их
|
||
предлагает только при включённой настройке «бета-версии»).
|
||
- Одна версия на весь продукт: образы, APK и веб собираются с одним номером.
|
||
- Android `versionCode = MAJOR·1 000 000 + MINOR·10 000 + PATCH·100 + (99 для релиза | N для -rc.N)`,
|
||
так что `1.2.0-rc.1 < 1.2.0-rc.2 < 1.2.0 < 1.2.1`; MINOR и PATCH ≤ 99. Считает `scripts/version.mjs`
|
||
(тесты — `pnpm test:scripts`).
|
||
- Changelog — `git-cliff` из Conventional Commits.
|
||
|
||
## `ci.yml` — на каждый push и PR
|
||
|
||
```yaml
|
||
on:
|
||
push: { branches: [main] }
|
||
pull_request:
|
||
|
||
jobs:
|
||
changes: # фильтр путей: api / recommender / web / mobile / packages / docs
|
||
contract: # spectral lint openapi.yaml; регенерация Go + TS клиентов; git diff --exit-code
|
||
api: # go vet, golangci-lint, go test -race ./... (testcontainers), покрытие
|
||
recommender: # uv sync; ruff; mypy --strict; pytest (вкл. оффлайн-оценку качества)
|
||
ts: # pnpm install --frozen-lockfile; turbo lint typecheck test; i18n:check
|
||
web-build: # vite build, проверка размера бандла
|
||
docker: # сборка всех Docker-образов без push (сервер собирает их сам — ADR-013)
|
||
mobile-check: # tsc, jest, expo prebuild + ./gradlew assembleDebug (только если менялся mobile/)
|
||
e2e: # needs: api, web-build; docker compose -f deploy/compose.test.yaml up; playwright test
|
||
```
|
||
|
||
- Кэши: Go modules / build cache, pnpm store, uv cache, Gradle, сгенерированные тестовые аудио.
|
||
- Ветка `main` защищена: обязательны `contract`, `api`, `recommender`, `ts`, `e2e`.
|
||
- Ночью (`schedule`) — Maestro E2E на эмуляторе и полный прогон без фильтров путей.
|
||
|
||
## `release.yml` — по тегу
|
||
|
||
```yaml
|
||
on:
|
||
push:
|
||
tags: ['v*.*.*']
|
||
|
||
jobs:
|
||
verify: # все проверки ci.yml
|
||
release: # needs: verify — сразу создаёт Gitea Release (scripts/release.mjs create)
|
||
android: # needs: verify, release — собирает APK и докладывает его в релиз
|
||
```
|
||
|
||
Релиз появляется сразу после проверок, не дожидаясь APK. Текст строит `scripts/release.mjs`:
|
||
|
||
- **статус Android** вверху: «⏳ собирается» со ссылкой на ход сборки → «✅ собран» (имя файла, размер,
|
||
versionCode, SHA-256; предупреждение, если подпись отладочная) или «❌ не удалась» со ссылкой на лог;
|
||
- **изменения** — коммиты (Conventional Commits) с прошлого тега по разделам «Новое», «Исправления»,
|
||
«Сборка и CI» и т. д., отдельно — несовместимые (`feat!:`); для финального релиза отсчёт от прошлого
|
||
финального, а не от последнего `-rc`; ссылка на полный diff;
|
||
- **обновление сервера** — бэкап, `./update.sh vX.Y.Z`, проверка версии; **установка APK** и проверка SHA-256.
|
||
|
||
Job `android`: Android SDK, `expo prebuild`, `./gradlew assembleRelease` (только arm64-v8a), подпись `apksigner`,
|
||
затем `release.mjs android success <apk>` прикладывает APK и `.sha256` и меняет статус. При любом сбое —
|
||
`release.mjs android failure`. Перезапуск job безопасен: статус снова «собирается», файлы заменяются.
|
||
Перезапуск всего workflow обновляет текст существующего релиза, а не создаёт второй.
|
||
|
||
Docker-образы **не публикуются**: сервер собирает их сам из клонированного репозитория (ADR-013).
|
||
Чтобы сборка на сервере не ломалась, job `docker` в `ci.yml` собирает все образы без push на каждый PR.
|
||
|
||
## Секреты
|
||
|
||
| Секрет | Назначение |
|
||
|---|---|
|
||
| `RELEASE_TOKEN` | Создание и редактирование релиза, загрузка ассетов (scope `write:repository`) |
|
||
| `ANDROID_KEYSTORE_BASE64`, `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, `ANDROID_KEY_PASSWORD` | Подпись APK. **Ключ один на всю жизнь приложения** — хранить резервную копию вне Gitea. Без них финальный релиз падает; `-rc` собирается с отладочной подписью. |
|
||
|
||
## Деплой
|
||
|
||
Автодеплой не обязателен. На сервере в клоне репозитория: `deploy/update.sh [vX.Y.Z]` — checkout тега,
|
||
`docker compose build` и `up -d`; миграции применяются при старте `api` ([install-guide.md](install-guide.md)).
|
||
Опционально — job `deploy` в `release.yml` по SSH на сервер для не-pre-release тегов (с ручным approval через environment).
|