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.
- 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.