clearctx 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/bin/setup.js +33 -1
- package/package.json +3 -2
- package/skills/api-design/SKILL.md +796 -0
- package/skills/devops/SKILL.md +1043 -0
- package/skills/index.json +53 -0
- package/skills/nodejs-backend/SKILL.md +853 -0
- package/skills/postgresql/SKILL.md +315 -0
- package/skills/react-frontend/SKILL.md +683 -0
- package/skills/security/SKILL.md +1000 -0
- package/skills/testing-qa/SKILL.md +842 -0
- package/skills/typescript/SKILL.md +932 -0
- package/src/mcp-server.js +126 -1
- package/src/prompts.js +47 -2
- package/src/skill-registry.js +182 -0
- package/src/stream-session.js +22 -2
- package/STRATEGY.md +0 -485
|
@@ -0,0 +1,1043 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devops
|
|
3
|
+
description: Production-grade DevOps patterns for Docker, CI/CD, deployment strategies, Nginx, health checks, and infrastructure automation
|
|
4
|
+
domain: devops
|
|
5
|
+
keywords: [devops, docker, ci-cd, deployment, nginx, github-actions, health-checks, monitoring]
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# DevOps & Infrastructure Expertise
|
|
10
|
+
|
|
11
|
+
## Worker Context
|
|
12
|
+
|
|
13
|
+
You are a DevOps specialist worker. Your role is to implement production-grade infrastructure configurations, containerization, CI/CD pipelines, deployment strategies, and monitoring solutions.
|
|
14
|
+
|
|
15
|
+
### Dockerfile — Multi-Stage Builds & Layer Optimization
|
|
16
|
+
|
|
17
|
+
**CRITICAL:** NEVER use `:latest` tag in production. Pin exact versions.
|
|
18
|
+
|
|
19
|
+
**Layer caching strategy:**
|
|
20
|
+
1. COPY package manifests FIRST (package.json, package-lock.json)
|
|
21
|
+
2. RUN dependency installation (npm ci, yarn install --frozen-lockfile)
|
|
22
|
+
3. COPY application source LAST
|
|
23
|
+
|
|
24
|
+
```dockerfile
|
|
25
|
+
# GOOD — Optimized multi-stage build
|
|
26
|
+
FROM node:20.11.0-alpine AS builder
|
|
27
|
+
WORKDIR /app
|
|
28
|
+
COPY package*.json ./
|
|
29
|
+
RUN npm ci --only=production && npm cache clean --force
|
|
30
|
+
COPY src ./src
|
|
31
|
+
RUN npm run build
|
|
32
|
+
|
|
33
|
+
FROM node:20.11.0-alpine AS runner
|
|
34
|
+
WORKDIR /app
|
|
35
|
+
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
|
|
36
|
+
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
|
|
37
|
+
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
|
|
38
|
+
COPY --chown=nodejs:nodejs package.json ./
|
|
39
|
+
USER nodejs
|
|
40
|
+
EXPOSE 3000
|
|
41
|
+
ENV NODE_ENV=production
|
|
42
|
+
CMD ["node", "dist/server.js"]
|
|
43
|
+
|
|
44
|
+
# BAD — Single stage, runs as root, uses :latest
|
|
45
|
+
FROM node:latest
|
|
46
|
+
WORKDIR /app
|
|
47
|
+
COPY . .
|
|
48
|
+
RUN npm install
|
|
49
|
+
CMD ["node", "server.js"]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**IMPORTANT:** .dockerignore MUST exclude:
|
|
53
|
+
```
|
|
54
|
+
node_modules
|
|
55
|
+
.git
|
|
56
|
+
.env
|
|
57
|
+
.env.*
|
|
58
|
+
!.env.example
|
|
59
|
+
*.log
|
|
60
|
+
coverage/
|
|
61
|
+
.DS_Store
|
|
62
|
+
README.md
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Docker Compose — Service Dependencies & Health Checks
|
|
66
|
+
|
|
67
|
+
**CRITICAL:** Use `condition: service_healthy` NOT `condition: service_started`. Services must be ready, not just running.
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
# GOOD — Health checks with dependency orchestration
|
|
71
|
+
version: '3.8'
|
|
72
|
+
|
|
73
|
+
services:
|
|
74
|
+
postgres:
|
|
75
|
+
image: postgres:16.1-alpine
|
|
76
|
+
environment:
|
|
77
|
+
POSTGRES_USER: ${DB_USER}
|
|
78
|
+
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
79
|
+
POSTGRES_DB: ${DB_NAME}
|
|
80
|
+
volumes:
|
|
81
|
+
- postgres_data:/var/lib/postgresql/data
|
|
82
|
+
healthcheck:
|
|
83
|
+
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
|
|
84
|
+
interval: 5s
|
|
85
|
+
timeout: 3s
|
|
86
|
+
retries: 5
|
|
87
|
+
networks:
|
|
88
|
+
- backend
|
|
89
|
+
|
|
90
|
+
redis:
|
|
91
|
+
image: redis:7.2.4-alpine
|
|
92
|
+
healthcheck:
|
|
93
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
94
|
+
interval: 5s
|
|
95
|
+
timeout: 3s
|
|
96
|
+
retries: 5
|
|
97
|
+
networks:
|
|
98
|
+
- backend
|
|
99
|
+
|
|
100
|
+
api:
|
|
101
|
+
build:
|
|
102
|
+
context: .
|
|
103
|
+
dockerfile: Dockerfile
|
|
104
|
+
ports:
|
|
105
|
+
- "${API_PORT:-3000}:3000"
|
|
106
|
+
env_file:
|
|
107
|
+
- .env
|
|
108
|
+
depends_on:
|
|
109
|
+
postgres:
|
|
110
|
+
condition: service_healthy
|
|
111
|
+
redis:
|
|
112
|
+
condition: service_healthy
|
|
113
|
+
healthcheck:
|
|
114
|
+
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
|
|
115
|
+
interval: 10s
|
|
116
|
+
timeout: 5s
|
|
117
|
+
retries: 3
|
|
118
|
+
start_period: 30s
|
|
119
|
+
networks:
|
|
120
|
+
- backend
|
|
121
|
+
|
|
122
|
+
volumes:
|
|
123
|
+
postgres_data:
|
|
124
|
+
|
|
125
|
+
networks:
|
|
126
|
+
backend:
|
|
127
|
+
driver: bridge
|
|
128
|
+
|
|
129
|
+
# BAD — No health checks, vague depends_on
|
|
130
|
+
version: '3.8'
|
|
131
|
+
services:
|
|
132
|
+
db:
|
|
133
|
+
image: postgres
|
|
134
|
+
environment:
|
|
135
|
+
POSTGRES_PASSWORD: password123 # Hardcoded secret!
|
|
136
|
+
|
|
137
|
+
app:
|
|
138
|
+
build: .
|
|
139
|
+
depends_on:
|
|
140
|
+
- db # No condition — app starts before DB is ready
|
|
141
|
+
ports:
|
|
142
|
+
- "3000:3000"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### CI/CD Pipelines — GitHub Actions Pattern
|
|
146
|
+
|
|
147
|
+
**NEVER:** Hardcode secrets in workflow files. Use `${{ secrets.SECRET_NAME }}`.
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
# GOOD — Parallel stages with caching and secrets
|
|
151
|
+
name: CI/CD Pipeline
|
|
152
|
+
|
|
153
|
+
on:
|
|
154
|
+
push:
|
|
155
|
+
branches: [main, develop]
|
|
156
|
+
pull_request:
|
|
157
|
+
branches: [main]
|
|
158
|
+
|
|
159
|
+
env:
|
|
160
|
+
NODE_VERSION: '20.11.0'
|
|
161
|
+
|
|
162
|
+
jobs:
|
|
163
|
+
lint:
|
|
164
|
+
runs-on: ubuntu-latest
|
|
165
|
+
steps:
|
|
166
|
+
- uses: actions/checkout@v4
|
|
167
|
+
|
|
168
|
+
- name: Setup Node.js
|
|
169
|
+
uses: actions/setup-node@v4
|
|
170
|
+
with:
|
|
171
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
172
|
+
cache: 'npm'
|
|
173
|
+
|
|
174
|
+
- name: Install dependencies
|
|
175
|
+
run: npm ci
|
|
176
|
+
|
|
177
|
+
- name: Run linter
|
|
178
|
+
run: npm run lint
|
|
179
|
+
|
|
180
|
+
test:
|
|
181
|
+
runs-on: ubuntu-latest
|
|
182
|
+
strategy:
|
|
183
|
+
matrix:
|
|
184
|
+
node-version: [18.x, 20.x]
|
|
185
|
+
steps:
|
|
186
|
+
- uses: actions/checkout@v4
|
|
187
|
+
|
|
188
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
189
|
+
uses: actions/setup-node@v4
|
|
190
|
+
with:
|
|
191
|
+
node-version: ${{ matrix.node-version }}
|
|
192
|
+
cache: 'npm'
|
|
193
|
+
|
|
194
|
+
- name: Install dependencies
|
|
195
|
+
run: npm ci
|
|
196
|
+
|
|
197
|
+
- name: Run tests
|
|
198
|
+
run: npm test -- --coverage
|
|
199
|
+
|
|
200
|
+
- name: Upload coverage
|
|
201
|
+
uses: codecov/codecov-action@v3
|
|
202
|
+
if: matrix.node-version == '20.x'
|
|
203
|
+
|
|
204
|
+
build:
|
|
205
|
+
runs-on: ubuntu-latest
|
|
206
|
+
needs: [lint, test]
|
|
207
|
+
steps:
|
|
208
|
+
- uses: actions/checkout@v4
|
|
209
|
+
|
|
210
|
+
- name: Setup Node.js
|
|
211
|
+
uses: actions/setup-node@v4
|
|
212
|
+
with:
|
|
213
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
214
|
+
cache: 'npm'
|
|
215
|
+
|
|
216
|
+
- name: Install dependencies
|
|
217
|
+
run: npm ci
|
|
218
|
+
|
|
219
|
+
- name: Build application
|
|
220
|
+
run: npm run build
|
|
221
|
+
|
|
222
|
+
- name: Upload build artifacts
|
|
223
|
+
uses: actions/upload-artifact@v4
|
|
224
|
+
with:
|
|
225
|
+
name: dist
|
|
226
|
+
path: dist/
|
|
227
|
+
|
|
228
|
+
deploy:
|
|
229
|
+
runs-on: ubuntu-latest
|
|
230
|
+
needs: build
|
|
231
|
+
if: github.ref == 'refs/heads/main'
|
|
232
|
+
environment: production
|
|
233
|
+
steps:
|
|
234
|
+
- uses: actions/checkout@v4
|
|
235
|
+
|
|
236
|
+
- name: Download build artifacts
|
|
237
|
+
uses: actions/download-artifact@v4
|
|
238
|
+
with:
|
|
239
|
+
name: dist
|
|
240
|
+
path: dist/
|
|
241
|
+
|
|
242
|
+
- name: Deploy to production
|
|
243
|
+
env:
|
|
244
|
+
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
|
245
|
+
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
|
246
|
+
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
|
247
|
+
run: |
|
|
248
|
+
echo "$DEPLOY_KEY" > deploy_key
|
|
249
|
+
chmod 600 deploy_key
|
|
250
|
+
rsync -avz -e "ssh -i deploy_key -o StrictHostKeyChecking=no" \
|
|
251
|
+
dist/ $DEPLOY_USER@$DEPLOY_HOST:/var/www/app/
|
|
252
|
+
|
|
253
|
+
# BAD — Sequential jobs, no caching, secrets exposed
|
|
254
|
+
jobs:
|
|
255
|
+
build-and-deploy:
|
|
256
|
+
runs-on: ubuntu-latest
|
|
257
|
+
steps:
|
|
258
|
+
- uses: actions/checkout@v4
|
|
259
|
+
- run: npm install # Should use npm ci
|
|
260
|
+
- run: npm run lint
|
|
261
|
+
- run: npm test
|
|
262
|
+
- run: npm run build
|
|
263
|
+
- run: |
|
|
264
|
+
sshpass -p 'mypassword' ssh user@server.com # SECRET IN CODE!
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Environment Management
|
|
268
|
+
|
|
269
|
+
**CRITICAL:** NEVER commit `.env` files with real values. Commit `.env.example` with placeholder values only.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# GOOD — .env.example (committed to git)
|
|
273
|
+
NODE_ENV=development
|
|
274
|
+
PORT=3000
|
|
275
|
+
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
|
|
276
|
+
REDIS_URL=redis://localhost:6379
|
|
277
|
+
JWT_SECRET=your-secret-key-here
|
|
278
|
+
API_KEY=your-api-key-here
|
|
279
|
+
|
|
280
|
+
# GOOD — Config validation on startup (src/config.js)
|
|
281
|
+
const requiredEnvVars = [
|
|
282
|
+
'NODE_ENV',
|
|
283
|
+
'DATABASE_URL',
|
|
284
|
+
'REDIS_URL',
|
|
285
|
+
'JWT_SECRET'
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
for (const envVar of requiredEnvVars) {
|
|
289
|
+
if (!process.env[envVar]) {
|
|
290
|
+
throw new Error(`Missing required environment variable: ${envVar}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const config = {
|
|
295
|
+
env: process.env.NODE_ENV,
|
|
296
|
+
port: parseInt(process.env.PORT, 10) || 3000,
|
|
297
|
+
db: {
|
|
298
|
+
url: process.env.DATABASE_URL,
|
|
299
|
+
pool: {
|
|
300
|
+
min: 2,
|
|
301
|
+
max: 10
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
redis: {
|
|
305
|
+
url: process.env.REDIS_URL
|
|
306
|
+
},
|
|
307
|
+
jwt: {
|
|
308
|
+
secret: process.env.JWT_SECRET,
|
|
309
|
+
expiresIn: '7d'
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
module.exports = config;
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Environment parity checklist:**
|
|
317
|
+
- [ ] Same Node.js version across dev/staging/prod
|
|
318
|
+
- [ ] Same dependency versions (use package-lock.json)
|
|
319
|
+
- [ ] Same database version
|
|
320
|
+
- [ ] Same environment variable names
|
|
321
|
+
- [ ] Secrets stored in CI/CD vault, not in code
|
|
322
|
+
|
|
323
|
+
### Deployment Strategies — Decision Tree
|
|
324
|
+
|
|
325
|
+
| Strategy | Use When | Downtime | Rollback Speed | Complexity | Resource Cost |
|
|
326
|
+
|----------|----------|----------|----------------|------------|---------------|
|
|
327
|
+
| **Rolling** | Default for most apps | Minimal (partial) | Medium (redeploy previous) | Low | 1x |
|
|
328
|
+
| **Blue-Green** | Zero-downtime critical, need instant rollback | None | Instant (switch routing) | Medium | 2x |
|
|
329
|
+
| **Canary** | High-risk changes, gradual rollout | None | Fast (route 100% to stable) | High | 1.1-1.5x |
|
|
330
|
+
| **Recreate** | Dev/staging only, breaking changes | Full | Slow (redeploy) | Very Low | 1x |
|
|
331
|
+
|
|
332
|
+
**IMPORTANT:** Choose based on:
|
|
333
|
+
1. Acceptable downtime: None → Blue-Green or Canary
|
|
334
|
+
2. Resource budget: Limited → Rolling
|
|
335
|
+
3. Risk level: High → Canary
|
|
336
|
+
4. Rollback criticality: Instant needed → Blue-Green
|
|
337
|
+
|
|
338
|
+
```yaml
|
|
339
|
+
# GOOD — Rolling deployment (Kubernetes)
|
|
340
|
+
apiVersion: apps/v1
|
|
341
|
+
kind: Deployment
|
|
342
|
+
metadata:
|
|
343
|
+
name: api
|
|
344
|
+
spec:
|
|
345
|
+
replicas: 4
|
|
346
|
+
strategy:
|
|
347
|
+
type: RollingUpdate
|
|
348
|
+
rollingUpdate:
|
|
349
|
+
maxSurge: 1 # Max 1 extra pod during update
|
|
350
|
+
maxUnavailable: 1 # Max 1 pod down during update
|
|
351
|
+
template:
|
|
352
|
+
spec:
|
|
353
|
+
containers:
|
|
354
|
+
- name: api
|
|
355
|
+
image: myapp:v2.0.0
|
|
356
|
+
readinessProbe:
|
|
357
|
+
httpGet:
|
|
358
|
+
path: /ready
|
|
359
|
+
port: 3000
|
|
360
|
+
initialDelaySeconds: 5
|
|
361
|
+
periodSeconds: 5
|
|
362
|
+
livenessProbe:
|
|
363
|
+
httpGet:
|
|
364
|
+
path: /health
|
|
365
|
+
Port: 3000
|
|
366
|
+
initialDelaySeconds: 15
|
|
367
|
+
periodSeconds: 10
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Nginx — Reverse Proxy & Static Serving
|
|
371
|
+
|
|
372
|
+
**IMPORTANT:** Always set proper proxy headers for upstream to get real client IP.
|
|
373
|
+
|
|
374
|
+
```nginx
|
|
375
|
+
# GOOD — Production Nginx config
|
|
376
|
+
upstream api_backend {
|
|
377
|
+
least_conn;
|
|
378
|
+
server api1:3000 max_fails=3 fail_timeout=30s;
|
|
379
|
+
server api2:3000 max_fails=3 fail_timeout=30s;
|
|
380
|
+
server api3:3000 max_fails=3 fail_timeout=30s;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
# Rate limiting zone
|
|
384
|
+
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
385
|
+
|
|
386
|
+
server {
|
|
387
|
+
listen 80;
|
|
388
|
+
server_name api.example.com;
|
|
389
|
+
|
|
390
|
+
# Redirect HTTP to HTTPS
|
|
391
|
+
return 301 https://$server_name$request_uri;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
server {
|
|
395
|
+
listen 443 ssl http2;
|
|
396
|
+
server_name api.example.com;
|
|
397
|
+
|
|
398
|
+
# SSL certificates (Let's Encrypt)
|
|
399
|
+
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
|
|
400
|
+
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
|
|
401
|
+
ssl_protocols TLSv1.2 TLSv1.3;
|
|
402
|
+
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
403
|
+
|
|
404
|
+
# Security headers
|
|
405
|
+
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
406
|
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
407
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
408
|
+
add_header X-XSS-Protection "1; mode=block" always;
|
|
409
|
+
|
|
410
|
+
# Static files
|
|
411
|
+
location /static/ {
|
|
412
|
+
alias /var/www/static/;
|
|
413
|
+
expires 1y;
|
|
414
|
+
access_log off;
|
|
415
|
+
add_header Cache-Control "public, immutable";
|
|
416
|
+
try_files $uri $uri/ =404;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
# API proxy with rate limiting
|
|
420
|
+
location /api/ {
|
|
421
|
+
limit_req zone=api_limit burst=20 nodelay;
|
|
422
|
+
|
|
423
|
+
proxy_pass http://api_backend;
|
|
424
|
+
proxy_http_version 1.1;
|
|
425
|
+
|
|
426
|
+
# Preserve original request info
|
|
427
|
+
proxy_set_header Host $host;
|
|
428
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
429
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
430
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
431
|
+
proxy_set_header X-Request-ID $request_id;
|
|
432
|
+
|
|
433
|
+
# Timeouts
|
|
434
|
+
proxy_connect_timeout 5s;
|
|
435
|
+
proxy_send_timeout 60s;
|
|
436
|
+
proxy_read_timeout 60s;
|
|
437
|
+
|
|
438
|
+
# Buffering
|
|
439
|
+
proxy_buffering on;
|
|
440
|
+
proxy_buffer_size 4k;
|
|
441
|
+
proxy_buffers 8 4k;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
# Health check endpoint (no rate limiting)
|
|
445
|
+
location /health {
|
|
446
|
+
access_log off;
|
|
447
|
+
proxy_pass http://api_backend;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
# BAD — Insecure, no rate limiting, missing headers
|
|
452
|
+
server {
|
|
453
|
+
listen 80;
|
|
454
|
+
server_name api.example.com;
|
|
455
|
+
|
|
456
|
+
location / {
|
|
457
|
+
proxy_pass http://localhost:3000; # No upstream, hardcoded port
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Health Checks — Liveness vs Readiness
|
|
463
|
+
|
|
464
|
+
**CRITICAL distinction:**
|
|
465
|
+
- **Liveness:** Is the process alive? (Kill and restart if fails)
|
|
466
|
+
- **Readiness:** Can it serve traffic? (Remove from load balancer if fails)
|
|
467
|
+
|
|
468
|
+
```javascript
|
|
469
|
+
// GOOD — Comprehensive health check implementation
|
|
470
|
+
const express = require('express');
|
|
471
|
+
const app = express();
|
|
472
|
+
|
|
473
|
+
// Liveness probe — lightweight, just confirms process is running
|
|
474
|
+
app.get('/health', (req, res) => {
|
|
475
|
+
res.status(200).json({
|
|
476
|
+
status: 'ok',
|
|
477
|
+
timestamp: new Date().toISOString(),
|
|
478
|
+
uptime: process.uptime(),
|
|
479
|
+
version: process.env.APP_VERSION || 'unknown'
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// Readiness probe — checks all dependencies
|
|
484
|
+
app.get('/ready', async (req, res) => {
|
|
485
|
+
const checks = {
|
|
486
|
+
database: false,
|
|
487
|
+
redis: false,
|
|
488
|
+
diskSpace: false
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
// Check database connection
|
|
493
|
+
await db.query('SELECT 1');
|
|
494
|
+
checks.database = true;
|
|
495
|
+
|
|
496
|
+
// Check Redis connection
|
|
497
|
+
await redis.ping();
|
|
498
|
+
checks.redis = true;
|
|
499
|
+
|
|
500
|
+
// Check disk space (optional but recommended)
|
|
501
|
+
const diskUsage = await checkDiskSpace('/');
|
|
502
|
+
checks.diskSpace = diskUsage.percentUsed < 90;
|
|
503
|
+
|
|
504
|
+
const allReady = Object.values(checks).every(status => status === true);
|
|
505
|
+
|
|
506
|
+
res.status(allReady ? 200 : 503).json({
|
|
507
|
+
status: allReady ? 'ready' : 'not_ready',
|
|
508
|
+
checks,
|
|
509
|
+
timestamp: new Date().toISOString()
|
|
510
|
+
});
|
|
511
|
+
} catch (error) {
|
|
512
|
+
res.status(503).json({
|
|
513
|
+
status: 'not_ready',
|
|
514
|
+
checks,
|
|
515
|
+
error: error.message,
|
|
516
|
+
timestamp: new Date().toISOString()
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// BAD — Single endpoint, no dependency checks
|
|
522
|
+
app.get('/health', (req, res) => {
|
|
523
|
+
res.send('OK'); // No JSON, no version info, doesn't check DB/Redis
|
|
524
|
+
});
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Logging & Monitoring
|
|
528
|
+
|
|
529
|
+
**IMPORTANT:** Use structured JSON logs for production. Include request IDs for correlation.
|
|
530
|
+
|
|
531
|
+
```javascript
|
|
532
|
+
// GOOD — Structured logging with request ID correlation
|
|
533
|
+
const winston = require('winston');
|
|
534
|
+
const { v4: uuidv4 } = require('uuid');
|
|
535
|
+
|
|
536
|
+
const logger = winston.createLogger({
|
|
537
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
538
|
+
format: winston.format.combine(
|
|
539
|
+
winston.format.timestamp(),
|
|
540
|
+
winston.format.json()
|
|
541
|
+
),
|
|
542
|
+
defaultMeta: {
|
|
543
|
+
service: 'api',
|
|
544
|
+
version: process.env.APP_VERSION,
|
|
545
|
+
environment: process.env.NODE_ENV
|
|
546
|
+
},
|
|
547
|
+
transports: [
|
|
548
|
+
new winston.transports.Console()
|
|
549
|
+
]
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// Request ID middleware
|
|
553
|
+
app.use((req, res, next) => {
|
|
554
|
+
req.id = req.headers['x-request-id'] || uuidv4();
|
|
555
|
+
res.setHeader('x-request-id', req.id);
|
|
556
|
+
next();
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
// Request logging middleware
|
|
560
|
+
app.use((req, res, next) => {
|
|
561
|
+
const start = Date.now();
|
|
562
|
+
|
|
563
|
+
res.on('finish', () => {
|
|
564
|
+
const duration = Date.now() - start;
|
|
565
|
+
logger.info('HTTP request', {
|
|
566
|
+
requestId: req.id,
|
|
567
|
+
method: req.method,
|
|
568
|
+
path: req.path,
|
|
569
|
+
statusCode: res.statusCode,
|
|
570
|
+
duration,
|
|
571
|
+
userAgent: req.headers['user-agent'],
|
|
572
|
+
ip: req.headers['x-real-ip'] || req.ip
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
next();
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
// Prometheus metrics endpoint
|
|
580
|
+
const client = require('prom-client');
|
|
581
|
+
const register = new client.Registry();
|
|
582
|
+
|
|
583
|
+
const httpRequestDuration = new client.Histogram({
|
|
584
|
+
name: 'http_request_duration_seconds',
|
|
585
|
+
help: 'Duration of HTTP requests in seconds',
|
|
586
|
+
labelNames: ['method', 'route', 'status_code'],
|
|
587
|
+
buckets: [0.1, 0.5, 1, 2, 5]
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const httpRequestTotal = new client.Counter({
|
|
591
|
+
name: 'http_requests_total',
|
|
592
|
+
help: 'Total number of HTTP requests',
|
|
593
|
+
labelNames: ['method', 'route', 'status_code']
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
register.registerMetric(httpRequestDuration);
|
|
597
|
+
register.registerMetric(httpRequestTotal);
|
|
598
|
+
|
|
599
|
+
app.get('/metrics', async (req, res) => {
|
|
600
|
+
res.set('Content-Type', register.contentType);
|
|
601
|
+
res.end(await register.metrics());
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
// BAD — Unstructured logs, no correlation
|
|
605
|
+
console.log('User logged in'); // No timestamp, no user ID, no request context
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**Log levels decision tree:**
|
|
609
|
+
| Level | Use For | Production Volume |
|
|
610
|
+
|-------|---------|-------------------|
|
|
611
|
+
| error | System failures, unhandled exceptions | Low |
|
|
612
|
+
| warn | Recoverable issues, deprecated usage | Low-Medium |
|
|
613
|
+
| info | Business events, HTTP requests | Medium |
|
|
614
|
+
| debug | Detailed app flow, variable values | High (disable in prod) |
|
|
615
|
+
|
|
616
|
+
### Database Migrations in CI/CD
|
|
617
|
+
|
|
618
|
+
**CRITICAL:** Run migrations BEFORE deploying new code. Migration failures must block deployment.
|
|
619
|
+
|
|
620
|
+
```yaml
|
|
621
|
+
# GOOD — Separate migration job in CI/CD
|
|
622
|
+
jobs:
|
|
623
|
+
migrate:
|
|
624
|
+
runs-on: ubuntu-latest
|
|
625
|
+
environment: production
|
|
626
|
+
steps:
|
|
627
|
+
- uses: actions/checkout@v4
|
|
628
|
+
|
|
629
|
+
- name: Setup Node.js
|
|
630
|
+
uses: actions/setup-node@v4
|
|
631
|
+
with:
|
|
632
|
+
node-version: '20.11.0'
|
|
633
|
+
|
|
634
|
+
- name: Install dependencies
|
|
635
|
+
run: npm ci
|
|
636
|
+
|
|
637
|
+
- name: Run database migrations
|
|
638
|
+
env:
|
|
639
|
+
DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
|
|
640
|
+
run: npm run migrate
|
|
641
|
+
timeout-minutes: 5
|
|
642
|
+
|
|
643
|
+
- name: Verify migration success
|
|
644
|
+
env:
|
|
645
|
+
DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
|
|
646
|
+
run: npm run migrate:status
|
|
647
|
+
|
|
648
|
+
deploy:
|
|
649
|
+
runs-on: ubuntu-latest
|
|
650
|
+
needs: migrate # Deploy ONLY if migrations succeed
|
|
651
|
+
environment: production
|
|
652
|
+
steps:
|
|
653
|
+
- name: Deploy application
|
|
654
|
+
run: ./deploy.sh
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
**NEVER do this:**
|
|
658
|
+
```javascript
|
|
659
|
+
// BAD — Running migrations in application startup
|
|
660
|
+
async function startServer() {
|
|
661
|
+
await runMigrations(); // ❌ Causes race conditions with multiple instances
|
|
662
|
+
app.listen(3000);
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
**Migration rollback strategy:**
|
|
667
|
+
- Each migration must have a `down()` method
|
|
668
|
+
- Test rollback in staging BEFORE production
|
|
669
|
+
- Automate rollback on deployment failure
|
|
670
|
+
- Keep migrations backward-compatible when possible
|
|
671
|
+
|
|
672
|
+
### Git Workflow & Conventional Commits
|
|
673
|
+
|
|
674
|
+
**Branch naming convention:**
|
|
675
|
+
```
|
|
676
|
+
feature/TICKET-123-add-user-auth
|
|
677
|
+
bugfix/TICKET-456-fix-memory-leak
|
|
678
|
+
hotfix/TICKET-789-patch-security-vuln
|
|
679
|
+
chore/update-dependencies
|
|
680
|
+
docs/api-documentation
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
**Conventional commits format:**
|
|
684
|
+
```
|
|
685
|
+
<type>(<scope>): <subject>
|
|
686
|
+
|
|
687
|
+
<body>
|
|
688
|
+
|
|
689
|
+
<footer>
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
**Types:**
|
|
693
|
+
- `feat:` New feature
|
|
694
|
+
- `fix:` Bug fix
|
|
695
|
+
- `chore:` Maintenance (deps, config)
|
|
696
|
+
- `docs:` Documentation only
|
|
697
|
+
- `refactor:` Code change without behavior change
|
|
698
|
+
- `test:` Adding or updating tests
|
|
699
|
+
- `ci:` CI/CD pipeline changes
|
|
700
|
+
- `perf:` Performance improvement
|
|
701
|
+
|
|
702
|
+
**Example:**
|
|
703
|
+
```
|
|
704
|
+
feat(api): add user authentication endpoint
|
|
705
|
+
|
|
706
|
+
Implement JWT-based authentication with refresh tokens.
|
|
707
|
+
Includes rate limiting and brute-force protection.
|
|
708
|
+
|
|
709
|
+
Closes #123
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
**Protected main branch settings:**
|
|
713
|
+
- ✅ Require pull request reviews (min 1)
|
|
714
|
+
- ✅ Require status checks to pass (CI pipeline)
|
|
715
|
+
- ✅ Require branches to be up to date
|
|
716
|
+
- ✅ Include administrators in restrictions
|
|
717
|
+
- ❌ Allow force pushes
|
|
718
|
+
- ❌ Allow deletions
|
|
719
|
+
|
|
720
|
+
## Conventions
|
|
721
|
+
|
|
722
|
+
### Response Formats
|
|
723
|
+
**API health/ready responses:**
|
|
724
|
+
```json
|
|
725
|
+
{
|
|
726
|
+
"status": "ok" | "ready" | "not_ready",
|
|
727
|
+
"timestamp": "ISO 8601 string",
|
|
728
|
+
"checks": { "database": true, "redis": true },
|
|
729
|
+
"version": "semver string"
|
|
730
|
+
}
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
### Naming Conventions
|
|
734
|
+
- Environment variables: `SCREAMING_SNAKE_CASE`
|
|
735
|
+
- Docker image tags: `semver` (e.g., `v1.2.3`) or `git-sha` (e.g., `abc1234`)
|
|
736
|
+
- Kubernetes resources: `kebab-case`
|
|
737
|
+
- Secrets in CI/CD: `UPPER_SNAKE_CASE` with service prefix (e.g., `DB_PASSWORD`, `API_KEY`)
|
|
738
|
+
|
|
739
|
+
### File Paths
|
|
740
|
+
- Always use relative paths in configs
|
|
741
|
+
- Use environment variables for absolute paths that differ per environment
|
|
742
|
+
- Example: `${PROJECT_ROOT}/dist` not `/home/user/project/dist`
|
|
743
|
+
|
|
744
|
+
### Port Assignments
|
|
745
|
+
| Service | Default Port | Environment Variable |
|
|
746
|
+
|---------|--------------|---------------------|
|
|
747
|
+
| API | 3000 | `API_PORT` |
|
|
748
|
+
| Frontend | 8080 | `FRONTEND_PORT` |
|
|
749
|
+
| PostgreSQL | 5432 | `DB_PORT` |
|
|
750
|
+
| Redis | 6379 | `REDIS_PORT` |
|
|
751
|
+
| Nginx | 80/443 | N/A |
|
|
752
|
+
|
|
753
|
+
## Common Patterns
|
|
754
|
+
|
|
755
|
+
### Pattern 1: Multi-Stage Docker Build with Non-Root User
|
|
756
|
+
```dockerfile
|
|
757
|
+
FROM node:20.11.0-alpine AS builder
|
|
758
|
+
WORKDIR /app
|
|
759
|
+
COPY package*.json ./
|
|
760
|
+
RUN npm ci --only=production
|
|
761
|
+
COPY . .
|
|
762
|
+
RUN npm run build
|
|
763
|
+
|
|
764
|
+
FROM node:20.11.0-alpine AS runner
|
|
765
|
+
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
|
|
766
|
+
WORKDIR /app
|
|
767
|
+
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
|
|
768
|
+
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
|
|
769
|
+
USER nodejs
|
|
770
|
+
EXPOSE 3000
|
|
771
|
+
CMD ["node", "dist/server.js"]
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
### Pattern 2: Docker Compose with Health-Based Dependencies
|
|
775
|
+
```yaml
|
|
776
|
+
version: '3.8'
|
|
777
|
+
services:
|
|
778
|
+
db:
|
|
779
|
+
image: postgres:16.1-alpine
|
|
780
|
+
healthcheck:
|
|
781
|
+
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
|
|
782
|
+
interval: 5s
|
|
783
|
+
timeout: 3s
|
|
784
|
+
retries: 5
|
|
785
|
+
|
|
786
|
+
api:
|
|
787
|
+
build: .
|
|
788
|
+
depends_on:
|
|
789
|
+
db:
|
|
790
|
+
condition: service_healthy
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### Pattern 3: GitHub Actions Parallel Pipeline
|
|
794
|
+
```yaml
|
|
795
|
+
jobs:
|
|
796
|
+
lint:
|
|
797
|
+
runs-on: ubuntu-latest
|
|
798
|
+
steps:
|
|
799
|
+
- uses: actions/checkout@v4
|
|
800
|
+
- uses: actions/setup-node@v4
|
|
801
|
+
with:
|
|
802
|
+
node-version: '20.11.0'
|
|
803
|
+
cache: 'npm'
|
|
804
|
+
- run: npm ci
|
|
805
|
+
- run: npm run lint
|
|
806
|
+
|
|
807
|
+
test:
|
|
808
|
+
runs-on: ubuntu-latest
|
|
809
|
+
steps:
|
|
810
|
+
- uses: actions/checkout@v4
|
|
811
|
+
- uses: actions/setup-node@v4
|
|
812
|
+
with:
|
|
813
|
+
node-version: '20.11.0'
|
|
814
|
+
cache: 'npm'
|
|
815
|
+
- run: npm ci
|
|
816
|
+
- run: npm test
|
|
817
|
+
|
|
818
|
+
deploy:
|
|
819
|
+
needs: [lint, test]
|
|
820
|
+
runs-on: ubuntu-latest
|
|
821
|
+
if: github.ref == 'refs/heads/main'
|
|
822
|
+
steps:
|
|
823
|
+
- name: Deploy
|
|
824
|
+
run: ./deploy.sh
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### Pattern 4: Nginx Reverse Proxy with Rate Limiting
|
|
828
|
+
```nginx
|
|
829
|
+
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
830
|
+
|
|
831
|
+
upstream api_backend {
|
|
832
|
+
least_conn;
|
|
833
|
+
server api1:3000;
|
|
834
|
+
server api2:3000;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
server {
|
|
838
|
+
listen 443 ssl http2;
|
|
839
|
+
|
|
840
|
+
location /api/ {
|
|
841
|
+
limit_req zone=api_limit burst=20 nodelay;
|
|
842
|
+
proxy_pass http://api_backend;
|
|
843
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
844
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
### Pattern 5: Comprehensive Health Checks
|
|
850
|
+
```javascript
|
|
851
|
+
app.get('/health', (req, res) => {
|
|
852
|
+
res.status(200).json({
|
|
853
|
+
status: 'ok',
|
|
854
|
+
uptime: process.uptime(),
|
|
855
|
+
version: process.env.APP_VERSION
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
app.get('/ready', async (req, res) => {
|
|
860
|
+
const checks = {
|
|
861
|
+
database: await checkDB(),
|
|
862
|
+
redis: await checkRedis()
|
|
863
|
+
};
|
|
864
|
+
const ready = Object.values(checks).every(v => v === true);
|
|
865
|
+
res.status(ready ? 200 : 503).json({
|
|
866
|
+
status: ready ? 'ready' : 'not_ready',
|
|
867
|
+
checks
|
|
868
|
+
});
|
|
869
|
+
});
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
## Anti-Patterns
|
|
873
|
+
|
|
874
|
+
### Anti-Pattern 1: Secrets in Dockerfile or Code
|
|
875
|
+
```dockerfile
|
|
876
|
+
# BAD — Secret hardcoded in Dockerfile
|
|
877
|
+
ENV DATABASE_PASSWORD=super_secret_password
|
|
878
|
+
ENV API_KEY=sk-1234567890abcdef
|
|
879
|
+
|
|
880
|
+
# GOOD — Secrets injected at runtime
|
|
881
|
+
ENV DATABASE_PASSWORD=${DATABASE_PASSWORD}
|
|
882
|
+
ENV API_KEY=${API_KEY}
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
```javascript
|
|
886
|
+
// BAD — Secret hardcoded in code
|
|
887
|
+
const dbPassword = 'mypassword123';
|
|
888
|
+
|
|
889
|
+
// GOOD — Secret from environment variable
|
|
890
|
+
const dbPassword = process.env.DATABASE_PASSWORD;
|
|
891
|
+
if (!dbPassword) throw new Error('DATABASE_PASSWORD not set');
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
### Anti-Pattern 2: Running as Root in Containers
|
|
895
|
+
```dockerfile
|
|
896
|
+
# BAD — Runs as root (default)
|
|
897
|
+
FROM node:20-alpine
|
|
898
|
+
WORKDIR /app
|
|
899
|
+
COPY . .
|
|
900
|
+
CMD ["node", "server.js"]
|
|
901
|
+
|
|
902
|
+
# GOOD — Creates and uses non-root user
|
|
903
|
+
FROM node:20-alpine
|
|
904
|
+
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
|
|
905
|
+
WORKDIR /app
|
|
906
|
+
COPY --chown=nodejs:nodejs . .
|
|
907
|
+
USER nodejs
|
|
908
|
+
CMD ["node", "server.js"]
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
### Anti-Pattern 3: No Health Checks
|
|
912
|
+
```yaml
|
|
913
|
+
# BAD — No health checks, depends_on without condition
|
|
914
|
+
services:
|
|
915
|
+
db:
|
|
916
|
+
image: postgres:16-alpine
|
|
917
|
+
|
|
918
|
+
api:
|
|
919
|
+
build: .
|
|
920
|
+
depends_on:
|
|
921
|
+
- db # Starts when db container starts, NOT when postgres is ready
|
|
922
|
+
|
|
923
|
+
# GOOD — Health checks with service_healthy condition
|
|
924
|
+
services:
|
|
925
|
+
db:
|
|
926
|
+
image: postgres:16-alpine
|
|
927
|
+
healthcheck:
|
|
928
|
+
test: ["CMD-SHELL", "pg_isready"]
|
|
929
|
+
interval: 5s
|
|
930
|
+
retries: 5
|
|
931
|
+
|
|
932
|
+
api:
|
|
933
|
+
build: .
|
|
934
|
+
depends_on:
|
|
935
|
+
db:
|
|
936
|
+
condition: service_healthy
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
### Anti-Pattern 4: Manual SSH Deployments
|
|
940
|
+
```bash
|
|
941
|
+
# BAD — Manual deployment script
|
|
942
|
+
sshpass -p 'password' ssh user@server.com "cd /var/www && git pull && npm install && pm2 restart app"
|
|
943
|
+
|
|
944
|
+
# GOOD — Automated CI/CD deployment with secrets vault
|
|
945
|
+
# In GitHub Actions:
|
|
946
|
+
steps:
|
|
947
|
+
- name: Deploy via SSH
|
|
948
|
+
env:
|
|
949
|
+
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
|
950
|
+
HOST: ${{ secrets.DEPLOY_HOST }}
|
|
951
|
+
run: |
|
|
952
|
+
echo "$SSH_KEY" > key
|
|
953
|
+
chmod 600 key
|
|
954
|
+
ssh -i key $HOST './deploy.sh'
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
### Anti-Pattern 5: Monolithic CI Jobs
|
|
958
|
+
```yaml
|
|
959
|
+
# BAD — One giant job that does everything sequentially
|
|
960
|
+
jobs:
|
|
961
|
+
build-test-deploy:
|
|
962
|
+
runs-on: ubuntu-latest
|
|
963
|
+
steps:
|
|
964
|
+
- run: npm run lint
|
|
965
|
+
- run: npm test
|
|
966
|
+
- run: npm run build
|
|
967
|
+
- run: npm run deploy # If any step fails, all fail. No parallelization.
|
|
968
|
+
|
|
969
|
+
# GOOD — Separate jobs that run in parallel
|
|
970
|
+
jobs:
|
|
971
|
+
lint:
|
|
972
|
+
runs-on: ubuntu-latest
|
|
973
|
+
steps:
|
|
974
|
+
- run: npm run lint
|
|
975
|
+
|
|
976
|
+
test:
|
|
977
|
+
runs-on: ubuntu-latest
|
|
978
|
+
steps:
|
|
979
|
+
- run: npm test
|
|
980
|
+
|
|
981
|
+
deploy:
|
|
982
|
+
needs: [lint, test] # Runs only after both pass
|
|
983
|
+
runs-on: ubuntu-latest
|
|
984
|
+
steps:
|
|
985
|
+
- run: npm run deploy
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
## Integration Notes
|
|
989
|
+
|
|
990
|
+
### Team Communication (if multi-session worker)
|
|
991
|
+
1. **Before starting:** Call `team_check_inbox()` to check for messages from orchestrator or other workers
|
|
992
|
+
2. **Read conventions:** Call `artifact_read({ artifactId: "shared-conventions" })` to get project-wide naming, format, and behavior standards
|
|
993
|
+
3. **Publish infrastructure artifact:**
|
|
994
|
+
```javascript
|
|
995
|
+
artifact_publish({
|
|
996
|
+
artifactId: "infrastructure",
|
|
997
|
+
title: "Infrastructure Configuration",
|
|
998
|
+
data: {
|
|
999
|
+
files: [
|
|
1000
|
+
"Dockerfile",
|
|
1001
|
+
"docker-compose.yml",
|
|
1002
|
+
".dockerignore",
|
|
1003
|
+
".github/workflows/ci-cd.yml",
|
|
1004
|
+
"nginx/nginx.conf"
|
|
1005
|
+
],
|
|
1006
|
+
ports: {
|
|
1007
|
+
api: 3000,
|
|
1008
|
+
nginx: 80,
|
|
1009
|
+
nginxSsl: 443
|
|
1010
|
+
},
|
|
1011
|
+
healthEndpoints: {
|
|
1012
|
+
liveness: "/health",
|
|
1013
|
+
readiness: "/ready",
|
|
1014
|
+
metrics: "/metrics"
|
|
1015
|
+
},
|
|
1016
|
+
environmentVars: [
|
|
1017
|
+
"NODE_ENV",
|
|
1018
|
+
"DATABASE_URL",
|
|
1019
|
+
"REDIS_URL",
|
|
1020
|
+
"JWT_SECRET",
|
|
1021
|
+
"API_PORT",
|
|
1022
|
+
"APP_VERSION"
|
|
1023
|
+
],
|
|
1024
|
+
deploymentStrategy: "rolling",
|
|
1025
|
+
cicdPipeline: ".github/workflows/ci-cd.yml"
|
|
1026
|
+
}
|
|
1027
|
+
})
|
|
1028
|
+
```
|
|
1029
|
+
4. **Coordinate with other workers:**
|
|
1030
|
+
- **Backend worker:** Confirm API port, health check endpoints (`/health`, `/ready`), metrics endpoint (`/metrics`)
|
|
1031
|
+
- **Database worker:** Get migration script paths, connection string format, health check query
|
|
1032
|
+
- **Frontend worker:** Confirm static asset paths for Nginx serving
|
|
1033
|
+
5. **Broadcast completion:** Call `team_broadcast({ from: "devops", content: "Infrastructure setup complete. Dockerfile, docker-compose.yml, CI/CD pipeline, and Nginx config ready." })`
|
|
1034
|
+
6. **Use relative paths only:** All file paths in artifacts must be relative to project root (e.g., `src/config.js` NOT `/home/user/project/src/config.js`)
|
|
1035
|
+
|
|
1036
|
+
### Standalone Worker Mode
|
|
1037
|
+
If operating independently (no team tools available):
|
|
1038
|
+
1. Create infrastructure files in project root: `Dockerfile`, `docker-compose.yml`, `.dockerignore`
|
|
1039
|
+
2. Create `.github/workflows/ci-cd.yml` for GitHub Actions pipeline
|
|
1040
|
+
3. Create `nginx/nginx.conf` if web server config is needed
|
|
1041
|
+
4. Create `.env.example` with all required environment variables (no real values)
|
|
1042
|
+
5. Verify all files use relative paths
|
|
1043
|
+
6. Report completion with list of created files and configuration summary
|