Skip to content

refactor: hexagonal architecture, DDD domain layer, CD service rename, CI tests#90

Open
ronenhamias wants to merge 10 commits into
developfrom
refactor/remove-dead-code
Open

refactor: hexagonal architecture, DDD domain layer, CD service rename, CI tests#90
ronenhamias wants to merge 10 commits into
developfrom
refactor/remove-dead-code

Conversation

@ronenhamias

@ronenhamias ronenhamias commented Jun 5, 2026

Copy link
Copy Markdown
Member

Summary

Complete architectural overhaul of Robokit from a flat, monolithic layout into a hexagonal (ports & adapters) architecture with a clean domain-driven design layer. Alongside the structural refactor, the "environment service" concept is consistently renamed to Continuous Deployment (CD) service throughout the codebase.


What changed

Architecture — Hexagonal / DDD

The entire app/ directory is replaced by src/ with strict layering:

src/
  domain/          ← pure business logic, zero I/O
  application/     ← use cases, orchestration
  adapters/        ← GitHub, CD service, Vault
  templates/       ← markdown renderer
  config.js        ← single validated config object
  logger.js        ← structured pino logger
  server.js        ← entry point
  webhook.js       ← Probot wiring

Deleted (dead / replaced): app/cache.js, app/config.js, app/environments.js, app/github-service.js, app/http-gateway.js, app/utils.js, app/vault-api.js, app/statuses/templates.js, index.js, robokit.js


Domain layer (src/domain/)

File What it does
PipelineStatus.js Single source of truth for all CD-status → GitHub status/conclusion/marker mappings. Fixes the silent CANCELLED vs CANCELED typo bug.
DeploymentContext.js Immutable value object (Object.freeze). All deploy state in one place; withCheckRunName() / withNamespace() return new instances.
DeploymentPolicy.js Pure functions: evaluate(checkRun) decides trigger type; evaluateUserAction(action) maps GitHub button clicks. Zero I/O.

Application layer (src/application/)

File What it does
DeploymentOrchestrator.js Replaces coordinator.js. Drives the full deploy pipeline: build immutable context → acquire lock → create check run → stream CD service → update GitHub on every status change → finalize. Uses local state object instead of mutable shared object.
deploymentLock.js Per-owner/repo:branch concurrency guard. A second deploy for the same key while one is in-flight is skipped with a warning rather than running in parallel.

Adapters (src/adapters/)

Adapter Notes
cd-service/CdServiceClient.js Renamed from environments.js. Fixed WebSocket double-connect race with #connecting private field (reuses in-flight promise). Uses rxjs directly (Subject) — removes rxjs-compat.
cd-service/CdServiceMock.js Extracted mock for LOCAL_DEV=true. Emits RUNNING×2 then SUCCEEDED; no real WS needed.
github/CheckRunAdapter.js Replaces check-run.js. Uses PipelineStatus — fixes the CANCELLED conclusion bug. Removed obsolete ant-man-preview / flash-preview headers.
github/DeploymentAdapter.js Replaces deployment.js. Same cleanup.
github/OctokitRegistry.js Replaces cache.js. Typed register/get with warn-on-miss.
vault/VaultAdapter.js Renamed from vault-api.js.

Infrastructure

  • src/config.js — validates all required env vars at boot (APP_ID, PRIVATE_KEY, WEBHOOK_SECRET), exits with a clear message on missing values. Groups all config in one object.
  • src/logger.js — pino structured logger; child loggers carry deployId, repo, branch, sha correlation fields across the entire pipeline.
  • src/server.js — selects real vs mock CD service based on LOCAL_DEV. Adds GET /health endpoint ({ status, uptime, cdService }).
  • src/templates/renderer.js — safe string interpolation (only substitutes string | number values). Fixed "Continues Delivery" → "Continuous Delivery" typo in templates.
  • package.json — added pino, removed rxjs-compat. Updated scripts: start → Probot, robokit → direct node for IntelliJ.
  • Dockerfile — updated COPY paths for new src/ layout.

Tests

  • test/unit/domain/PipelineStatus.test.js — 20 tests covering all status mappings, markers, isTerminal, unknown fallbacks, and the CANCELED/CANCELLED typo guard.
  • test/unit/domain/DeploymentContext.test.js — 6 tests: field assignment, immutability, withCheckRunName, withNamespace, chaining.
  • test/unit/domain/DeploymentPolicy.test.js — 14 tests: all evaluate() branches and all evaluateUserAction() cases.
  • test/unit/application/deploymentLock.test.js — 6 tests: basic run, skip duplicate, release after completion, release after error, key isolation, isLocked state.
  • test/e2e.test.js — added GET /health test; cleaned up stale commented-out tests.

CI

.github/workflows/publish-docker.yml — added a test job that runs npm test before the Docker build. The build job now has needs: test, so a failing test blocks the image publish.


.env cleanup

Removed unused variables (GITHUB_INSTALLATION_ID, GITHUB_OWNER, GITHUB_REPO, GITHUB_SHA). Regrouped remaining variables into four labelled sections: Server, GitHub App, Continuous Deployment Service, Vault.


Test plan

  • npm test passes (all unit tests green)
  • CI test job runs before build on push to develop
  • LOCAL_DEV=true npm run robokit starts without errors; /health returns { status: 'ok' }
  • Sending a valid check_run webhook (smee.io) triggers the mock CD pipeline and completes the GitHub check run
  • A second simultaneous deploy for the same branch logs deploy.skipped.already-in-progress and returns without error

ronenhamias and others added 8 commits June 5, 2026 14:30
- Remove unused imports, smee() function, and empty event handler from index.js
- Remove 6 unused static methods from utils.js (isPullRequest, time, mapToChecks, format, getPrgress, toPrgress)
- Remove unused labels() and content() methods from github-service.js
- Remove unused sendResponse() from http-gateway.js
- Simplify toDeployRequest() in environments.js by removing empty release branches
- Remove no-op env() wrapper from statuses/templates.js
- Remove broken build/watch scripts and unused deps (lodash, body-parser, smee-client, babel stack) from package.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When LOCAL_DEV=true in .env:
- robokit.js skips Vault k8s login and starts the Probot server
  directly using credentials already present in .env
- environments.js skips the WebSocket connection to the env service
  and returns a mock observable that emits RUNNING -> SUCCEEDED events
  so the full GitHub check-run flow can be exercised locally

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… LOCAL_DEV mode

- Replace God-class http-gateway with focused domain modules: coordinator,
  context, check-run, deployment, environment
- Remove all pull_request and feature-branch deploy label code
- Add LOCAL_DEV mode: skips Vault auth and env-service, uses mock deploy
- Add e2e test suite (test/e2e.test.js) and fakewebhook.ps1 for manual testing
- Fix bugs: duration calculation (getSeconds), vault-api constructor, connectFailed promise
- Remove new Promise antipatterns; flatten all async flows to async/await
- Remove unused statuses (waiting, running), config fields, and dead env vars
- Add ROBOKIT_RELEASE_CHECK constant; single-expression shouldTrigger
- Simplify cache and templates (remove unnecessary classes)
- Write comprehensive README with setup, config reference, and trigger docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename environment service → CdServiceClient (cd-service/) to reflect
  what it actually does: continuous deployment
- Extract domain layer: PipelineStatus, DeploymentPolicy, DeploymentContext
- Extract application layer: DeploymentOrchestrator, deploymentLock
- Extract adapters: CheckRunAdapter, DeploymentAdapter, OctokitRegistry
- Add structured JSON logging via pino (replaces console.log with tags)
- Add /health endpoint for Kubernetes liveness probes
- Add per-repo deployment concurrency lock (prevents double-deploy on
  rapid pushes to same branch)
- Fix CANCELLED/CANCELED typo in status mapping (dead code in marker())
- Fix WebSocket double-connect race condition in CdServiceClient
- Remove GitHub API preview headers (ant-man, flash — now stable)
- Validate required env vars at boot, exit with clear error if missing
- Remove mystery trigger-deploy file
- Remove rxjs-compat, use rxjs Subject directly
- Move templates to src/templates/, fix "Continues" → "Continuous" typo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PipelineStatus: all env→GitHub mappings, marker symbols, isTerminal,
  unknown-status fallbacks, and a guard against the CANCELED/CANCELLED typo
- DeploymentContext: field assignment, Object.freeze, withCheckRunName,
  withNamespace, chaining
- DeploymentPolicy: CI trigger (deployable/non-deployable branch, release
  bypass, wrong conclusion), release trigger, own-check-run guard,
  user action evaluation (deploy_now, cancel, null/unknown)
- deploymentLock: concurrent key isolation, skips duplicate, releases on
  completion and on error, isLocked reflects live state
- Delete dead test/index.test.js (referenced deleted index.js)
- Add /health check to e2e suite

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
npm test must pass before the image is built or the deploy trigger fires.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Superseded by the e2e test suite (test/e2e.test.js) which sends signed
webhooks programmatically via npm run test:e2e.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
trigger-deploy removed — manual deploy triggers replaced by the Re-Deploy
button on the Robokit CD check run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ronenhamias ronenhamias changed the title Refactor/remove dead code refactor: hexagonal architecture, DDD domain layer, CD service rename, CI tests Jun 5, 2026
ronenhamias and others added 2 commits June 5, 2026 21:07
- Replace old app/ flat layout with new src/ hexagonal tree
- Remove deleted fakewebhook.ps1 section
- Clean up .env example (remove removed E2E-only variables)
- Add GET /health endpoint documentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant