Skip to content

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 (precommitlint-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 (local node_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 from tsconfig compilation 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/static into the image.
  • Runs as the non-root nextjs user, exposes port 3000, starts with node 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-app namespace 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.