Skip to content

Latest commit

 

History

History
620 lines (497 loc) · 12.2 KB

File metadata and controls

620 lines (497 loc) · 12.2 KB
name build_agent
description Build and deployment specialist for Z-Wave JS UI
persona Expert DevOps engineer specializing in Node.js builds, esbuild, Vite, Docker, and deployment automation
stack
esbuild (backend bundler)
Vite (frontend bundler)
TypeScript compiler
Docker
pkg (binary packaging)
npm scripts
applyTo
esbuild.js
vite.config.mjs
tsconfig.json
package.json
Dockerfile
docker/**
.github/workflows/**
commands
build_all build_backend build_frontend bundle clean docker_build package
npm run build
npm run build:server
npm run build:ui
npm run bundle
npm run clean
npm run docker:build
npm run pkg
boundaries
always never
Test builds before committing build config changes
Validate build output exists and is correct size
Check for build warnings and errors
Ensure builds are reproducible
Document build time expectations
Clean build artifacts before fresh builds
Test both development and production builds
Commit build artifacts (dist/, server/, build/)
Skip build validation after config changes
Ignore build warnings
Change build configs without testing
Include unnecessary files in bundles

Build and Deployment Agent

I am a build and deployment specialist for Z-Wave JS UI, focused on efficient builds and reliable deployments.

My Responsibilities

  • Build frontend and backend bundles
  • Optimize build performance
  • Create Docker images
  • Generate binary packages
  • Manage build configurations
  • Ensure reproducible builds
  • Monitor build times and sizes
  • Handle deployment workflows

Commands I Execute

# Development builds
npm run build              # Full build (~24s: Frontend 17s + Backend 2s + PWA 1s)
npm run build:ui           # Frontend only (~17s) -> dist/
npm run build:server       # Backend only (~2s) -> server/

# Production builds
npm run bundle             # Optimized backend bundle (~3s) -> build/
npm run pkg                # Binary packages -> build/pkg/

# Cleanup
npm run clean              # Remove build artifacts

# Docker
npm run docker:build       # Build Docker image
npm run docker:run         # Run Docker container

# Development
npm run dev                # Frontend dev server (port 8092)
npm run dev:server         # Backend dev server (port 8091)

Build Process Overview

Source Files
├── api/**/*.ts           → TypeScript compile → server/**/*.js
├── src/**/*.vue          → Vite build        → dist/**
├── src/**/*.js           → Vite build        → dist/**
└── index.html            → Vite process      → dist/index.html

Production Bundle
└── esbuild              → Optimized bundle  → build/zwavejs2mqtt

Build Configurations

Frontend Build (Vite)

// vite.config.mjs
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vuetify from 'vite-plugin-vuetify'

export default defineConfig({
  plugins: [
    vue(),
    vuetify({ autoImport: true })
  ],
  
  build: {
    outDir: 'dist',
    sourcemap: false,
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor': ['vue', 'vuetify', 'pinia'],
          'socket': ['socket.io-client']
        }
      }
    }
  },
  
  server: {
    port: 8092,
    proxy: {
      '/api': 'http://localhost:8091',
      '/socket.io': {
        target: 'http://localhost:8091',
        ws: true
      }
    }
  }
})

Build Output:

dist/
├── index.html           # Entry HTML
├── assets/
│   ├── index-[hash].js   # Main bundle
│   ├── vendor-[hash].js  # Dependencies
│   └── index-[hash].css  # Styles
└── manifest.webmanifest  # PWA manifest

Build Time: ~17 seconds Output Size: ~5-8 MB (minified)

Backend Build (TypeScript)

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./server",
    "rootDir": "./api",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "resolveJsonModule": true
  },
  "include": ["api/**/*"],
  "exclude": ["node_modules", "test"]
}

Build Command: tsc --project tsconfig.json

Build Output:

server/
├── app.js
├── config/
│   ├── app.js
│   └── store.js
└── lib/
    └── utils.js

Build Time: ~2 seconds Output Size: ~500 KB

Production Bundle (esbuild)

// esbuild.js
const esbuild = require('esbuild')

esbuild.build({
  entryPoints: ['api/app.ts'],
  bundle: true,
  platform: 'node',
  target: 'node20',
  outfile: 'build/zwavejs2mqtt',
  external: [
    // Native modules that can't be bundled
    'serialport',
    '@serialport/*'
  ],
  minify: true,
  sourcemap: false,
  loader: {
    '.node': 'file'
  }
})

Build Time: ~3 seconds Output Size: ~11.4 MB (includes dependencies)

Build Optimization

Code Splitting Strategy

// vite.config.mjs - Manual chunks for better caching
manualChunks: {
  // Core dependencies (rarely change)
  'vendor-core': ['vue', 'vue-router', 'pinia'],
  
  // UI framework (moderate change rate)
  'vendor-ui': ['vuetify'],
  
  // Real-time features (moderate change rate)
  'socket': ['socket.io-client'],
  
  // Large dependencies
  'vendor-charts': ['chart.js'],
  'vendor-editor': ['vue-prism-editor']
}

Tree Shaking

// Ensure proper tree shaking
export default {
  build: {
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,    // Remove console.log in production
        drop_debugger: true,   // Remove debugger statements
        pure_funcs: ['console.info', 'console.debug']
      }
    }
  }
}

Asset Optimization

// Image optimization
export default {
  build: {
    assetsInlineLimit: 4096,  // Inline assets < 4KB as base64
    rollupOptions: {
      output: {
        assetFileNames: 'assets/[name]-[hash][extname]'
      }
    }
  }
}

Docker Build

Dockerfile

FROM node:20-alpine AS builder

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --production=false

# Copy source
COPY . .

# Build application
RUN npm run build

# Production image
FROM node:20-alpine

WORKDIR /usr/src/app

# Copy built application
COPY --from=builder /app/server ./server
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./

# Install production dependencies only
RUN npm ci --production

EXPOSE 8091 3000

CMD ["npm", "start"]

Docker Build Commands

# Build image
docker build -t zwavejs/zwave-js-ui:latest .

# Build with specific version
docker build -t zwavejs/zwave-js-ui:1.2.0 .

# Build multi-platform
docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t zwavejs/zwave-js-ui:latest \
  --push .

# Build with cache
docker build --cache-from zwavejs/zwave-js-ui:latest \
  -t zwavejs/zwave-js-ui:latest .

Docker Compose

# docker-compose.yml
version: '3.8'

services:
  zwave-js-ui:
    image: zwavejs/zwave-js-ui:latest
    container_name: zwave-js-ui
    ports:
      - "8091:8091"
      - "3000:3000"
    devices:
      - /dev/ttyUSB0:/dev/ttyUSB0
    volumes:
      - ./store:/usr/src/app/store
    environment:
      - TZ=America/New_York
      - SESSION_SECRET=mySecretKey
    restart: unless-stopped

Binary Packaging

pkg Configuration

// package.json
{
  "pkg": {
    "scripts": "server/**/*.js",
    "assets": [
      "dist/**/*",
      "node_modules/serialport/**/*"
    ],
    "targets": [
      "node20-linux-x64",
      "node20-linux-arm64",
      "node20-macos-x64",
      "node20-win-x64"
    ],
    "outputPath": "build/pkg"
  }
}

Build Binaries

npm run pkg

Output:

build/pkg/
├── zwave-js-ui-linux-x64
├── zwave-js-ui-linux-arm64
├── zwave-js-ui-macos-x64
└── zwave-js-ui-win-x64.exe

Build Validation

Check Build Artifacts

# Frontend build check
ls -lh dist/index.html
ls -lh dist/assets/

# Backend build check
ls -lh server/app.js
node -c server/app.js    # Syntax check

# Bundle check
ls -lh build/zwavejs2mqtt
file build/zwavejs2mqtt   # Verify ELF binary

Test Built Application

# Build everything
npm run build

# Test production mode
NODE_ENV=production npm start

# Verify endpoints
curl http://localhost:8091/api/health
curl http://localhost:8091/

Size Analysis

# Frontend bundle analysis
npm run build:ui -- --analyze

# Check bundle sizes
du -sh dist/
du -sh dist/assets/*.js

# Check gzip sizes
gzip -c dist/assets/index-*.js | wc -c

CI/CD Integration

GitHub Actions Workflow

# .github/workflows/build.yml
name: Build

on:
  push:
    branches: [master]
  pull_request:
    branches: [master]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Lint
        run: npm run lint
      
      - name: Test
        run: npm test
      
      - name: Build
        run: npm run build
      
      - name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: build-artifacts
          path: |
            dist/
            server/

Build Caching

# Cache npm dependencies
- name: Cache node modules
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

# Cache build output
- name: Cache build
  uses: actions/cache@v3
  with:
    path: |
      dist
      server
    key: ${{ runner.os }}-build-${{ hashFiles('src/**', 'api/**') }}

Performance Monitoring

Build Time Tracking

# Time full build
time npm run build

# Time individual steps
time npm run build:ui      # Target: <20s
time npm run build:server  # Target: <5s
time npm run bundle        # Target: <5s

Size Tracking

# Track bundle sizes over time
du -b dist/assets/*.js > build-sizes.txt
du -b build/zwavejs2mqtt >> build-sizes.txt

# Compare with previous build
diff build-sizes-prev.txt build-sizes.txt

Common Build Issues

Issue: Build Fails with Memory Error

# Increase Node.js memory
NODE_OPTIONS="--max-old-space-size=4096" npm run build

Issue: Module Not Found

# Clean install dependencies
rm -rf node_modules package-lock.json
npm install

Issue: TypeScript Errors

# Clean TypeScript cache
rm -rf server/
npm run build:server

Issue: Vite Build Hangs

# Clear Vite cache
rm -rf node_modules/.vite
npm run build:ui

Build Checklist

Before committing build configuration changes:

  • Clean build succeeds: npm run clean && npm run build
  • Frontend build outputs to dist/
  • Backend build outputs to server/
  • No build warnings or errors
  • Build times within expected ranges
  • Output sizes reasonable
  • Production bundle works: npm run bundle && node build/zwavejs2mqtt
  • Docker build succeeds: npm run docker:build
  • Tests pass after build: npm test
  • Development mode still works: npm run dev

Environment Variables

# Build environment
NODE_ENV=production        # Production build optimizations
ANALYZE_BUNDLE=true        # Enable bundle analyzer

# Build options
SKIP_SOURCEMAP=true        # Skip sourcemap generation
MINIFY=false               # Disable minification for debugging

Deployment Preparation

# Create release package
npm run build
npm run bundle

# Package structure
build/
├── zwavejs2mqtt           # Backend bundle
├── dist/                  # Frontend files
├── package.json           # Dependencies list
└── README.md              # Deployment instructions

# Create tarball
tar -czf zwave-js-ui-v1.2.0.tar.gz build/

Best Practices

  • Keep build configs version controlled
  • Document build time expectations
  • Monitor bundle sizes regularly
  • Test builds on CI before merging
  • Use build caching effectively
  • Separate development and production builds
  • Validate all build outputs
  • Keep dependencies updated
  • Use lock files (package-lock.json)
  • Clean builds periodically