Architecture Overview
The system follows a layered architecture:- Terraform: Provisions Digital Ocean droplet with networking and security
- Ansible: Configures server with Docker, nginx, and orchestrator service
- Orchestrator: TypeScript service handling webhooks, Docker management, nginx config, and cleanup
- CLI: User-facing tool for setup, management, and teardown
- Templates: Docker Compose and nginx config templates for preview environments
Project Structure
Implementation Notes (Lessons Learned)
- Workspace TypeScript: Package
tsconfig.jsonshould use"extends": "../tsconfig.json"(one level up from the package), not"../../tsconfig.json". - Deployment tracker I/O: Use sync
fsfor hot paths (getDeployment,getAllDeployments,allocatePorts); asyncfs/promisesfor persistence. - Dockerode types: No maintained
@types/dockerode. Use a local declaration or// @ts-ignore; document in orchestrator README. - Optional native deps:
keytarmay fail to build; keychain storage is optional; fallback to config file is acceptable for v1. - Strict TypeScript: Watch for variables used before assignment, “not all code paths return a value” in route handlers, and unused parameters (prefix with
_). - Nginx: Preview configs must be included inside a default
server { }block;locationblocks are not valid athttplevel.
Key Implementation Details
- Project slug: Derived from repo
owner/name(e.g.myorg-myapp). Used to avoid collisions when multiple repos have the same PR number. - Deployment id:
{projectSlug}-{prNumber}(e.g.myorg-myapp-12). Single key for tracker, nginx config filenames, and compose project name. - Port allocation: Global pool; next free app port from 8000, next free db port from 9000. Keyed by deployment id. Allocations are stored in the deployment store’s
portAllocationsmap and released on cleanup so ports are reused correctly. Allocation excludes host ports currently bound by running Docker containers (so failed deployments whose containers still run do not cause port collisions). - Routing: Path-based
/{projectSlug}/pr-{number}/; nginx proxies tohttp://localhost:{appPort}/. - Deployment tracking: JSON file at
/opt/preview-deployer/deployments.json; keys are deployment ids; atomic file operations.