|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +**GitHub Workflow Orchestrator** is a service that orchestrates GitHub Actions workflows triggered by pull request events. It receives GitHub webhook events (`pull_request`, `check_run`), matches them against configurable workflow definitions stored in PostgreSQL, and dispatches GitHub Actions workflows via the GitHub API. |
| 6 | + |
| 7 | +Key capabilities: |
| 8 | +- Configurable event filtering (JsonPath matchers, file path matchers, Dependabot matchers) |
| 9 | +- Concurrency control via a slot system to limit concurrent workflow runs |
| 10 | +- GitHub check run management (creates/updates check runs on PRs) |
| 11 | +- Comment callback system (OIDC-authenticated callbacks from dispatched workflows) |
| 12 | +- React frontend dashboard for managing workflow definitions (CRUD) |
| 13 | + |
| 14 | +## Tech Stack |
| 15 | + |
| 16 | +| Layer | Technology | |
| 17 | +|----------|----------------------------------------------------------------------------| |
| 18 | +| Backend | Kotlin 2.x, Spring Boot 3.x, JDK 21 | |
| 19 | +| Frontend | React 19, TypeScript 5.8, Vite 6, Mantine UI 7, TanStack Router + Query | |
| 20 | +| Database | PostgreSQL 16, Flyway migrations, Spring Data JDBC (`NamedParameterJdbcTemplate`) | |
| 21 | +| Scheduling | db-scheduler (persistent task queue in PostgreSQL) | |
| 22 | +| Build | Gradle 9.x (Kotlin DSL), version catalog (`libs.versions.toml`) | |
| 23 | +| Testing | Kotest (FunSpec), MockK, WireMock, Spring Boot Test (backend); Vitest (frontend) | |
| 24 | +| Auth | Auth0 java-jwt + jwks-rsa for GitHub App JWT authentication | |
| 25 | + |
| 26 | +## Project Structure |
| 27 | + |
| 28 | +``` |
| 29 | +. |
| 30 | +├── backend/ # Kotlin/Spring Boot backend |
| 31 | +│ ├── build.gradle.kts |
| 32 | +│ └── src/ |
| 33 | +│ ├── main/ |
| 34 | +│ │ ├── kotlin/pl/allegro/tech/github/botorchestrator/ |
| 35 | +│ │ │ ├── api/ # REST endpoints (webhooks, callbacks) |
| 36 | +│ │ │ ├── application/ # Event handlers (orchestration logic) |
| 37 | +│ │ │ ├── config/ # Spring configuration |
| 38 | +│ │ │ ├── domain/ # Core business logic & interfaces |
| 39 | +│ │ │ │ ├── filtering/ # PR matchers (JsonPath, FilePath, Dependabot) |
| 40 | +│ │ │ │ ├── workflows/ # Workflow definitions (CRUD, repository, config) |
| 41 | +│ │ │ │ ├── comment/ # Comment domain |
| 42 | +│ │ │ │ └── dependabot/ # Dependabot version parsing |
| 43 | +│ │ │ ├── infra/ # Infrastructure implementations |
| 44 | +│ │ │ │ ├── github/ # GitHub API client, auth (JWT, OIDC) |
| 45 | +│ │ │ │ ├── postgres/ # JDBC repositories, slots |
| 46 | +│ │ │ │ └── task/ # db-scheduler task definitions |
| 47 | +│ │ │ └── AppRunner.kt # Application entry point |
| 48 | +│ │ └── resources/ |
| 49 | +│ │ ├── application.yml |
| 50 | +│ │ └── db/migration/ # Flyway SQL migrations (V001-V006) |
| 51 | +│ ├── test/ # Unit tests |
| 52 | +│ └── integration/ # Integration tests (WireMock, Spring Boot Test) |
| 53 | +├── frontend/ # React/TypeScript frontend |
| 54 | +│ ├── package.json |
| 55 | +│ ├── vite.config.ts |
| 56 | +│ └── src/ |
| 57 | +│ ├── main.tsx # Entry point |
| 58 | +│ ├── App.tsx # Router + React Query setup |
| 59 | +│ ├── api.ts # Axios client with React Query |
| 60 | +│ ├── api-types.ts # Auto-generated from OpenAPI |
| 61 | +│ ├── routes/ # File-based routing (TanStack Router) |
| 62 | +│ └── components/ # UI components (WorkflowsTable, WorkflowForm, etc.) |
| 63 | +├── build-logic/ # Gradle convention plugins |
| 64 | +├── docs/ # Additional documentation |
| 65 | +├── compose.yml # Docker Compose (PostgreSQL for local dev) |
| 66 | +├── build.gradle.kts # Root build script |
| 67 | +├── settings.gradle.kts # Multi-module settings |
| 68 | +└── gradle/libs.versions.toml # Dependency version catalog |
| 69 | +``` |
| 70 | + |
| 71 | +## Architecture |
| 72 | + |
| 73 | +The backend follows a **layered architecture**: `api` -> `application` -> `domain` -> `infra`. |
| 74 | + |
| 75 | +**Request flow:** GitHub Webhook -> REST Endpoint (`api`) -> db-scheduler task (`infra/task`) -> Event Handler (`application`) -> Workflow Dispatcher (`domain`) -> GitHub API Client (`infra/github`) |
| 76 | + |
| 77 | +Key patterns: |
| 78 | +- **Domain interfaces** (`GithubClient`, `AvailableSlots`, `WorkflowDefinitionRepository`) with infrastructure implementations (`RestGithubClient`, `PostgresAvailableSlots`, `JdbcWorkflowDefinitionRepository`) |
| 79 | +- **Strategy pattern** for PR filtering: `CompoundPullRequestMatcher` with `ANY`/`ALL` matching strategies and pluggable matchers |
| 80 | +- **Reliable processing**: Events are scheduled as db-scheduler one-time tasks with retry, not processed synchronously |
| 81 | +- **GitHub App auth**: JWT signing with token caching and TTL-based refresh |
| 82 | + |
| 83 | +## Build & Run Commands |
| 84 | + |
| 85 | +### Backend |
| 86 | + |
| 87 | +```bash |
| 88 | +./gradlew run # Run the application (loads .env via build-logic plugin) |
| 89 | +./gradlew check # All tests (unit + integration) |
| 90 | +./gradlew test # Unit tests only |
| 91 | +./gradlew integrationTest # Integration tests only |
| 92 | +``` |
| 93 | + |
| 94 | +### Frontend |
| 95 | + |
| 96 | +```bash |
| 97 | +npm run start # Dev server on port 3000 (proxies to backend) |
| 98 | +npm run build # TypeScript check + Vite build |
| 99 | +npm run test # Vitest |
| 100 | +npm run lint # ESLint |
| 101 | +npm run generate-types # Regenerate API types from OpenAPI spec |
| 102 | +``` |
| 103 | + |
| 104 | +### Infrastructure |
| 105 | + |
| 106 | +```bash |
| 107 | +docker compose up # Start PostgreSQL 16 for local development |
| 108 | +``` |
| 109 | + |
| 110 | +## Code Conventions |
| 111 | + |
| 112 | +- **Kotlin style**: `intellij_idea` ktlint code style, 4-space indent, 160-char line width (see `.editorconfig`) |
| 113 | +- **No trailing commas** in Kotlin |
| 114 | +- **No star imports** in Kotlin (`name_count_to_use_star_import = 2147483647`) |
| 115 | +- **Test style**: Kotest `FunSpec` with `shouldBe` matchers; specs named `*Spec.kt` (unit) and `*IntSpec.kt` (integration) |
| 116 | +- **Integration tests**: Located in `backend/src/integration/`, extend `BaseIntegrationSpec`, use WireMock for GitHub API mocking |
| 117 | +- **Frontend**: ESLint + Prettier enforced via Husky pre-commit hooks (lint-staged) |
| 118 | +- **API types**: Auto-generated from OpenAPI spec via `swagger-typescript-api` -- do not edit `api-types.ts` manually |
| 119 | +- **Frontend routing**: File-based with TanStack Router (routes in `frontend/src/routes/`) |
| 120 | +- **i18n**: Translations in `frontend/public/locales/{en-US,pl}/translation.json` |
| 121 | + |
| 122 | +## Key Domain Concepts |
| 123 | + |
| 124 | +- **Workflow Definition**: A configuration that maps GitHub events to a specific GitHub Actions workflow to dispatch. Stored in PostgreSQL, managed via REST API + frontend. |
| 125 | +- **Slot**: Concurrency control unit. Each dispatched workflow occupies a slot; `max-concurrent-runs` limits how many can run simultaneously. |
| 126 | +- **Concurrency Group**: Controls how GitHub Actions handles concurrent runs of the same workflow for the same PR (see `docs/concurrency-group.md`). |
| 127 | +- **Matchers/Filters**: Rules that determine whether an incoming PR event should trigger a workflow (JsonPath, file path regex, Dependabot). |
| 128 | +- **Check Callback**: URL passed to dispatched workflows so they can report status back to the PR check. |
| 129 | +- **Comment Callback**: URL passed to dispatched workflows so they can post comments on the PR (authenticated via GitHub OIDC). |
| 130 | + |
| 131 | +## Environment Variables |
| 132 | + |
| 133 | +See `.env.example` for required configuration: |
| 134 | +- `DB_PORT` -- PostgreSQL port |
| 135 | +- `APP_BASE_URL` -- Base URL for callback URLs |
| 136 | +- GitHub App credentials (app ID, private key, installation ID) |
| 137 | + |
| 138 | +## Important Notes |
| 139 | + |
| 140 | +- The project uses **Gradle composite builds** (`build-logic/` for convention plugins) |
| 141 | +- Database migrations are in `backend/src/main/resources/db/migration/` (Flyway, `V001` through `V006`) |
| 142 | +- Local seed data: `backend/src/main/resources/db/local/V0099__local.sql` |
| 143 | +- Docker Compose is configured for Spring Boot's Docker Compose support (`spring-boot-docker-compose` dependency) |
| 144 | +- The frontend build is integrated into Gradle via the `node-gradle` plugin |
0 commit comments