Skip to content

Build & Deploy

Build / scripts

{
  "scripts": {
    "test":  "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon server.js",
    "dev":   "nodemon server.js"
  }
}

Both start and dev invoke nodemon. As elsewhere in the platform, production presumably overrides this via PM2 node server.js (verify with the ops owner).

Dockerfile (multi-stage, optimised)

A well-crafted multi-stage Dockerfile is committed. Highlights:

# Stage 1: Builder
FROM node:20.19.2-slim AS builder
ENV NODE_ENV=production \
    YARN_CACHE_FOLDER=/tmp/.yarn-cache \
    YARN_NETWORK_CONCURRENCY=8
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production=true --prefer-offline --link-duplicates && \
    find node_modules -type d \( -name "test*" -o -name "example*" -o -name "docs" -o -name ".git" \) -prune -exec rm -rf {} + && \
    find node_modules -type f \( -name "*.md" -o -name "*.ts" -not -path "*/typescript/*" -o -name "LICENSE*" -o -name "CHANGELOG*" -o -name "*.o" \) -delete && \
    yarn cache clean

# Stage 2: Runtime
FROM node:20.19.2-slim
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y --no-install-recommends \
    nginx libnss3 libexpat1 fontconfig && \
    apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY .env conf.js favicon.ico ./
COPY nginx.conf /etc/nginx/nginx.conf
COPY *.js ./
COPY routes/ ./routes/
COPY modules/ ./modules/
COPY helper/ ./helper/
COPY actions/ ./actions/
COPY conf/ ./conf/
RUN echo '#!/bin/bash\nnginx -g "daemon off;" &\nnode server.js' > /entrypoint.sh && \
    chmod +x /entrypoint.sh
EXPOSE 80 8080
CMD ["/entrypoint.sh"]

Notable:

  • Two-stage: builder strips test files / docs / LICENSE / CHANGELOG / .o files from node_modules to slim the runtime image
  • Runtime libs for Puppeteer: libnss3, libexpat1, fontconfig are present — needed for headless Chrome
  • nginx in same container: not a sidecar; nginx is launched alongside node via the entrypoint script. nginx + node share the container's network and file system.
  • .env is COPY'd into the image: this means the .env file must exist in the build context and is baked into the image. Anyone who pulls the image can read the secrets. This is a finding — see security.md. Better: pass .env as a runtime mount (docker run -v), or use Docker secrets / env vars on the orchestrator.

nginx.conf

In the repo. Not inspected in detail in this audit; should be audited for:

  • Reverse-proxy target (likely 127.0.0.1:5002 or similar)
  • HSTS / X-Frame-Options / Referrer-Policy / X-Content-Type-Options headers
  • Body-size limit
  • Suppressed server header

push.sh

A manual SSH-deploy script. Contents not inspected; typical pattern is something like:

ssh user@server 'cd /path/to/designer-api && git pull && pm2 restart designer-api'

No CI

  • No Jenkinsfile
  • No .github/workflows/
  • Deploys are presumably manual via push.sh or git pull + pm2 restart

This is a gap. someli-api has both Jenkins and GitHub Actions; designer-api has neither in repo.

PM2 management

No ecosystem.config.js. With 57 jobs + 6 bots + the main server, the deploy box must somehow pm2 start each one. Either:

  • The team does this manually on first deploy and PM2 saves the list via pm2 save / pm2 startup
  • There's an external ops repo with PM2 manifests
  • Everything runs inside server.js's process via the require() of the bots (which also kicks off their crons) — would mean PM2 only manages one process

Audit recommendation: commit an ecosystem.config.js. If everything is in one process, document it; if there are many processes, declare them.

Health checks

No /health endpoint. nginx may have its own health-check that just verifies the upstream responds. Verify.

Rollback

  • Docker: docker run <previous-tag> (if tags are kept)
  • Bare-metal: git checkout <previous-sha> && pm2 restart

Neither is documented in repo.

ID Recommendation Effort
D-1 Remove .env COPY from Dockerfile; use runtime mount or orchestrator env vars Small
D-2 Commit a CI workflow (.github/workflows/dev-designer-api-deploy.yml modelled after someli-api's) Small
D-3 Commit ecosystem.config.js with all processes declared Small
D-4 Add a /health endpoint Trivial
D-5 Audit nginx.conf for security headers Small
D-6 Document the rollback runbook Trivial
D-7 Tag Docker images with the git SHA so rollback is well-defined Small