No description
Find a file
2026-06-02 22:56:03 +03:00
scripts rm typecheck and kit check 2026-06-02 22:56:03 +03:00
.gitignore build docker 2026-06-02 16:35:09 +03:00
README.md some fixes + readme 2026-06-02 21:40:12 +03:00

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 проекта

  1. Добавить workflow:
.forgejo/workflows/backend.yml
  1. Добавить Dockerfile:
.cicd/docker/Dockerfile
  1. Добавить production config:
.cicd/deploy/production.yaml
  1. Добавить staging config, если нужен:
.cicd/deploy/staging.yaml
  1. Добавить secrets в Forgejo:
REGISTRY_USER
REGISTRY_PASSWORD
KOMODO_WEBHOOK_SECRET
  1. В 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 проекта

  1. Добавить workflow:
.forgejo/workflows/frontend.yml
  1. Добавить Dockerfile:
.cicd/docker/Dockerfile
  1. Добавить production config:
.cicd/deploy/production.yaml
  1. Добавить staging config, если нужен:
.cicd/deploy/staging.yaml
  1. Убедиться, что в package.json есть:
{
  "scripts": {
    "lint": "...",
    "check": "..."
  }
}
  1. Добавить secrets в Forgejo:
REGISTRY_USER
REGISTRY_PASSWORD
KOMODO_WEBHOOK_SECRET
  1. В 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, если указан