- Shell 100%
| scripts | ||
| .gitignore | ||
| README.md | ||
Forgejo CI Templates
Универсальная схема CI/CD для проектов, где:
- Forgejo Actions собирает Docker image.
- Forgejo Registry хранит image.
- Komodo делает deploy через webhook/procedure после успешного push.
- Конкретные настройки окружений лежат в
.cicd/deploy/staging.yamlи.cicd/deploy/production.yaml. - Общая логика сборки лежит в отдельной репе шаблонов:
devops/forgejo-ci-templates.
Общая идея
В каждом проекте должен быть минимум конфигурации:
.forgejo/workflows/backend.yml
.forgejo/workflows/frontend.yml
.cicd/docker/Dockerfile
.cicd/deploy/staging.yaml
.cicd/deploy/production.yaml
Workflow в Forgejo почти одинаковый для всех проектов.
Разница между проектами задаётся в YAML-конфигах:
.cicd/deploy/staging.yaml
.cicd/deploy/production.yaml
Поддерживаемые ветки
Скрипт реагирует только на две ветки:
| Ветка | Конфиг | Окружение |
|---|---|---|
staging |
.cicd/deploy/staging.yaml |
staging |
master |
.cicd/deploy/production.yaml |
production |
Если push пришёл в другую ветку, скрипт ничего не делает и завершает job успешно.
Поведение скрипта
Если файла конфига нет
Например, push в staging, но файла нет:
.cicd/deploy/staging.yaml
Скрипт ничего не билдит, не пушит и не вызывает webhook.
Job завершается успешно.
Deploy config not found: .cicd/deploy/staging.yaml
Skipping build, push and webhook.
Если в конфиге указано disabled: true
disabled: true
Скрипт полностью игнорирует этот конфиг.
Не будет:
- frontend/backend checks;
- docker build;
- docker push;
- webhook.
Job завершается успешно.
Deploy config disabled: .cicd/deploy/staging.yaml
Skipping build, push and webhook.
Это удобно, если окружение временно отключено.
Если в конфиге нет webhook_url
Image будет собран и запушен в registry, но Komodo webhook вызван не будет.
No webhook_url configured for site '__single__', skipping Komodo webhook.
Если webhook_url указан
После успешного:
docker build
docker push
скрипт вызовет Komodo webhook.
Webhook вызывается только после успешного push обоих тегов:
image:environment
image:environment-sha
Docker image tags
Для каждого билда пушатся два тега.
Для production:
git.azure-net.ru/project/backend:production
git.azure-net.ru/project/backend:production-<commit_sha>
Для staging:
git.azure-net.ru/project/backend:staging
git.azure-net.ru/project/backend:staging-<commit_sha>
Первый тег — moving tag, его удобно использовать в Komodo:
image: git.azure-net.ru/project/backend:production
Второй тег — конкретная версия для rollback:
image: git.azure-net.ru/project/backend:production-ab8069275a5c6c81134e460cdb11d917ab8a6b38
Важное ограничение Docker image name
Docker image name должен быть lowercase.
Правильно:
image: git.azure-net.ru/drevproject/drevproject-site-backend
Неправильно:
image: git.azure-net.ru/DrevProject/drevproject-site-backend
Docker упадёт с ошибкой:
repository name must be lowercase
Secrets
В репе проекта нужно добавить secrets:
Settings → Actions → Secrets
Обязательные secrets
REGISTRY_USER
REGISTRY_PASSWORD
Они используются для:
docker login git.azure-net.ru
Optional secret для Komodo webhook
KOMODO_WEBHOOK_SECRET
Если secret указан, скрипт отправит webhook с подписью:
X-Hub-Signature-256: sha256=<signature>
Если secret не указан, webhook будет вызван обычным POST-запросом без подписи.
Backend workflow
Файл:
.forgejo/workflows/backend.yml
name: backend
on:
push:
branches:
- staging
- master
workflow_dispatch:
jobs:
build:
runs-on: docker
env:
FORGEJO_REF_NAME: ${{ forgejo.ref_name }}
FORGEJO_SHA: ${{ forgejo.sha }}
FORGEJO_REPOSITORY: ${{ forgejo.repository }}
REGISTRY: git.azure-net.ru
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
KOMODO_WEBHOOK_SECRET: ${{ secrets.KOMODO_WEBHOOK_SECRET }}
steps:
- uses: actions/checkout@v4
- name: Login to registry
run: |
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY" \
-u "$REGISTRY_USER" \
--password-stdin
- name: Load CI templates
run: |
git clone --depth 1 https://git.azure-net.ru/devops/forgejo-ci-templates.git /tmp/forgejo-ci-templates
- name: Build backend
run: |
/tmp/forgejo-ci-templates/scripts/build-docker.sh backend
Frontend workflow
Файл:
.forgejo/workflows/frontend.yml
name: frontend
on:
push:
branches:
- staging
- master
workflow_dispatch:
jobs:
build:
runs-on: docker
env:
FORGEJO_REF_NAME: ${{ forgejo.ref_name }}
FORGEJO_SHA: ${{ forgejo.sha }}
FORGEJO_REPOSITORY: ${{ forgejo.repository }}
REGISTRY: git.azure-net.ru
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
KOMODO_WEBHOOK_SECRET: ${{ secrets.KOMODO_WEBHOOK_SECRET }}
steps:
- uses: actions/checkout@v4
- name: Login to registry
run: |
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY" \
-u "$REGISTRY_USER" \
--password-stdin
- name: Load CI templates
run: |
git clone --depth 1 https://git.azure-net.ru/devops/forgejo-ci-templates.git /tmp/forgejo-ci-templates
- name: Build frontend
run: |
/tmp/forgejo-ci-templates/scripts/build-docker.sh frontend
Backend deploy config
Production
Файл:
.cicd/deploy/production.yaml
Минимальный вариант:
image: git.azure-net.ru/drevproject/drevproject-site-backend
context: .
dockerfile: .cicd/docker/Dockerfile
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/PROCEDURE_ID/main
Staging
Файл:
.cicd/deploy/staging.yaml
image: git.azure-net.ru/drevproject/drevproject-site-backend
context: .
dockerfile: .cicd/docker/Dockerfile
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/STAGING_PROCEDURE_ID/main
Backend с тестами
Если нужно прогонять тесты перед docker build:
image: git.azure-net.ru/drevproject/drevproject-site-backend
context: .
dockerfile: .cicd/docker/Dockerfile
test_command: dotnet test --configuration Release
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/PROCEDURE_ID/main
Если test_command не указан, backend-тесты будут пропущены:
No backend test_command found, skipping backend tests
Отключённый backend env
Например, staging временно выключен:
disabled: true
image: git.azure-net.ru/drevproject/drevproject-site-backend
context: .
dockerfile: .cicd/docker/Dockerfile
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/STAGING_PROCEDURE_ID/main
Скрипт ничего не соберёт и завершится успешно.
Frontend deploy config
Single-site frontend
Файл:
.cicd/deploy/production.yaml
image: git.azure-net.ru/drevproject/drevproject-site-frontend
context: .
dockerfile: .cicd/docker/Dockerfile
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/PROCEDURE_ID/main
build_args:
PUBLIC_BACKEND_API_URL: https://api.example.com
PUBLIC_FRONTEND_DOMAIN: example.com
PUBLIC_TOKEN_COOKIE_NAME: access_token
PUBLIC_REFRESH_TOKEN_COOKIE_NAME: refresh_token
PUBLIC_LANG_COOKIE_NAME: lang
Для staging:
image: git.azure-net.ru/drevproject/drevproject-site-frontend
context: .
dockerfile: .cicd/docker/Dockerfile
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/STAGING_PROCEDURE_ID/main
build_args:
PUBLIC_BACKEND_API_URL: https://api-stage.example.com
PUBLIC_FRONTEND_DOMAIN: stage.example.com
PUBLIC_TOKEN_COOKIE_NAME: access_token
PUBLIC_REFRESH_TOKEN_COOKIE_NAME: refresh_token
PUBLIC_LANG_COOKIE_NAME: lang
Multi-site frontend
Используется, если одна frontend-репа собирает несколько разных сайтов с разными env.
Файл:
.cicd/deploy/production.yaml
image: git.azure-net.ru/drevproject/drevproject-site-frontend
context: .
dockerfile: .cicd/docker/Dockerfile
defaults:
build_args:
PUBLIC_TOKEN_COOKIE_NAME: access_token
PUBLIC_REFRESH_TOKEN_COOKIE_NAME: refresh_token
PUBLIC_LANG_COOKIE_NAME: lang
sites:
main:
image_tag: production-main
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/MAIN_PROCEDURE_ID/main
build_args:
PUBLIC_BACKEND_API_URL: https://api.example.com
PUBLIC_FRONTEND_DOMAIN: example.com
second:
image_tag: production-second
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/SECOND_PROCEDURE_ID/main
build_args:
PUBLIC_BACKEND_API_URL: https://api-second.example.com
PUBLIC_FRONTEND_DOMAIN: second.example.com
В этом варианте будут собраны и запушены:
git.azure-net.ru/drevproject/drevproject-site-frontend:production-main
git.azure-net.ru/drevproject/drevproject-site-frontend:production-main-<sha>
git.azure-net.ru/drevproject/drevproject-site-frontend:production-second
git.azure-net.ru/drevproject/drevproject-site-frontend:production-second-<sha>
После каждого успешного push будет вызван webhook конкретного сайта.
Multi-site frontend с отключённым сайтом
image: git.azure-net.ru/drevproject/drevproject-site-frontend
context: .
dockerfile: .cicd/docker/Dockerfile
defaults:
build_args:
PUBLIC_TOKEN_COOKIE_NAME: access_token
PUBLIC_REFRESH_TOKEN_COOKIE_NAME: refresh_token
PUBLIC_LANG_COOKIE_NAME: lang
sites:
main:
image_tag: production-main
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/MAIN_PROCEDURE_ID/main
build_args:
PUBLIC_BACKEND_API_URL: https://api.example.com
PUBLIC_FRONTEND_DOMAIN: example.com
second:
disabled: true
image_tag: production-second
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/SECOND_PROCEDURE_ID/main
build_args:
PUBLIC_BACKEND_API_URL: https://api-second.example.com
PUBLIC_FRONTEND_DOMAIN: second.example.com
main будет собран и задеплоен.
second будет полностью пропущен:
Site disabled: second
Skipping build, push and webhook for site 'second'.
Поля deploy config
disabled
Тип:
disabled: true
Где можно использовать:
- в корне
staging.yaml/production.yaml; - внутри
sites.<site>.
Если disabled: true в корне файла — отключается весь env.
Если disabled: true внутри сайта — отключается только этот сайт.
image
Docker image без tag.
image: git.azure-net.ru/drevproject/drevproject-site-backend
Tag скрипт добавит сам.
context
Docker build context.
context: .
Если не указан, используется:
.
dockerfile
Путь до Dockerfile.
dockerfile: .cicd/docker/Dockerfile
Если не указан, используется:
.cicd/docker/Dockerfile
image_tag
Опциональный tag.
Для single-site, если не указан:
| Ветка | Tag |
|---|---|
staging |
staging |
master |
production |
Для multi-site, если image_tag не указан:
<environment>-<site>
Например:
production-main
staging-main
build_args
Docker build args.
build_args:
PUBLIC_BACKEND_API_URL: https://api.example.com
PUBLIC_FRONTEND_DOMAIN: example.com
Скрипт передаст их в docker build:
--build-arg PUBLIC_BACKEND_API_URL=https://api.example.com
--build-arg PUBLIC_FRONTEND_DOMAIN=example.com
defaults.build_args
Используется только для multi-site, чтобы не повторять общие build args.
defaults:
build_args:
PUBLIC_TOKEN_COOKIE_NAME: access_token
PUBLIC_REFRESH_TOKEN_COOKIE_NAME: refresh_token
PUBLIC_LANG_COOKIE_NAME: lang
Для каждого сайта итоговые build args считаются так:
defaults.build_args + sites.<site>.build_args
Если ключ есть и в defaults, и в site — значение из site перезапишет defaults.
webhook_url
Komodo webhook URL.
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/PROCEDURE_ID/main
Поддерживается alias:
komodo_webhook_url: https://deploy.azure-net.ru/listener/github/procedure/PROCEDURE_ID/main
Если webhook не указан — build и push будут выполнены, deploy не будет вызван.
test_command
Только для backend.
test_command: dotnet test --configuration Release
Если не указан — backend tests пропускаются.
Frontend checks
Для frontend перед docker build выполняется:
npm run lint
npm run check
npx azure-net check internal .
Если в package.json есть script test, дополнительно выполняется:
npm run test
Если script test отсутствует, тесты пропускаются:
No npm test script found, skipping frontend tests
Package manager detection для frontend
Скрипт сам выбирает package manager:
| Файл | Package manager |
|---|---|
pnpm-lock.yaml |
pnpm |
yarn.lock |
yarn |
| иначе | npm |
Для pnpm:
pnpm install --frozen-lockfile
Для yarn:
yarn install --frozen-lockfile
Для npm:
npm ci
если есть package-lock.json, иначе:
npm install
Dockerfile location
Рекомендуется хранить Dockerfile здесь:
.cicd/docker/Dockerfile
Пример backend:
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY *.csproj ./
RUN dotnet restore
COPY . ./
RUN dotnet publish -c Release -o /app/publish --no-restore
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
WORKDIR /app
EXPOSE 5000
ENV ASPNETCORE_URLS=http://*:5000
COPY --from=build /app/publish .
CMD ["dotnet", "App.dll"]
Пример frontend:
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json* pnpm-lock.yaml* yarn.lock* ./
RUN corepack enable
RUN if [ -f pnpm-lock.yaml ]; then pnpm install --frozen-lockfile; \
elif [ -f yarn.lock ]; then yarn install --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
else npm install; fi
FROM node:22-alpine AS build
WORKDIR /app
ARG PUBLIC_BACKEND_API_URL
ARG PUBLIC_FRONTEND_DOMAIN
ARG PUBLIC_TOKEN_COOKIE_NAME
ARG PUBLIC_REFRESH_TOKEN_COOKIE_NAME
ARG PUBLIC_LANG_COOKIE_NAME
ENV PUBLIC_BACKEND_API_URL=$PUBLIC_BACKEND_API_URL
ENV PUBLIC_FRONTEND_DOMAIN=$PUBLIC_FRONTEND_DOMAIN
ENV PUBLIC_TOKEN_COOKIE_NAME=$PUBLIC_TOKEN_COOKIE_NAME
ENV PUBLIC_REFRESH_TOKEN_COOKIE_NAME=$PUBLIC_REFRESH_TOKEN_COOKIE_NAME
ENV PUBLIC_LANG_COOKIE_NAME=$PUBLIC_LANG_COOKIE_NAME
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN corepack enable
RUN npm run build
FROM node:22-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/build ./build
COPY --from=build /app/package.json ./package.json
COPY --from=build /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "build"]
Komodo usage
Рекомендуемый вариант:
Forgejo Actions:
1. checkout
2. docker login
3. docker build
4. docker push image:production
5. docker push image:production-<sha>
6. call Komodo procedure webhook
Komodo Procedure:
1. redeploy deployment / stack
Для Deployment в Komodo удобно делать Procedure, потому что у Deployment может не быть прямого webhook.
В webhook_url кладётся webhook процедуры:
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/PROCEDURE_ID/main
Примеры минимальных конфигов
Backend production
image: git.azure-net.ru/example/example-backend
context: .
dockerfile: .cicd/docker/Dockerfile
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/BACKEND_PRODUCTION_PROCEDURE_ID/main
Backend staging disabled
disabled: true
image: git.azure-net.ru/example/example-backend
context: .
dockerfile: .cicd/docker/Dockerfile
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/BACKEND_STAGING_PROCEDURE_ID/main
Frontend production single-site
image: git.azure-net.ru/example/example-frontend
context: .
dockerfile: .cicd/docker/Dockerfile
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/FRONTEND_PRODUCTION_PROCEDURE_ID/main
build_args:
PUBLIC_BACKEND_API_URL: https://api.example.com
PUBLIC_FRONTEND_DOMAIN: example.com
PUBLIC_TOKEN_COOKIE_NAME: access_token
PUBLIC_REFRESH_TOKEN_COOKIE_NAME: refresh_token
PUBLIC_LANG_COOKIE_NAME: lang
Frontend production multi-site
image: git.azure-net.ru/example/example-frontend
context: .
dockerfile: .cicd/docker/Dockerfile
defaults:
build_args:
PUBLIC_TOKEN_COOKIE_NAME: access_token
PUBLIC_REFRESH_TOKEN_COOKIE_NAME: refresh_token
PUBLIC_LANG_COOKIE_NAME: lang
sites:
main:
image_tag: production-main
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/MAIN_PROCEDURE_ID/main
build_args:
PUBLIC_BACKEND_API_URL: https://api.example.com
PUBLIC_FRONTEND_DOMAIN: example.com
admin:
image_tag: production-admin
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/ADMIN_PROCEDURE_ID/main
build_args:
PUBLIC_BACKEND_API_URL: https://api.example.com
PUBLIC_FRONTEND_DOMAIN: admin.example.com
Checklist для нового backend проекта
- Добавить workflow:
.forgejo/workflows/backend.yml
- Добавить Dockerfile:
.cicd/docker/Dockerfile
- Добавить production config:
.cicd/deploy/production.yaml
- Добавить staging config, если нужен:
.cicd/deploy/staging.yaml
- Добавить secrets в Forgejo:
REGISTRY_USER
REGISTRY_PASSWORD
KOMODO_WEBHOOK_SECRET
- В Komodo deployment/stack должен использовать image с moving tag:
git.azure-net.ru/example/example-backend:production
или для staging:
git.azure-net.ru/example/example-backend:staging
Checklist для нового frontend проекта
- Добавить workflow:
.forgejo/workflows/frontend.yml
- Добавить Dockerfile:
.cicd/docker/Dockerfile
- Добавить production config:
.cicd/deploy/production.yaml
- Добавить staging config, если нужен:
.cicd/deploy/staging.yaml
- Убедиться, что в
package.jsonесть:
{
"scripts": {
"lint": "...",
"check": "..."
}
}
- Добавить secrets в Forgejo:
REGISTRY_USER
REGISTRY_PASSWORD
KOMODO_WEBHOOK_SECRET
- В Komodo deployment/stack должен использовать image с нужным moving tag.
Для single-site:
git.azure-net.ru/example/example-frontend:production
Для multi-site:
git.azure-net.ru/example/example-frontend:production-main
git.azure-net.ru/example/example-frontend:production-admin
Типовые проблемы
repository name must be lowercase
Проблема:
image: git.azure-net.ru/DrevProject/project-backend
Решение:
image: git.azure-net.ru/drevproject/project-backend
Dockerfile not found
Проверь путь:
dockerfile: .cicd/docker/Dockerfile
И наличие файла:
.cicd/docker/Dockerfile
Image is required
В конфиге нет image, и это не multi-site config.
Минимально нужно:
image: git.azure-net.ru/example/project
Webhook не вызывается
Проверь, что в конфиге есть:
webhook_url: https://deploy.azure-net.ru/listener/github/procedure/ID/main
Также проверь secret:
KOMODO_WEBHOOK_SECRET
Если в Komodo webhook настроен с secret, а в Forgejo secret пустой или неправильный, Komodo может отклонить запрос.
Frontend падает на npm run lint
Для frontend lint обязателен.
Добавь script в package.json или исправь ошибки lint.
Frontend падает на npm run check
Для frontend check обязателен.
Для SvelteKit обычно:
{
"scripts": {
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json"
}
}
Рекомендуемая структура проекта
.
├── .forgejo
│ └── workflows
│ ├── backend.yml
│ └── frontend.yml
├── .cicd
│ ├── docker
│ │ └── Dockerfile
│ └── deploy
│ ├── staging.yaml
│ └── production.yaml
├── src
├── package.json
└── ...
Для backend-проекта может быть только:
.forgejo/workflows/backend.yml
.cicd/docker/Dockerfile
.cicd/deploy/staging.yaml
.cicd/deploy/production.yaml
Для frontend-проекта может быть только:
.forgejo/workflows/frontend.yml
.cicd/docker/Dockerfile
.cicd/deploy/staging.yaml
.cicd/deploy/production.yaml
Краткая схема
push to staging
↓
.forgejo/workflows/*.yml
↓
build-docker.sh frontend/backend
↓
.cicd/deploy/staging.yaml
↓
docker build
↓
docker push :staging
docker push :staging-<sha>
↓
Komodo webhook, если указан
push to master
↓
.forgejo/workflows/*.yml
↓
build-docker.sh frontend/backend
↓
.cicd/deploy/production.yaml
↓
docker build
↓
docker push :production
docker push :production-<sha>
↓
Komodo webhook, если указан