Stage 2: Build & Deployment
Overview
The build and deployment stage runs only after tests pass and only on push to master/main. It is skipped for pull requests.
Duration: 2-5 minutes
Runs on: Push to master/main only (after tests
pass)
Deployment Flow
to master/main"] C{"Passed?"} D["Setup Node.js 20"] E["Install dependencies"] F["Build app
npm run build"] G["Verify build"] H["Build Docker image"] I["Push to registry"] J["Deploy to production"] K["Verify deployment"] A --> B B --> C C -->|No (PR)| L["Skip deployment"] C -->|Yes| D D --> E E --> F F --> G G --> H H --> I I --> J J --> K K --> M["Done"] style M fill:#c8e6c9 style L fill:#ffccbc
Build Job Steps
1. Checkout Repository
Clones repository and sets up git context.
2. Setup Node.js & Cache
Uses Node.js v20 with npm caching.
3. Install Dependencies
- run: npm ciClean install from package-lock.json.
4. Build Production Bundle
- name: Build production bundle
run: |
if [ -n "${{ secrets.FRONTEND_API_BASE_URL }}" ]; then
export VITE_API_BASE_URL="${{ secrets.FRONTEND_API_BASE_URL }}"
fi
npm run buildBuild Process:
- TypeScript compilation
- Bundling and minification
- Asset optimization
- Tree shaking
- Code splitting
Output: dist/ directory
5. Verify Production Build
- name: Verify production build
run: bash scripts/verify-production-build.shVerification Checks:
- β dist/ directory exists
- β No test files (.test.)
- β No source maps in production
- β No docs/ directory
- β index.html is present
- β No console.logs or debuggers
6. Build Docker Image
- name: Build Docker image
run: docker build \
--build-arg VITE_API_BASE_URL="${{ secrets.FRONTEND_API_BASE_URL }}" \
-t stockease-frontend:latest .Multi-stage Build:
Stage 1: Builder - Node.js 18 Alpine
- Install dependencies
- Copy source files
- Run build
- Output: dist/
Stage 2: Production - nginx Alpine
- Copy dist/ from builder
- Configure nginx
- Expose port 80
- Final image: ~45MB
7. Additional Steps
- Push to Docker Registry
- Deploy to cloud platform
- Run smoke tests
- Notify deployment status
Build Output
Artifact Structure
dist/
βββ index.html # Entry point
βββ assets/
β βββ index-ABC123DEF456.js # Bundled JavaScript (minified)
β βββ index-XYZ789UVW012.css # Compiled CSS (minified)
β βββ vendor-ABC123DEF456.js # Vendor code (React, etc.)
β βββ images/ # Optimized images
βββ public/
βββ (copied public assets)
Build Statistics
Metrics:
ββ Bundle Size: ~250KB gzipped
ββ JavaScript: ~180KB gzipped
ββ CSS: ~40KB gzipped
ββ Images: ~30KB gzipped
ββ Build Time: 30-60 seconds
ββ Chunk Count: 3-5 optimal chunks
Docker Build Process
Build Command
docker build \
--build-arg VITE_API_BASE_URL=https://api.stockease.com/api \
-t stockease-frontend:latest .Dockerfile Multi-Stage Build
Stage 1: Builder (Node.js 18 Alpine)
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run buildStage 2: Production (nginx Alpine)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY ops/nginx/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Image Verification
# List image layers
docker history stockease-frontend:latest
# Check image size
docker images stockease-frontend
# Run and test
docker run -p 8080:80 stockease-frontend:latest
curl http://localhost:8080Deployment Strategy
Zero-Downtime Deployment
version: '3'
services:
frontend:
image: stockease-frontend:${BUILD_TAG}
ports:
- "80:80"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/index.html"]
interval: 10s
timeout: 5s
retries: 3
restart: alwaysHealth Checks:
- Verify container starts
- Check HTTP 200 on index.html
- Automatic restart on failure
- Graceful rollback if unhealthy
Deployment Process
Build Docker image
β
Push to registry
β
Pull on production server
β
Stop old container
β
Start new container
β
Health checks
β
Success β Done
Failure β Rollback to old version
Local Testing
Build Locally
# Build production bundle
npm run build
# Verify dist directory
ls -la dist/
# Check for test files (should be empty)
find dist -name "*.test.*" Test Docker Locally
# Build Docker image
docker build -f Dockerfile -t stockease:test .
# Run container
docker run -p 8080:80 stockease:test
# Test in browser
curl http://localhost:8080Environment Configuration
Build uses environment variables from GitHub Secrets:
| Secret | Usage |
|---|---|
| FRONTEND_API_BASE_URL | Injected as VITE_API_BASE_URL |
| DOCKER_USERNAME | Docker registry authentication |
| DOCKER_PASSWORD | Docker registry token |
See Secrets Configuration for setup details.
Conditional Deployment
Deployment only runs if:
- β Event is push (not pull_request)
- β Branch is master or main
- β Previous tests passed
- β Build completed successfully
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main')Related Documentation
- Main Pipeline Overview
- Testing Stage
- Secrets Configuration
- Troubleshooting
- Docker & Containerization
Last Updated: November 2025
Build Tool: Vite 6.0.5
Docker Base: nginx Alpine
Image Size: ~45MB