Build & Deployment¶
The platform builds to a self-contained Next.js standalone server, packaged as a Docker image and deployed to Kubernetes (AWS EKS). This page covers local development, the Protocol Buffer build, the Docker image, and the CI/CD pipelines.
Local Development¶
Environment configuration is selected with env-cmd against a per-environment .env file. Dev
runs on Turbopack.
| Script | What it does |
|---|---|
yarn dev |
Dev server on port 3000 using .env.production, Turbopack (--turbo). |
yarn dev:stg |
Dev server on port 3001 using .env.staging. |
yarn build |
Production build (next build). build:stg / build:prd select env files. |
yarn start |
Run the built server. |
yarn lint |
next lint. |
yarn verify |
next lint && next build — the pre-push gate. |
yarn proto |
Regenerate TypeScript proto clients (see below). |
yarn analyze |
Build with the bundle analyzer (ANALYZE=true). |
Pre-commit quality is enforced via Husky + lint-staged (precommit → lint-staged).
Versioning/releases use Commitizen (cz bump) and semantic-release.
Note
next.config.js sets output: "standalone" (required for the Docker image), allow-lists remote
image domains, and aliases underscore → lodash under Turbopack.
Protocol Buffers¶
The proto layer is the type contract between the platform and the backend. proto-build.js
compiles every .proto in proto/ into a TypeScript client in proto-types/.
flowchart LR
P["proto/*.proto"] -->|"yarn proto"| PB["proto-build.js
(protoc + ts-proto)"]
PB --> T["proto-types/*.ts
(nice-grpc clients + types)"]
T --> S["services / api routes
consume typed clients"]
- Driven by
protoc(localnode_modules/.bin/protoc) with the ts-proto plugin. - Output options:
outputServices=nice-grpc,outputServices=generic-definitions,lowerCaseServiceMethods=true,esModuleInterop=true. proto-types/is generated code; it is excluded fromtsconfigcompilation to avoid double-compiling.
Regenerate after proto changes
Any change to a .proto file requires re-running yarn proto and committing the regenerated
proto-types/ output, or the typed clients will drift from the contract.
Docker Image¶
The image (deploy/zephyr-web-platform/Dockerfile) is a slim Node 20 Alpine image that runs the
Next.js standalone server as a non-root user.
flowchart LR
Build["next build
(output: standalone)"] --> Art[".next/standalone + .next/static + public"]
Art --> Img["node:20-alpine image
user: nextjs, port 3000"]
Img --> Run["CMD node server.js"]
- Base:
node:20-alpine,NODE_ENV=production. - Copies
public/,.next/standalone, and.next/staticinto the image. - Runs as the non-root
nextjsuser, exposes port 3000, starts withnode server.js.
CI/CD¶
There are two pipeline systems in the repo; the active path is GitHub Actions, with a Bitbucket pipeline retained for build/lint.
flowchart TD
PR[Pull Request] --> CI["ci.yml: yarn lint + yarn build"]
Stg[merge → release/stage] --> SB["stage.yml: build image → push → deploy staging"]
Prod[merge → main] --> PB["prod.yaml: build image → push → deploy production"]
SB --> K8s[(Kubernetes / EKS)]
PB --> K8s
| Workflow | Trigger | Action |
|---|---|---|
.github/workflows/ci.yml |
Pull request | yarn lint + yarn build. |
.github/workflows/stage.yml |
Merge to release/stage |
Build image, push to registry, deploy to staging. |
.github/workflows/prod.yaml |
Merge to main |
Build image, push to registry, deploy to production. |
.github/workflows/test.yml |
Manual | Run the test suite. |
The Bitbucket pipeline (bitbucket-pipelines.yml) runs PR build + lint on a node:20 image with
.next/cache and node_modules caching.
Deployment Topology¶
- Runs in the
grepsr-appnamespace on Kubernetes (AWS EKS). - Reaches backend services via in-cluster gRPC at
{service}.{env}.svc.grepsr.net:443. - See Integration Points for transport/TLS details and the High Level Architecture for the broader platform context.
Release Workflow¶
The Makefile wraps the release flow:
| Target | Action |
|---|---|
make sync |
Pull origin release/stage and fetch tags. |
make bump |
Commitizen version bump. |
make push |
Push commits and tags to the remote. |