# DevOps Subagent Reference ## Critical Rule: Server Access is READ-ONLY **Claude Code has READ-ONLY access to production/test servers.** The `claude-win10` user cannot execute write operations directly. When working with production/test servers: 1. **Provide commands** for the user to execute (do not attempt SSH) 2. **Wait for user** to report command output 3. **Provide fix commands** 1-3 at a time (errors may cascade) 4. **Verify success** with read-only commands after user executes fixes 5. **Document findings** in relevant documentation Commands in this reference are for the **user to run on the server**, not for Claude to execute. --- ## Critical Rule: Git Bash Path Conversion Git Bash on Windows auto-converts Unix paths, breaking container commands. | Solution | Example | | ---------------------------- | -------------------------------------------------------- | | `sh -c` with single quotes | `podman exec container sh -c '/usr/local/bin/script.sh'` | | Double slashes | `podman exec container //usr//local//bin//script.sh` | | MSYS_NO_PATHCONV=1 | `MSYS_NO_PATHCONV=1 podman exec ...` | | Windows paths for host files | `podman cp "d:/path/file" container:/tmp/file` | --- ## Container Commands (Podman) ### Dev Container Operations ```bash # List running containers podman ps # Container logs podman logs flyer-crawler-dev podman logs -f flyer-crawler-dev # Follow # Execute in container podman exec -it flyer-crawler-dev bash podman exec -it flyer-crawler-dev npm run test:unit # Restart container podman restart flyer-crawler-dev # Container resource usage podman stats flyer-crawler-dev ``` ### Test Execution (from Windows) ```bash # Unit tests - pipe output for AI processing podman exec -it flyer-crawler-dev npm run test:unit 2>&1 | tee test-results.txt # Integration tests podman exec -it flyer-crawler-dev npm run test:integration # Type check (CRITICAL before commit) podman exec -it flyer-crawler-dev npm run type-check # Specific test file podman exec -it flyer-crawler-dev npm test -- --run src/hooks/useAuth.test.tsx ``` ### Database Operations (from Windows) ```bash # Reset dev database podman exec -it flyer-crawler-dev npm run db:reset:dev # Access PostgreSQL podman exec -it flyer-crawler-dev psql -U postgres -d flyer_crawler_dev # Run SQL file (use MSYS_NO_PATHCONV to avoid path conversion) MSYS_NO_PATHCONV=1 podman exec -it flyer-crawler-dev psql -U postgres -d flyer_crawler_dev -f /app/sql/master_schema_rollup.sql ``` --- ## PM2 Commands ### Production Server > **Note**: These commands are for the **user to execute on the server**. Claude Code provides commands but cannot run them directly. See [Server Access is READ-ONLY](#critical-rule-server-access-is-read-only) above. ```bash # List all apps pm2 list # App status pm2 show flyer-crawler-api # Logs pm2 logs flyer-crawler-api pm2 logs --lines 100 # Restart apps pm2 restart flyer-crawler-api pm2 reload flyer-crawler-api # Zero-downtime # Stop/Start pm2 stop flyer-crawler-api pm2 start flyer-crawler-api # Delete and reload from config pm2 delete all pm2 start ecosystem.config.cjs ``` ### PM2 Config: `ecosystem.config.cjs` | App | Purpose | Memory | Mode | | -------------------------------- | ---------------- | ------ | ------- | | `flyer-crawler-api` | Express server | 500M | Cluster | | `flyer-crawler-worker` | BullMQ worker | 1G | Fork | | `flyer-crawler-analytics-worker` | Analytics worker | 1G | Fork | Test variants: `*-test` suffix --- ## CI/CD Workflows ### Location: `.gitea/workflows/` | Workflow | Trigger | Purpose | | ----------------------------- | ------------ | ----------------------- | | `deploy-to-test.yml` | Push to main | Auto-deploy to test env | | `deploy-to-prod.yml` | Manual | Deploy to production | | `manual-db-backup.yml` | Manual | Database backup | | `manual-db-reset-test.yml` | Manual | Reset test database | | `manual-db-reset-prod.yml` | Manual | Reset prod database | | `manual-db-restore.yml` | Manual | Restore database | | `manual-deploy-major.yml` | Manual | Major version release | | `manual-redis-flush-prod.yml` | Manual | Flush Redis cache | ### Deploy to Test Pipeline Steps 1. Checkout code 2. Setup Node.js 20 3. `npm ci` 4. Bump patch version (creates git tag) 5. `npm run type-check` 6. `npm run test:unit` 7. Check schema hash against deployed DB 8. `npm run build` 9. Copy files to `/var/www/flyer-crawler-test.projectium.com/` 10. `pm2 reload ecosystem-test.config.cjs` ### Deploy to Production Pipeline Steps 1. Verify confirmation phrase ("deploy-to-prod") 2. Checkout `main` branch 3. `npm ci` 4. Bump minor version (creates git tag) 5. Check schema hash against prod DB 6. `npm run build` (with Sentry source maps) 7. Copy files to `/var/www/flyer-crawler.projectium.com/` 8. `pm2 reload ecosystem.config.cjs` --- ## Deployment Paths | Environment | Path | Domain | | ------------- | --------------------------------------------- | ----------------------------------- | | Production | `/var/www/flyer-crawler.projectium.com/` | `flyer-crawler.projectium.com` | | Test | `/var/www/flyer-crawler-test.projectium.com/` | `flyer-crawler-test.projectium.com` | | Dev Container | `/app/` | `localhost:3000` | --- ## Environment Configuration ### Files | File | Purpose | | --------------------------- | ----------------------- | | `ecosystem.config.cjs` | PM2 production config | | `ecosystem-test.config.cjs` | PM2 test config | | `src/config/env.ts` | Zod schema for env vars | | `.env.example` | Template for env vars | ### Required Secrets (Gitea CI/CD) | Category | Secrets | | ---------- | -------------------------------------------------------------------------------------------- | | Database | `DB_HOST`, `DB_USER_PROD`, `DB_PASSWORD_PROD`, `DB_DATABASE_PROD` | | Test DB | `DB_USER_TEST`, `DB_PASSWORD_TEST`, `DB_DATABASE_TEST` | | Redis | `REDIS_PASSWORD_PROD`, `REDIS_PASSWORD_TEST` | | Auth | `JWT_SECRET`, `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GH_CLIENT_ID`, `GH_CLIENT_SECRET` | | AI | `VITE_GOOGLE_GENAI_API_KEY`, `VITE_GOOGLE_GENAI_API_KEY_TEST` | | Monitoring | `SENTRY_DSN`, `SENTRY_DSN_TEST`, `SENTRY_AUTH_TOKEN` | | Maps | `GOOGLE_MAPS_API_KEY` | ### Adding New Secret 1. Add to Gitea Settings > Secrets 2. Update workflow YAML: `SENTRY_DSN: ${{ secrets.SENTRY_DSN }}` 3. Update `ecosystem.config.cjs` 4. Update `src/config/env.ts` Zod schema 5. Update `.env.example` --- ## Redis Commands ### Dev Container ```bash # Access Redis CLI podman exec -it flyer-crawler-dev redis-cli # Common commands KEYS * FLUSHALL INFO ``` ### Production > **Note**: User executes these commands on the server. ```bash # Access Redis CLI redis-cli -a $REDIS_PASSWORD # Flush cache (use with caution) # Or use manual-redis-flush-prod.yml workflow ``` --- ## Health Checks ### Endpoints | Endpoint | Purpose | | -------------------------- | -------------------------------------- | | `GET /api/health` | Basic health check | | `GET /api/health/detailed` | Full system status (DB, Redis, queues) | ### Manual Health Check ```bash # From Windows curl http://localhost:3000/api/health # Or via Podman podman exec -it flyer-crawler-dev curl http://localhost:3000/api/health ``` --- ## Log Locations ### Production Server ```bash # PM2 logs ~/.pm2/logs/ # NGINX logs /var/log/nginx/access.log /var/log/nginx/error.log # Application logs (via PM2) pm2 logs flyer-crawler-api --lines 200 ``` ### Dev Container ```bash # View container logs podman logs flyer-crawler-dev # Follow logs podman logs -f flyer-crawler-dev ``` --- ## Backup/Restore ### Database Backup (Manual Workflow) Trigger `manual-db-backup.yml` from Gitea Actions UI. ### Manual Backup > **Note**: User executes these commands on the server. ```bash # Backup PGPASSWORD=$DB_PASSWORD pg_dump -h $DB_HOST -U $DB_USER $DB_NAME > backup_$(date +%Y%m%d).sql # Restore PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -U $DB_USER $DB_NAME < backup.sql ``` --- ## Bugsink (Error Tracking) ### Dev Container Token Generation ```bash MSYS_NO_PATHCONV=1 podman exec -e DATABASE_URL=postgresql://bugsink:bugsink_dev_password@postgres:5432/bugsink -e SECRET_KEY=dev-bugsink-secret-key-minimum-50-characters-for-security flyer-crawler-dev sh -c 'cd /opt/bugsink/conf && DJANGO_SETTINGS_MODULE=bugsink_conf PYTHONPATH=/opt/bugsink/conf:/opt/bugsink/lib/python3.10/site-packages /opt/bugsink/bin/python -m django create_auth_token' ``` ### Production Token Generation > **Note**: User executes this command on the server. ```bash cd /opt/bugsink && bugsink-manage create_auth_token ``` --- ## Common Troubleshooting ### Container Won't Start ```bash # Check logs podman logs flyer-crawler-dev # Inspect container podman inspect flyer-crawler-dev # Remove and recreate podman rm -f flyer-crawler-dev # Then recreate with docker-compose or podman run ``` ### Database Connection Issues ```bash # Test connection inside container podman exec -it flyer-crawler-dev psql -U postgres -d flyer_crawler_dev -c "SELECT 1" # Check if PostgreSQL is running podman exec -it flyer-crawler-dev pg_isready ``` ### PM2 App Keeps Restarting ```bash # Check logs pm2 logs flyer-crawler-api --err --lines 100 # Check for memory issues pm2 monit # View app details pm2 show flyer-crawler-api ``` ### Redis Connection Issues ```bash # Test Redis inside container podman exec -it flyer-crawler-dev redis-cli ping # Check Redis logs podman logs flyer-crawler-redis ```