From a201858fd014a7da80b6da7c7bd199d479bc22f4 Mon Sep 17 00:00:00 2001 From: "Claude Opus 4.7 (1M context)" Date: Mon, 18 May 2026 19:21:20 +0300 Subject: [PATCH 1/2] feat(deploy): switch to docker compose for v2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace imperative `docker run` deploys (images from ghcr.io) with a `docker compose` based pipeline pulling from `git.dyakov.space`: - New `deploy/compose.yaml` with `control-panel-service` and `web` services on the existing external `web` network. No DB service: an external Postgres is reached via `DB_DSN`. Container names `com_profcomff_{api,ui}_redirect[_test]` are preserved for the existing reverse-proxy configs; `redirector-api`/`redirector-www` aliases are added for parity with upstream. - Workflow now does `compose pull` → `compose run --rm control-panel-service alembic upgrade head` → `compose up -d` per environment. Testing deploys `dev-latest` on push to `main`, Production deploys `latest` on `v*` tags. Login uses `secrets.DYAKOVSPACE_CI_TOKEN` as `robot-profcomff`. - README rewritten as a service overview + deploy reference; the application source is no longer open source so the old upstream links are dropped. --- .github/workflows/deploy.yml | 224 +++++++----------- README.md | 97 +++++++- deploy/compose.yaml | 36 +++ .../2026-05-18-redirect-v2.1-deploy-design.md | 119 ++++++++++ 4 files changed, 327 insertions(+), 149 deletions(-) create mode 100644 deploy/compose.yaml create mode 100644 docs/superpowers/specs/2026-05-18-redirect-v2.1-deploy-design.md diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8472ba6..1245067 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,143 +1,72 @@ -name: Deploy docker +name: Deploy redirector on: push: branches: ['main'] - tags: - - 'v*' - + tags: ['v*'] env: - REGISTRY: ghcr.io - API_IMAGE_NAME: dyakovri/redirector-api - UI_IMAGE_NAME: dyakovri/redirector-ui - API_CONTAITER_NAME: com_profcomff_api_redirect - UI_CONTAITER_NAME: com_profcomff_ui_redirect + REGISTRY: git.dyakov.space jobs: - build-testing: - name: Build testing - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - repository: ${{ env.UI_IMAGE_NAME }} - - name: Log in to the Container registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/profcomff/redirect-ui - tags: | - type=raw,value=test,enable=true - - name: Build and push Docker image - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: | - API_ROOT=https://to.test.profcomff.com - deploy-testing: name: Deploy Testing + if: github.event_name == 'push' && github.ref == 'refs/heads/main' runs-on: [self-hosted, Linux, testing] - needs: build-testing environment: name: Testing url: https://to.test.profcomff.com/ permissions: packages: read - + env: + PROJECT_VERSION: dev-latest + PROJECT_NAME: com_profcomff_redirect_test + CONTAINER_SUFFIX: _test + DB_DSN: ${{ secrets.DB_DSN }} + JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }} + OIDC_CLIENT_SECRET: ${{ secrets.OIDC_CLIENT_SECRET }} + OIDC_TRUSTED_TOKEN: ${{ secrets.OIDC_TRUSTED_TOKEN }} + BASE_URL: ${{ vars.BASE_URL }} + OIDC_CONFIGURATION_URI: ${{ vars.OIDC_CONFIGURATION_URI }} + OIDC_CLIENT_ID: ${{ vars.OIDC_CLIENT_ID }} + OIDC_ADMIN_CLAIM: ${{ vars.OIDC_ADMIN_CLAIM }} + OIDC_ADMIN_CLAIM_VALUE: ${{ vars.OIDC_ADMIN_CLAIM_VALUE }} + ALLOWED_DOMAINS: ${{ vars.ALLOWED_DOMAINS }} steps: - - name: Pull new API - run: docker pull ${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }}:master + - name: Checkout repository + uses: actions/checkout@v4 - - name: Pull new UI - run: docker pull ${{ env.REGISTRY }}/profcomff/redirect-ui:test + - name: Log in to git.dyakov.space + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: robot-profcomff + password: ${{ secrets.DYAKOVSPACE_CI_TOKEN }} - - name: Migrate DB + - name: Pull images run: | - docker run \ - --rm \ - --network=web \ - --env DB_DSN=${{ secrets.DB_DSN }} \ - --name ${{ env.API_CONTAITER_NAME }}_migration \ - ${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }}:master \ - alembic upgrade head + docker compose \ + --project-directory deploy \ + --project-name ${{ env.PROJECT_NAME }} \ + pull - - name: Run test API + - name: Migrate DB run: | - docker stop ${{ env.API_CONTAITER_NAME }}_test || true && docker rm ${{ env.API_CONTAITER_NAME }}_test || true - docker run \ - --detach \ - --restart on-failure:3 \ - --network=web \ - --env DB_DSN=${{ secrets.DB_DSN }} \ - --name ${{ env.API_CONTAITER_NAME }}_test \ - ${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }}:master + docker compose \ + --project-directory deploy \ + --project-name ${{ env.PROJECT_NAME }} \ + run --rm control-panel-service \ + uv run --active --no-sync --directory=backend/control-panel-service alembic upgrade head - - name: Run test UI + - name: Deploy run: | - docker stop ${{ env.UI_CONTAITER_NAME }}_test || true && docker rm ${{ env.UI_CONTAITER_NAME }}_test || true - docker run \ - --detach \ - --restart on-failure:3 \ - --network=web \ - --name ${{ env.UI_CONTAITER_NAME }}_test \ - ${{ env.REGISTRY }}/profcomff/redirect-ui:test - - build-production: - name: Build production - runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - repository: ${{ env.UI_IMAGE_NAME }} - - name: Log in to the Container registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/profcomff/redirect-ui - tags: | - type=ref,event=tag,enable=${{ startsWith(github.ref, 'refs/tags/v') }} - type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }} - - name: Build and push Docker image - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: | - API_ROOT=https://to.profcomff.com + docker compose \ + --project-directory deploy \ + --project-name ${{ env.PROJECT_NAME }} \ + up --detach --remove-orphans deploy-production: name: Deploy Production - needs: - - build-production - - deploy-testing if: startsWith(github.ref, 'refs/tags/v') runs-on: [self-hosted, Linux, production] environment: @@ -145,42 +74,49 @@ jobs: url: https://to.profcomff.com/ permissions: packages: read - + env: + PROJECT_VERSION: latest + PROJECT_NAME: com_profcomff_redirect + CONTAINER_SUFFIX: "" + DB_DSN: ${{ secrets.DB_DSN }} + JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }} + OIDC_CLIENT_SECRET: ${{ secrets.OIDC_CLIENT_SECRET }} + OIDC_TRUSTED_TOKEN: ${{ secrets.OIDC_TRUSTED_TOKEN }} + BASE_URL: ${{ vars.BASE_URL }} + OIDC_CONFIGURATION_URI: ${{ vars.OIDC_CONFIGURATION_URI }} + OIDC_CLIENT_ID: ${{ vars.OIDC_CLIENT_ID }} + OIDC_ADMIN_CLAIM: ${{ vars.OIDC_ADMIN_CLAIM }} + OIDC_ADMIN_CLAIM_VALUE: ${{ vars.OIDC_ADMIN_CLAIM_VALUE }} + ALLOWED_DOMAINS: ${{ vars.ALLOWED_DOMAINS }} steps: - - name: Pull new API - run: docker pull ${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }}:master + - name: Checkout repository + uses: actions/checkout@v4 - - name: Pull new UI - run: docker pull ${{ env.REGISTRY }}/profcomff/redirect-ui:latest + - name: Log in to git.dyakov.space + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: robot-profcomff + password: ${{ secrets.DYAKOVSPACE_CI_TOKEN }} - - name: Migrate DB + - name: Pull images run: | - docker run \ - --rm \ - --network=web \ - --env DB_DSN=${{ secrets.DB_DSN }} \ - --name ${{ env.API_CONTAITER_NAME }}_migration \ - ${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }}:master \ - alembic upgrade head + docker compose \ + --project-directory deploy \ + --project-name ${{ env.PROJECT_NAME }} \ + pull - - name: Run test API + - name: Migrate DB run: | - docker stop ${{ env.API_CONTAITER_NAME }} || true && docker rm ${{ env.API_CONTAITER_NAME }} || true - docker run \ - --detach \ - --restart always \ - --network=web \ - --env DB_DSN=${{ secrets.DB_DSN }} \ - --env SECRET=${{ secrets.SECRET }} \ - --name ${{ env.API_CONTAITER_NAME }} \ - ${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }}:master + docker compose \ + --project-directory deploy \ + --project-name ${{ env.PROJECT_NAME }} \ + run --rm control-panel-service \ + uv run --active --no-sync --directory=backend/control-panel-service alembic upgrade head - - name: Run test UI + - name: Deploy run: | - docker stop ${{ env.UI_CONTAITER_NAME }} || true && docker rm ${{ env.UI_CONTAITER_NAME }} || true - docker run \ - --detach \ - --restart always \ - --network=web \ - --name ${{ env.UI_CONTAITER_NAME }} \ - ${{ env.REGISTRY }}/profcomff/redirect-ui:latest + docker compose \ + --project-directory deploy \ + --project-name ${{ env.PROJECT_NAME }} \ + up --detach --remove-orphans diff --git a/README.md b/README.md index 47c4a85..bb75c5d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,95 @@ -# Сервис для сокращения ссылок +# Сервис коротких ссылок -Превращаем страшненькие ссылки в ссылки вида https://to.profcomff.com +Превращает длинные ссылки в `https://to.profcomff.com/`. OIDC-аутентификация +через `auth.profcomff.com`, soft-delete ссылок с сохранением истории переходов, +бар-чарты переходов за час/день/всё время. -Это только деплой приложения, сам код приложения можно глянуть тут: +- Production: +- Testing: -- API https://github.com/dyakovri/redirector-api -- Webapp https://github.com/dyakovri/redirector-ui +## Стек + +- **API:** Python 3.13 + FastAPI + SQLAlchemy/alembic. Внешний Postgres (по `DB_DSN`). +- **Web:** Vite + React 19 + TypeScript + Tailwind 4 SPA (PWA). +- **Auth:** HS256 access-JWT (15 мин, in-memory у фронта) + HttpOnly refresh-cookie + (sliding 30 дней). OIDC-флоу через `/api/v2/oidc/redirect` + `/api/v2/oidc/callback`. +- **Инфра:** Docker Compose поверх существующей external-сети `web`. Reverse-proxy + на хосте. + +Исходники сервиса закрытые; в этом репо живёт только деплой-обвязка. + +## Что в репо + +``` +deploy/compose.yaml prod docker-compose (API + UI, БД внешняя) +.github/workflows/deploy.yml GitHub Actions: testing + production +``` + +Образы тянутся из `git.dyakov.space/dyakov-space/to/{control-panel-service,web}` +(приватный Gitea-registry, доступ через `secrets.DYAKOVSPACE_CI_TOKEN`). + +## Деплой + +| Триггер | Окружение | Runner | Тег образов | Project name | +|---------|-----------|--------|-------------|--------------| +| push в `main` | Testing | `[self-hosted, Linux, testing]` | `dev-latest` | `com_profcomff_redirect_test` | +| push тега `v*` | Production | `[self-hosted, Linux, production]` | `latest` | `com_profcomff_redirect` | + +Шаги одинаковые: `docker compose pull` → `docker compose run --rm +control-panel-service ... alembic upgrade head` → `docker compose up -d +--remove-orphans`. Обе среды мигрируют свою БД отдельно. + +Имена контейнеров (`com_profcomff_api_redirect[_test]`, +`com_profcomff_ui_redirect[_test]`) сохранены ради существующих reverse-proxy +конфигов. Дополнительно сервисы получают network-aliases `redirector-api` и +`redirector-www` на сети `web`. + +## Требования к окружению на раннере + +- `docker` + плагин `docker compose` v2. +- Существующая external network `web` (`docker network create web`). +- Доступ к Postgres-инстансу, который указан в `DB_DSN` для этой среды. + +## Переменные в GitHub Environments + +Задаются вручную в Settings → Environments → Testing/Production. + +**Secrets** (одинаковый набор в обоих окружениях, разные значения): + +| Имя | Назначение | +|-----|-----------| +| `DYAKOVSPACE_CI_TOKEN` | Read-токен к `git.dyakov.space` для `docker login` | +| `DB_DSN` | `postgresql://...` к внешней БД (своя на каждом окружении) | +| `JWT_SECRET_KEY` | HS256-ключ подписи наших access-токенов (`openssl rand -hex 32`) | +| `OIDC_CLIENT_SECRET` | OIDC client secret | +| `OIDC_TRUSTED_TOKEN` | Опционально. Bearer-bypass для dev. В проде оставлять пустым | + +**Vars:** + +| Имя | Пример (production) | +|-----|--------------------| +| `BASE_URL` | `https://to.profcomff.com` | +| `OIDC_CONFIGURATION_URI` | `https://auth.profcomff.com/application/o/redirector/.well-known/openid-configuration` | +| `OIDC_CLIENT_ID` | `redirector` | +| `OIDC_ADMIN_CLAIM` | `groups` | +| `OIDC_ADMIN_CLAIM_VALUE` | `redirector-admin` | +| `ALLOWED_DOMAINS` | `["https://to.profcomff.com"]` | + +## Ручной запуск на раннере + +```bash +cd /path/to/checkout +PROJECT_VERSION=latest CONTAINER_SUFFIX= \ +DB_DSN=... BASE_URL=... JWT_SECRET_KEY=... \ +OIDC_CONFIGURATION_URI=... OIDC_CLIENT_ID=... OIDC_CLIENT_SECRET=... \ +OIDC_ADMIN_CLAIM=groups OIDC_ADMIN_CLAIM_VALUE='Redirector Admin' \ +ALLOWED_DOMAINS='["https://to.profcomff.com"]' \ +docker compose --project-directory deploy --project-name com_profcomff_redirect pull + +docker compose --project-directory deploy --project-name com_profcomff_redirect \ + run --rm control-panel-service \ + uv run --active --no-sync --directory=backend/control-panel-service alembic upgrade head + +docker compose --project-directory deploy --project-name com_profcomff_redirect \ + up --detach --remove-orphans +``` diff --git a/deploy/compose.yaml b/deploy/compose.yaml new file mode 100644 index 0000000..0ce312e --- /dev/null +++ b/deploy/compose.yaml @@ -0,0 +1,36 @@ +services: + control-panel-service: + image: ${REGISTRY:-git.dyakov.space}/dyakov-space/to/control-panel-service:${PROJECT_VERSION:-latest} + container_name: com_profcomff_api_redirect${CONTAINER_SUFFIX:-} + restart: unless-stopped + networks: + web: + aliases: + - redirector-api + environment: + DB_DSN: ${DB_DSN} + BASE_URL: ${BASE_URL} + JWT_SECRET_KEY: ${JWT_SECRET_KEY} + OIDC_CONFIGURATION_URI: ${OIDC_CONFIGURATION_URI} + OIDC_CLIENT_ID: ${OIDC_CLIENT_ID} + OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET} + OIDC_ADMIN_CLAIM: ${OIDC_ADMIN_CLAIM} + OIDC_ADMIN_CLAIM_VALUE: ${OIDC_ADMIN_CLAIM_VALUE} + OIDC_TRUSTED_TOKEN: ${OIDC_TRUSTED_TOKEN:-} + ALLOWED_DOMAINS: ${ALLOWED_DOMAINS} + + web: + image: ${REGISTRY:-git.dyakov.space}/dyakov-space/to/web:${PROJECT_VERSION:-latest} + container_name: com_profcomff_ui_redirect${CONTAINER_SUFFIX:-} + restart: unless-stopped + networks: + web: + aliases: + - redirector-www + depends_on: + - control-panel-service + +networks: + web: + external: true + name: web diff --git a/docs/superpowers/specs/2026-05-18-redirect-v2.1-deploy-design.md b/docs/superpowers/specs/2026-05-18-redirect-v2.1-deploy-design.md new file mode 100644 index 0000000..78c351e --- /dev/null +++ b/docs/superpowers/specs/2026-05-18-redirect-v2.1-deploy-design.md @@ -0,0 +1,119 @@ +# Redirect v2.1 Deploy — Design + +## Goal + +Перевести деплой profcomff-сервиса `redirect` на новую сборку (v2.1): + +- Образы — из приватного Gitea-registry `git.dyakov.space/dyakov-space/to/{control-panel-service,web}` вместо `ghcr.io/{dyakovri/redirector-api, profcomff/redirect-ui}`. +- Деплой — через `docker compose` с файлом `deploy/compose.yaml` (вместо императивных `docker run`). +- Внешняя БД (уже существует), один сервис API + один сервис UI. + +## Files + +### 1. `deploy/compose.yaml` (новый) + +Два сервиса в существующей external-сети `web`. БД не описывается — внешняя. + +```yaml +services: + control-panel-service: + image: ${REGISTRY:-git.dyakov.space}/dyakov-space/to/control-panel-service:${PROJECT_VERSION:-latest} + container_name: com_profcomff_api_redirect${CONTAINER_SUFFIX:-} + restart: unless-stopped + networks: + web: + aliases: + - redirector-api + environment: + DB_DSN: ${DB_DSN} + BASE_URL: ${BASE_URL} + JWT_SECRET_KEY: ${JWT_SECRET_KEY} + OIDC_CONFIGURATION_URI: ${OIDC_CONFIGURATION_URI} + OIDC_CLIENT_ID: ${OIDC_CLIENT_ID} + OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET} + OIDC_ADMIN_CLAIM: ${OIDC_ADMIN_CLAIM} + OIDC_ADMIN_CLAIM_VALUE: ${OIDC_ADMIN_CLAIM_VALUE} + OIDC_TRUSTED_TOKEN: ${OIDC_TRUSTED_TOKEN:-} + ALLOWED_DOMAINS: ${ALLOWED_DOMAINS} + + web: + image: ${REGISTRY:-git.dyakov.space}/dyakov-space/to/web:${PROJECT_VERSION:-latest} + container_name: com_profcomff_ui_redirect${CONTAINER_SUFFIX:-} + restart: unless-stopped + networks: + web: + aliases: + - redirector-www + depends_on: + - control-panel-service + +networks: + web: + external: true + name: web +``` + +**Ключевые решения:** + +- `container_name` сохраняет старые имена (`com_profcomff_api_redirect[_test]`, `com_profcomff_ui_redirect[_test]`) — внешний nginx продолжит ходить по ним. +- Алиасы `redirector-api` / `redirector-www` добавлены для совместимости с эталоном. +- `CONTAINER_SUFFIX` — пустой для production, `_test` для testing. Прилетает из workflow. +- БД отсутствует. Подключение через готовый `DB_DSN`. +- Никакой сети `backend` — между API и UI ходит nginx через `web`. + +### 2. `.github/workflows/deploy.yml` (полный переписан) + +Два job-а, один YAML-anchor для общих шагов нельзя (GitHub Actions не поддерживает), поэтому шаги дублируются дословно. + +**Триггеры:** + +- `push: main` → `deploy-testing` (runner `[self-hosted, Linux, testing]`, `PROJECT_VERSION=dev-latest`, `CONTAINER_SUFFIX=_test`, project `com_profcomff_redirect_test`). +- `push: tags v*` → `deploy-production` (runner `[self-hosted, Linux, production]`, `PROJECT_VERSION=latest`, `CONTAINER_SUFFIX=""`, project `com_profcomff_redirect`). + +**Шаги каждого job:** + +1. `actions/checkout@v4` — чтобы `deploy/compose.yaml` оказался на раннере. +2. `docker/login-action@v3` — `registry: git.dyakov.space`, `username: robot-profcomff`, `password: ${{ secrets.DYAKOVSPACE_CI_TOKEN }}`. +3. `docker compose --project-directory deploy --project-name pull`. +4. `docker compose ... run --rm control-panel-service uv run --active --no-sync --directory=backend/control-panel-service alembic upgrade head` (обе среды, разные БД). +5. `docker compose ... up --detach --remove-orphans`. + +**Env per job:** + +| Переменная | Источник | +|------------|----------| +| `DB_DSN` | `secrets.DB_DSN` (per-env) | +| `JWT_SECRET_KEY` | `secrets.JWT_SECRET_KEY` (per-env) | +| `OIDC_CLIENT_ID` | `secrets.OIDC_CLIENT_ID` (per-env) | +| `OIDC_CLIENT_SECRET` | `secrets.OIDC_CLIENT_SECRET` (per-env) | +| `OIDC_TRUSTED_TOKEN` | `secrets.OIDC_TRUSTED_TOKEN` (per-env, опц.) | +| `BASE_URL` | `vars.BASE_URL` (per-env) | +| `OIDC_CONFIGURATION_URI` | `vars.OIDC_CONFIGURATION_URI` (per-env) | +| `OIDC_ADMIN_CLAIM` | `vars.OIDC_ADMIN_CLAIM` (per-env) | +| `OIDC_ADMIN_CLAIM_VALUE` | `vars.OIDC_ADMIN_CLAIM_VALUE` (per-env) | +| `ALLOWED_DOMAINS` | `vars.ALLOWED_DOMAINS` (per-env) | + +Эти переменные задаются на стороне GitHub Environment (`Testing` / `Production`) пользователем после первого деплоя — workflow на них только ссылается. + +### 3. `README.md` (переписан) + +Старый README ссылался на open-source репо API/UI. С v2.1 сорсы закрытые, поэтому README превращается в краткое описание сервиса + блок про деплой. Содержание (адаптировано из эталонного `CLAUDE.md`): + +- что такое сервис (короткие ссылки, OIDC-auth, soft-delete, история переходов), +- продовый URL `https://to.profcomff.com/`, тестовый `https://to.test.profcomff.com/`, +- стек одним абзацем (FastAPI/Postgres, React+Vite+Tailwind PWA), +- что лежит в этом репо: `deploy/compose.yaml` + GH Actions workflow, образы тянутся из `git.dyakov.space/dyakov-space/to/{control-panel-service,web}`, +- как триггерится деплой (push в `main` → testing, тег `v*` → production), +- список ожидаемых `secrets`/`vars` в GitHub Environments. + +## Out of scope + +- Сборка образов (это уже делается в Gitea-CI исходного репо). +- Конфигурация nginx/Caddy на хостах — отдельный процесс. +- Создание GitHub Environments / secrets / vars — ручная задача владельца. + +## Risks / Notes + +- На раннерах нужен `docker compose` v2+ (плагин). У эталона используется аналогично. +- При первом запуске `secrets.DYAKOVSPACE_CI_TOKEN` должен иметь право `read:packages` на registry; добавляется заранее владельцем. +- Если `DB_DSN` уже занят миграциями параллельного процесса — миграция может упасть, нужна разовая ручная очистка. Не блокер для дизайна. From 9133da7b9edc9565b98c6310c5777ed45e301051 Mon Sep 17 00:00:00 2001 From: Roman Dyakov Date: Mon, 18 May 2026 19:38:42 +0300 Subject: [PATCH 2/2] fix: neural --- .github/workflows/deploy.yml | 2 - deploy/compose.yaml | 3 - .../2026-05-18-redirect-v2.1-deploy-design.md | 119 ------------------ 3 files changed, 124 deletions(-) delete mode 100644 docs/superpowers/specs/2026-05-18-redirect-v2.1-deploy-design.md diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1245067..6def7db 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -25,7 +25,6 @@ jobs: DB_DSN: ${{ secrets.DB_DSN }} JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }} OIDC_CLIENT_SECRET: ${{ secrets.OIDC_CLIENT_SECRET }} - OIDC_TRUSTED_TOKEN: ${{ secrets.OIDC_TRUSTED_TOKEN }} BASE_URL: ${{ vars.BASE_URL }} OIDC_CONFIGURATION_URI: ${{ vars.OIDC_CONFIGURATION_URI }} OIDC_CLIENT_ID: ${{ vars.OIDC_CLIENT_ID }} @@ -81,7 +80,6 @@ jobs: DB_DSN: ${{ secrets.DB_DSN }} JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }} OIDC_CLIENT_SECRET: ${{ secrets.OIDC_CLIENT_SECRET }} - OIDC_TRUSTED_TOKEN: ${{ secrets.OIDC_TRUSTED_TOKEN }} BASE_URL: ${{ vars.BASE_URL }} OIDC_CONFIGURATION_URI: ${{ vars.OIDC_CONFIGURATION_URI }} OIDC_CLIENT_ID: ${{ vars.OIDC_CLIENT_ID }} diff --git a/deploy/compose.yaml b/deploy/compose.yaml index 0ce312e..13bc206 100644 --- a/deploy/compose.yaml +++ b/deploy/compose.yaml @@ -1,7 +1,6 @@ services: control-panel-service: image: ${REGISTRY:-git.dyakov.space}/dyakov-space/to/control-panel-service:${PROJECT_VERSION:-latest} - container_name: com_profcomff_api_redirect${CONTAINER_SUFFIX:-} restart: unless-stopped networks: web: @@ -16,12 +15,10 @@ services: OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET} OIDC_ADMIN_CLAIM: ${OIDC_ADMIN_CLAIM} OIDC_ADMIN_CLAIM_VALUE: ${OIDC_ADMIN_CLAIM_VALUE} - OIDC_TRUSTED_TOKEN: ${OIDC_TRUSTED_TOKEN:-} ALLOWED_DOMAINS: ${ALLOWED_DOMAINS} web: image: ${REGISTRY:-git.dyakov.space}/dyakov-space/to/web:${PROJECT_VERSION:-latest} - container_name: com_profcomff_ui_redirect${CONTAINER_SUFFIX:-} restart: unless-stopped networks: web: diff --git a/docs/superpowers/specs/2026-05-18-redirect-v2.1-deploy-design.md b/docs/superpowers/specs/2026-05-18-redirect-v2.1-deploy-design.md deleted file mode 100644 index 78c351e..0000000 --- a/docs/superpowers/specs/2026-05-18-redirect-v2.1-deploy-design.md +++ /dev/null @@ -1,119 +0,0 @@ -# Redirect v2.1 Deploy — Design - -## Goal - -Перевести деплой profcomff-сервиса `redirect` на новую сборку (v2.1): - -- Образы — из приватного Gitea-registry `git.dyakov.space/dyakov-space/to/{control-panel-service,web}` вместо `ghcr.io/{dyakovri/redirector-api, profcomff/redirect-ui}`. -- Деплой — через `docker compose` с файлом `deploy/compose.yaml` (вместо императивных `docker run`). -- Внешняя БД (уже существует), один сервис API + один сервис UI. - -## Files - -### 1. `deploy/compose.yaml` (новый) - -Два сервиса в существующей external-сети `web`. БД не описывается — внешняя. - -```yaml -services: - control-panel-service: - image: ${REGISTRY:-git.dyakov.space}/dyakov-space/to/control-panel-service:${PROJECT_VERSION:-latest} - container_name: com_profcomff_api_redirect${CONTAINER_SUFFIX:-} - restart: unless-stopped - networks: - web: - aliases: - - redirector-api - environment: - DB_DSN: ${DB_DSN} - BASE_URL: ${BASE_URL} - JWT_SECRET_KEY: ${JWT_SECRET_KEY} - OIDC_CONFIGURATION_URI: ${OIDC_CONFIGURATION_URI} - OIDC_CLIENT_ID: ${OIDC_CLIENT_ID} - OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET} - OIDC_ADMIN_CLAIM: ${OIDC_ADMIN_CLAIM} - OIDC_ADMIN_CLAIM_VALUE: ${OIDC_ADMIN_CLAIM_VALUE} - OIDC_TRUSTED_TOKEN: ${OIDC_TRUSTED_TOKEN:-} - ALLOWED_DOMAINS: ${ALLOWED_DOMAINS} - - web: - image: ${REGISTRY:-git.dyakov.space}/dyakov-space/to/web:${PROJECT_VERSION:-latest} - container_name: com_profcomff_ui_redirect${CONTAINER_SUFFIX:-} - restart: unless-stopped - networks: - web: - aliases: - - redirector-www - depends_on: - - control-panel-service - -networks: - web: - external: true - name: web -``` - -**Ключевые решения:** - -- `container_name` сохраняет старые имена (`com_profcomff_api_redirect[_test]`, `com_profcomff_ui_redirect[_test]`) — внешний nginx продолжит ходить по ним. -- Алиасы `redirector-api` / `redirector-www` добавлены для совместимости с эталоном. -- `CONTAINER_SUFFIX` — пустой для production, `_test` для testing. Прилетает из workflow. -- БД отсутствует. Подключение через готовый `DB_DSN`. -- Никакой сети `backend` — между API и UI ходит nginx через `web`. - -### 2. `.github/workflows/deploy.yml` (полный переписан) - -Два job-а, один YAML-anchor для общих шагов нельзя (GitHub Actions не поддерживает), поэтому шаги дублируются дословно. - -**Триггеры:** - -- `push: main` → `deploy-testing` (runner `[self-hosted, Linux, testing]`, `PROJECT_VERSION=dev-latest`, `CONTAINER_SUFFIX=_test`, project `com_profcomff_redirect_test`). -- `push: tags v*` → `deploy-production` (runner `[self-hosted, Linux, production]`, `PROJECT_VERSION=latest`, `CONTAINER_SUFFIX=""`, project `com_profcomff_redirect`). - -**Шаги каждого job:** - -1. `actions/checkout@v4` — чтобы `deploy/compose.yaml` оказался на раннере. -2. `docker/login-action@v3` — `registry: git.dyakov.space`, `username: robot-profcomff`, `password: ${{ secrets.DYAKOVSPACE_CI_TOKEN }}`. -3. `docker compose --project-directory deploy --project-name pull`. -4. `docker compose ... run --rm control-panel-service uv run --active --no-sync --directory=backend/control-panel-service alembic upgrade head` (обе среды, разные БД). -5. `docker compose ... up --detach --remove-orphans`. - -**Env per job:** - -| Переменная | Источник | -|------------|----------| -| `DB_DSN` | `secrets.DB_DSN` (per-env) | -| `JWT_SECRET_KEY` | `secrets.JWT_SECRET_KEY` (per-env) | -| `OIDC_CLIENT_ID` | `secrets.OIDC_CLIENT_ID` (per-env) | -| `OIDC_CLIENT_SECRET` | `secrets.OIDC_CLIENT_SECRET` (per-env) | -| `OIDC_TRUSTED_TOKEN` | `secrets.OIDC_TRUSTED_TOKEN` (per-env, опц.) | -| `BASE_URL` | `vars.BASE_URL` (per-env) | -| `OIDC_CONFIGURATION_URI` | `vars.OIDC_CONFIGURATION_URI` (per-env) | -| `OIDC_ADMIN_CLAIM` | `vars.OIDC_ADMIN_CLAIM` (per-env) | -| `OIDC_ADMIN_CLAIM_VALUE` | `vars.OIDC_ADMIN_CLAIM_VALUE` (per-env) | -| `ALLOWED_DOMAINS` | `vars.ALLOWED_DOMAINS` (per-env) | - -Эти переменные задаются на стороне GitHub Environment (`Testing` / `Production`) пользователем после первого деплоя — workflow на них только ссылается. - -### 3. `README.md` (переписан) - -Старый README ссылался на open-source репо API/UI. С v2.1 сорсы закрытые, поэтому README превращается в краткое описание сервиса + блок про деплой. Содержание (адаптировано из эталонного `CLAUDE.md`): - -- что такое сервис (короткие ссылки, OIDC-auth, soft-delete, история переходов), -- продовый URL `https://to.profcomff.com/`, тестовый `https://to.test.profcomff.com/`, -- стек одним абзацем (FastAPI/Postgres, React+Vite+Tailwind PWA), -- что лежит в этом репо: `deploy/compose.yaml` + GH Actions workflow, образы тянутся из `git.dyakov.space/dyakov-space/to/{control-panel-service,web}`, -- как триггерится деплой (push в `main` → testing, тег `v*` → production), -- список ожидаемых `secrets`/`vars` в GitHub Environments. - -## Out of scope - -- Сборка образов (это уже делается в Gitea-CI исходного репо). -- Конфигурация nginx/Caddy на хостах — отдельный процесс. -- Создание GitHub Environments / secrets / vars — ручная задача владельца. - -## Risks / Notes - -- На раннерах нужен `docker compose` v2+ (плагин). У эталона используется аналогично. -- При первом запуске `secrets.DYAKOVSPACE_CI_TOKEN` должен иметь право `read:packages` на registry; добавляется заранее владельцем. -- Если `DB_DSN` уже занят миграциями параллельного процесса — миграция может упасть, нужна разовая ручная очистка. Не блокер для дизайна.