Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/app-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ on:
- ".env.example"
- "compose/**"
- "config/versions.env"
- "Taskfile.yml"
- "scripts/check-apps.ps1"
- "scripts/check-compose.ps1"
- "scripts/check-keycloak.ps1"
- "scripts/start-local.ps1"
- "scripts/service-local.ps1"
- "scripts/stop-local.ps1"

permissions:
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ jobs:
- uses: azure/setup-helm@v5.0.1
with:
version: v4.2.4
- uses: go-task/setup-task@v2
with:
version: 3.53.1
- name: Проверить команды разработчика
run: task --list
- name: Проверить Helm chart
shell: pwsh
run: ./scripts/check-config.ps1
Expand Down
5 changes: 2 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@
## Команды

```powershell
pwsh ./scripts/check-chart.ps1
pwsh ./scripts/smoke-k3d.ps1
pwsh ./scripts/check-docs.ps1
task verify
task test:e2e
```

## Правила изменений
Expand Down
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,47 @@
- Helm 4.2.4;
- k3d 5.9.0;
- kubectl;
- Task 3.53.1;
- Windows PowerShell 5.1 или PowerShell 7.

Создай локальный файл настроек и запусти инфраструктуру:
На Windows установи Task один раз:

```powershell
winget install --id Task.Task --exact --version 3.53.1
```

Открой новый терминал и выполни `task doctor`, чтобы проверить Docker Engine и Docker Compose.

Создай локальный файл настроек и запусти только инфраструктуру без приложений:

```powershell
Copy-Item .env.example .env
./scripts/start-local.ps1 -Observe
task infra:up
```

Поднять один сервис из соседней локальной репы вместе с обязательными зависимостями:

```powershell
task service:up SERVICE=action-service
```

Чтобы поднять проверяемый путь `Channel → Agent → Action → Gateway → Calendar`, используй:
После изменения кода пересобери только нужный сервис:

```powershell
./scripts/start-local.ps1 -Apps
./scripts/check-apps.ps1
task service:restart SERVICE=action-service
```

Чтобы поднять и проверить путь `Channel → Agent → Action → Gateway → Calendar`, используй:

```powershell
task services:up
task test:e2e
```

`task status` показывает состояние, `task service:logs SERVICE=action-service` — логи. `task down`
сохраняет локальные данные. `task reset` удаляет только volumes текущего Compose project и всегда
требует явного подтверждения.

`check-apps.ps1` отправляет demo-команду в Channel Gateway, проверяет предложение, создаёт и
подтверждает действие, ждёт Temporal workflow и проверяет, что Calendar MCP сохранил ровно одно
событие с тем же `eventId`.
Expand Down Expand Up @@ -81,7 +106,7 @@ Worker, Temporal UI и Linux Compose используют внутренний
Все быстрые проверки:

```powershell
./scripts/check-config.ps1
task verify
```

Полная проверка:
Expand Down
91 changes: 91 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
version: "3"

silent: true

vars:
POWERSHELL: '{{if eq OS "windows"}}powershell.exe{{else}}pwsh{{end}}'
POWERSHELL_ARGS: '{{if eq OS "windows"}}-NoProfile -ExecutionPolicy Bypass{{else}}-NoProfile{{end}}'

tasks:
default:
desc: Показать доступные команды
cmds:
- task --list

infra:up:
desc: Поднять PostgreSQL, Redpanda, Keycloak, Temporal и OPA без приложений
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/start-local.ps1'

doctor:
desc: Проверить инструменты для локального Compose-режима
cmds:
- docker version --format "Docker Engine {{`{{.Server.Version}}`}}"
- docker compose version

infra:observe:
desc: Поднять инфраструктуру вместе с локальной наблюдаемостью
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/start-local.ps1 -Observe'

services:up:
desc: Собрать и поднять все локальные сервисы
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/start-local.ps1 -Apps'

service:up:
desc: Собрать выбранный сервис и поднять его обязательные зависимости
requires:
vars: [SERVICE]
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/start-local.ps1 -Service "{{.SERVICE}}"'

service:stop:
desc: Остановить выбранный сервис
requires:
vars: [SERVICE]
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/service-local.ps1 -Action Stop -Service "{{.SERVICE}}"'

service:restart:
desc: Пересобрать и перезапустить выбранный сервис
requires:
vars: [SERVICE]
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/service-local.ps1 -Action Restart -Service "{{.SERVICE}}"'

service:logs:
desc: Следить за логами выбранного сервиса
requires:
vars: [SERVICE]
vars:
TAIL: '{{default "100" .TAIL}}'
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/service-local.ps1 -Action Logs -Service "{{.SERVICE}}" -Tail {{.TAIL}}'

status:
desc: Показать состояние локального окружения
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/service-local.ps1 -Action Status'

test:e2e:
desc: Поднять полный срез и выполнить сквозной тест встречи
cmds:
- task: services:up
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/check-apps.ps1'

verify:
desc: Выполнить локальные проверки конфигурации и документации
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/check-config.ps1'

down:
desc: Остановить локальное окружение без удаления данных
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/stop-local.ps1'

reset:
desc: Остановить окружение и удалить только его локальные данные
prompt: Все локальные данные Portable Agent будут удалены. Продолжить?
cmds:
- '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/stop-local.ps1 -DeleteData'
31 changes: 31 additions & 0 deletions docs/decisions/0003-taskfile-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ADR 0003: единые команды локальной разработки

Статус: принято.

## Контекст

Compose требует несколько env-файлов, profiles и Windows override. Прямой запуск скриптов заставлял
разработчика помнить эти детали и привязывал основной сценарий к конкретной версии PowerShell.
Нужны одинаковые короткие команды для Windows, Linux, локального Compose и будущего k3d-контура.

## Решение

Используем Task 3.53.1 как тонкую кроссплатформенную точку входа. Taskfile не хранит бизнес-логику и
не заменяет Docker Compose, Helm или тестовые инструменты. Он вызывает короткие проверяемые скрипты.

Поддерживаются отдельные сценарии:

- инфраструктура без приложений;
- выбранный сервис с обязательными зависимостями;
- все сервисы;
- состояние, логи, перезапуск и остановка;
- полный локальный E2E;
- явный reset с подтверждением.

Версия Task закреплена в CI. Taskfile валидируется в каждом pull request.

## Последствия

Разработчик изучает один набор команд вместо параметров Compose. Существующие скрипты остаются
обратимо совместимыми, но больше не являются главным пользовательским интерфейсом. Kubernetes-команды
будут добавлены в тот же Taskfile только после появления зелёного k3d developer smoke.
24 changes: 24 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Разработка

## Обычный цикл

Taskfile — единая точка входа. Внутренние PowerShell-скрипты остаются короткой реализацией команд,
но разработчику не нужно помнить Compose-файлы, profiles и env-файлы.
После установки Task выполни `task doctor`: команда заранее проверит Docker Engine и Docker Compose.

Для работы над сервисом:

1. Выполни `task infra:up`, чтобы поднять PostgreSQL, Redpanda, Keycloak, Temporal и OPA.
2. Запусти нужный сервис командой `task service:up SERVICE=<name>` либо запусти его из IDE против
опубликованных локальных портов инфраструктуры.
3. После изменения кода выполни `task service:restart SERVICE=<name>`.
4. Посмотри состояние через `task status`, логи — через `task service:logs SERVICE=<name>`.
5. Перед pull request выполни `task test:e2e`.

Допустимые имена: `channel-gateway`, `agent-runtime`, `action-service`, `mcp-gateway` и
`calendar-mcp`. Неизвестное имя отклоняется до вызова Docker.

`task test:e2e` поднимает полный локальный срез, ждёт healthchecks и проверяет путь от текста до
сохранённого события. Следующий пакет передаст запуск black-box сценариев репозиторию `test-lab`.

`task down` не удаляет данные. `task reset` предназначен для явного пересоздания тестового состояния
и запрашивает подтверждение.

Работа идёт по TDD:

1. проверка описывает ожидаемый Kubernetes-ресурс;
Expand Down
6 changes: 3 additions & 3 deletions docs/runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@

## Приложения не запускаются

1. Выполни `./scripts/check-compose.ps1` из Windows PowerShell 5.1 или PowerShell 7.
1. Выполни `task status`, затем `task verify`.
2. Проверь, что рядом лежат репозитории `portable-agent-channel-gateway`, `portable-agent-agent-runtime`,
`portable-agent-action-service`, `portable-agent-mcp-gateway` и `portable-agent-calendar-mcp`,
либо задай их `*_CONTEXT` в `.env`.
3. Если Keycloak сообщает о старом fixture, локально выполни
`./scripts/stop-local.ps1 -DeleteData`. Команда удаляет только volumes этого Compose project.
`task reset`. Команда запрашивает подтверждение и удаляет только volumes этого Compose project.
4. Проверь `/health/live` Channel Gateway и Agent Runtime, `/actuator/health/readiness` Action Service и `/health`
двух MCP-сервисов.
5. Не включай Calendar test API вне локального профиля `apps`.

После запуска проверь весь backend-путь одной командой:

```powershell
./scripts/check-apps.ps1
task test:e2e
```

Она отправляет текст через Channel Gateway, затем создаёт действие с тем же случайным `requestKey`,
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ nav:
- Решения:
- Отдельный deploy-репозиторий: decisions/0001-deploy-repository.md
- Платформа разработки: decisions/0002-developer-platform.md
- Единые команды разработчика: decisions/0003-taskfile-workflow.md
41 changes: 41 additions & 0 deletions scripts/check-compose.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,47 @@ if ($startScript -notmatch '\[switch\]\$Apps' -or $startScript -notmatch '"apps"
if ($startScript -notmatch 'compose/apps.local.yaml' -or $startScript -notmatch 'up -d --build --wait') {
throw "Локальные приложения должны собираться из соседних репозиториев."
}
$taskfilePath = "Taskfile.yml"
if (-not (Test-Path -LiteralPath $taskfilePath)) {
throw "Нет единой точки входа Taskfile.yml для локальной разработки."
}
$taskfile = Get-Content -Raw -LiteralPath $taskfilePath
foreach ($taskName in @(
"infra:up",
"services:up",
"service:up",
"service:stop",
"service:restart",
"service:logs",
"doctor",
"status",
"test:e2e",
"down",
"reset"
)) {
if ($taskfile -notmatch "(?m)^ $([regex]::Escape($taskName)):") {
throw "В Taskfile нет команды $taskName."
}
}
if ($taskfile -notmatch 'scripts/start-local\.ps1' `
-or $taskfile -notmatch 'scripts/service-local\.ps1' `
-or $taskfile -notmatch 'scripts/check-apps\.ps1' `
-or $taskfile -notmatch '(?m)^ prompt:') {
throw "Taskfile не связывает команды запуска, управления, E2E и безопасного reset."
}
if ($startScript -notmatch '\[string\]\$Service' -or $startScript -notmatch '\$Service') {
throw "start-local должен уметь запускать один выбранный сервис."
}
$serviceScriptPath = "scripts/service-local.ps1"
if (-not (Test-Path -LiteralPath $serviceScriptPath)) {
throw "Нет безопасного управления отдельным локальным сервисом."
}
$serviceScript = Get-Content -Raw -LiteralPath $serviceScriptPath
foreach ($action in @("Status", "Stop", "Restart", "Logs")) {
if ($serviceScript -notmatch [regex]::Escape('"' + $action + '"')) {
throw "service-local не поддерживает команду $action."
}
}
$appsOverride = Get-Content -Raw -LiteralPath "compose/apps.local.yaml"
foreach ($image in @("portable-agent/channel-gateway:local", "portable-agent/agent-runtime:local", "portable-agent/action-service:local", "portable-agent/mcp-gateway:local", "portable-agent/calendar-mcp:local")) {
if ($appsOverride -notmatch [regex]::Escape($image)) {
Expand Down
3 changes: 2 additions & 1 deletion scripts/check-docs.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ $requiredFiles = @(
"docs/architecture.md",
"docs/development.md",
"docs/runbook.md",
"docs/decisions/0001-deploy-repository.md"
"docs/decisions/0001-deploy-repository.md",
"docs/decisions/0003-taskfile-workflow.md"
)

$missing = $requiredFiles | Where-Object { -not (Test-Path -LiteralPath $_ -PathType Leaf) }
Expand Down
39 changes: 39 additions & 0 deletions scripts/service-local.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
param(
[Parameter(Mandatory = $true)]
[ValidateSet("Status", "Stop", "Restart", "Logs")]
[string]$Action,
[ValidateSet("channel-gateway", "agent-runtime", "action-service", "mcp-gateway", "calendar-mcp")]
[string]$Service,
[ValidateRange(1, 10000)]
[int]$Tail = 100
)

$ErrorActionPreference = "Stop"
if ($Action -ne "Status" -and -not $Service) {
throw "Для команды $Action укажи -Service."
}

$envFiles = @("--env-file", ".env.example", "--env-file", "config/versions.env")
if (Test-Path .env) { $envFiles += @("--env-file", ".env") }
$composeFiles = @("-f", "compose/compose.yaml")
$runningOnWindows = $PSVersionTable.PSEdition -eq "Desktop" -or $IsWindows
if ($runningOnWindows) { $composeFiles += @("-f", "compose/windows.local.yaml") }
$composeFiles += @("-f", "compose/apps.local.yaml")
$profiles = @("--profile", "core", "--profile", "apps")

switch ($Action) {
"Status" {
& docker compose @envFiles @composeFiles @profiles ps
}
"Stop" {
& docker compose @envFiles @composeFiles @profiles stop $Service
}
"Restart" {
& docker compose @envFiles @composeFiles @profiles up -d --build --wait $Service
}
"Logs" {
& docker compose @envFiles @composeFiles @profiles logs --tail $Tail --follow $Service
}
}

if ($LASTEXITCODE -ne 0) { throw "Команда $Action для локального окружения завершилась с ошибкой." }
Loading
Loading