25 KiB
25 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, user_setup, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | user_setup | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 07-config-consolidation | 03 | execute | 2 |
|
|
true |
|
|
Purpose: Implement D-05 (Python scripts with YAML parsing) for modern deployment infrastructure.
Output: 4 Python scripts in scripts/ folder with full deployment functionality and YAML config support.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/07-config-consolidation/07-CONTEXT.md @PROJECT_ARCHITECTURE.md @DEPLOYMENT.md @deploy.sh @run_standalone.sh @install_service.sh @export_prod.sh @config/backend.yaml.example @config/docker.yaml.example @docker-compose.yml Task 1: Create scripts/deploy.py (Docker deployment with YAML config) scripts/deploy.py - deploy.sh (current bash implementation to port) - docker-compose.yml (structure and environment variables) - config/docker.yaml.example (schema) - config/backend.yaml.example (config structure) - DEPLOYMENT.md (deployment procedure documentation) Create scripts/deploy.py to replace deploy.sh with Python implementation (per D-05).**Key features:**
1. **Script signature and usage:**
```bash
python3 scripts/deploy.py [production|staging|development] [--rebuild]
```
2. **Core functionality:**
- Pre-flight checks: Docker, Docker Compose, docker-compose.yml, config files
- Load config from config/docker.yaml and config/network.yaml
- Port availability checks (from network.yaml: backend_port, frontend_port, etc.)
- Environment file validation (config/backend.yaml exists and has required values)
- Docker Compose up with appropriate flags (rebuild if --rebuild)
- Health check polling (curl to /health endpoints)
- Deployment report (services running, ports, access URLs)
3. **Config file parsing:**
- Use PyYAML to load config/docker.yaml (for container resource limits, image names)
- Use PyYAML to load config/network.yaml (for port numbers and SSL settings)
- Use PyYAML to load config/backend.yaml (to validate required values)
- Fallback to sensible defaults if config files missing (but log warnings)
4. **Pre-flight checks (Step 1-5):**
- [ ] docker command available
- [ ] docker-compose command available
- [ ] docker-compose.yml exists
- [ ] config/backend.yaml exists (with helpful error if missing)
- [ ] config/network.yaml exists (with helpful error if missing)
5. **Port availability check (Step 6):**
- Read backend_port, frontend_port, backend_ssl_port, frontend_ssl_port from network.yaml
- Use netstat or ss to check if ports are in use
- Error if ports occupied, suggest alternatives
6. **Environment validation (Step 7):**
- Check config/backend.yaml for required values: JWT_SECRET_KEY, primary_ai_provider
- Warn if API keys are placeholders
- Proceed with warning (not error) for optional values
7. **Docker Compose deployment (Step 8-9):**
- Run `docker-compose up -d` (or with --build if --rebuild flag)
- Capture and display output with color codes
- Catch errors and provide helpful debugging steps
8. **Health checks (Step 10-11):**
- Poll backend health: `curl http://localhost:{backend_port}/health` (retry logic)
- Poll frontend health: `curl http://localhost:{frontend_port}/` (retry logic)
- Wait up to 2 minutes for services to become healthy
- Display health status to user
9. **Deployment report (Step 12):**
- Display service status: `docker-compose ps`
- Display access URLs:
- Frontend: http://localhost:{frontend_port}
- Backend API: http://localhost:{backend_port}/docs
- HTTPS: https://localhost:{frontend_ssl_port} (if SSL enabled in network.yaml)
- Display next steps (logs, troubleshooting, etc.)
10. **Error handling:**
- Descriptive error messages with suggested fixes
- Log all actions and results to stdout/stderr
- Use color output (GREEN for success, RED for errors, YELLOW for warnings, BLUE for info)
- Exit codes: 0 for success, 1 for fatal error
11. **Logging:**
- Use Python logging module (not print)
- Log level: INFO by default, DEBUG if --verbose flag
- Each step logged: "Step N/M: Description..."
- Results logged at end: "Deployment complete, services healthy"
12. **Required libraries:**
- sys, os, subprocess, time, socket (built-in)
- yaml (PyYAML)
- argparse (for CLI args)
- logging (for logging)
- No external deployment libraries (keep it simple)
13. **Make executable:** `chmod +x scripts/deploy.py` and include shebang: `#!/usr/bin/env python3`
- `test -f scripts/deploy.py && head -1 scripts/deploy.py | grep -q "python3"` (shebang present)
- `test -x scripts/deploy.py` (executable)
- `python3 -m py_compile scripts/deploy.py` (valid Python syntax)
- `python3 scripts/deploy.py --help | grep -q "deployment"` (help works)
- `grep -q "import yaml" scripts/deploy.py` (PyYAML imported)
- `grep -q "config/docker.yaml\|config/network.yaml" scripts/deploy.py` (loads config files)
- `grep -q "docker-compose" scripts/deploy.py` (calls docker-compose)
- `grep -q "curl.*health" scripts/deploy.py` (health checks present)
scripts/deploy.py created with Docker deployment, YAML config parsing, health checks, and error handling.
Task 2: Create scripts/run_standalone.py (Standalone launcher with YAML config)
scripts/run_standalone.py
- run_standalone.sh (current bash implementation to port)
- config/backend.yaml.example (schema)
- config/frontend.yaml.example (schema)
- backend/main.py (backend entry point)
- frontend package.json or next.config.js (frontend startup)
Create scripts/run_standalone.py to replace run_standalone.sh with Python implementation (per D-05).
**Key features:**
1. **Script signature:**
```bash
python3 scripts/run_standalone.py [--backend-only|--frontend-only]
```
2. **Core functionality:**
- Load config from config/backend.yaml and config/frontend.yaml
- Start FastAPI backend (uvicorn)
- Start Next.js frontend (npm run dev or node server.js)
- Display console output from both processes
- Handle shutdown gracefully (SIGTERM/SIGINT kills both services)
- Display health status and access URLs
3. **Config file parsing:**
- Load config/backend.yaml to get: backend_port, data_dir, logs_dir, log_level
- Load config/frontend.yaml to get: frontend_port, backend_url
- Use defaults if config files missing (with warnings)
4. **Backend startup (--backend-only or default):**
- Command: `uvicorn backend.main:app --host 0.0.0.0 --port {backend_port} --reload`
- Set environment: DATA_DIR, LOGS_DIR, LOG_LEVEL (from config)
- Capture output and display with [BACKEND] prefix
- Wait for backend to log "Uvicorn running on..." or similar
- Verify backend is listening on backend_port
5. **Frontend startup (--frontend-only or default):**
- Command: `npm run dev` (if in development) or `node server.js` (if built)
- Set environment: NEXT_PUBLIC_API_URL (from config:frontend:backend_url)
- Capture output and display with [FRONTEND] prefix
- Wait for frontend to log "ready - started server on..." or similar
- Verify frontend is listening on frontend_port
6. **Process management:**
- Use subprocess.Popen with shell=False (for security)
- Manage both processes in list/tuple
- Handle SIGTERM/SIGINT (Ctrl+C) to kill both processes
- Display "Shutting down..." and wait for clean shutdown
- Exit with code 0 if both shut down cleanly
7. **Health monitoring:**
- Periodically check if processes are alive (poll returncode)
- If one process dies, log error and optionally shutdown other (per config flag)
- Display uptime and status every 30 seconds
8. **Logging and output:**
- Use Python logging module
- Log each process with [BACKEND] / [FRONTEND] prefix
- Merge stdout/stderr from both processes to terminal
- Show final status: "Backend running on http://localhost:{backend_port}, Frontend on http://localhost:{frontend_port}"
9. **Error handling:**
- If uvicorn not installed, error and suggest: `pip install uvicorn`
- If npm not found, error and suggest: install Node.js
- If ports already in use, error with port number
- If config files missing, log warnings but use defaults
10. **Required libraries:**
- sys, os, subprocess, signal, time (built-in)
- yaml (PyYAML)
- argparse (for CLI args --backend-only, --frontend-only)
- logging (for logging)
11. **Make executable:** `chmod +x scripts/run_standalone.py` with shebang: `#!/usr/bin/env python3`
- `test -f scripts/run_standalone.py && head -1 scripts/run_standalone.py | grep -q "python3"` (shebang present)
- `test -x scripts/run_standalone.py` (executable)
- `python3 -m py_compile scripts/run_standalone.py` (valid Python syntax)
- `grep -q "import yaml" scripts/run_standalone.py` (PyYAML imported)
- `grep -q "config/backend.yaml\|config/frontend.yaml" scripts/run_standalone.py` (loads config)
- `grep -q "uvicorn\|subprocess.Popen" scripts/run_standalone.py` (backend startup present)
- `grep -q "npm\|node server" scripts/run_standalone.py` (frontend startup present)
- `grep -q "signal.signal\|SIGTERM" scripts/run_standalone.py` (signal handling present)
scripts/run_standalone.py created with backend/frontend startup, YAML config parsing, process management, and graceful shutdown.
Task 3: Create scripts/install_service.py (Systemd service installation)
scripts/install_service.py
- install_service.sh (current bash implementation to port)
- inventory.service.template (systemd service template)
- config/backend.yaml.example (to understand config structure)
- config/network.yaml.example (for port information)
Create scripts/install_service.py to replace install_service.sh with Python implementation (per D-05).
**Key features:**
1. **Script signature:**
```bash
sudo python3 scripts/install_service.py [--user=service_user] [--port=port]
```
2. **Core functionality:**
- Read inventory.service.template (or create template inline)
- Load config from config/backend.yaml (for paths, ports)
- Generate systemd service file with correct paths and user/group
- Install service file to /etc/systemd/system/ainventory.service
- Enable service (systemctl enable)
- Display installation summary and next steps
3. **Config file parsing:**
- Load config/backend.yaml to get: data_dir, logs_dir
- Load config/network.yaml to get: backend_port (for documentation)
- Use defaults if missing
4. **Service file generation:**
- Read inventory.service.template
- Replace placeholders:
- {PROJECT_DIR}: current working directory (project root)
- {SERVICE_USER}: service user (default: www-data, configurable via --user)
- {BACKEND_PORT}: from config/network.yaml
- {DATA_DIR}: from config/backend.yaml
- {LOGS_DIR}: from config/backend.yaml
- Template should:
- Type=simple
- ExecStart=/usr/bin/python3 {PROJECT_DIR}/scripts/run_standalone.py --backend-only
- WorkingDirectory={PROJECT_DIR}
- User={SERVICE_USER}
- Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
- Restart=on-failure
- RestartSec=10
5. **Permission checks:**
- Require sudo/root: `if os.getuid() != 0: error("Must run with sudo")`
- Check project directory is readable
- Check data_dir and logs_dir exist or can be created
6. **Service file installation:**
- Write service file to /etc/systemd/system/ainventory.service
- Set permissions: 644 (readable, not writable by non-root)
- Run `systemctl daemon-reload`
- Run `systemctl enable ainventory.service`
- Optionally start service: `systemctl start ainventory.service`
7. **Status display:**
- Show service file location
- Show service user and group
- Show project directory
- Show next steps: `systemctl start ainventory`, `systemctl status ainventory`
- Show logs: `journalctl -u ainventory -f`
8. **Error handling:**
- Check if service already installed (offer --force to overwrite)
- Check if user exists (suggest: `useradd -r -s /bin/false {user}`)
- Check if directories are writable
- Descriptive errors with suggested fixes
9. **Required libraries:**
- sys, os, subprocess, pwd, grp (built-in)
- yaml (PyYAML)
- argparse (for CLI args)
- logging (for logging)
10. **Make executable:** `chmod +x scripts/install_service.py` with shebang: `#!/usr/bin/env python3`
- `test -f scripts/install_service.py && head -1 scripts/install_service.py | grep -q "python3"` (shebang present)
- `test -x scripts/install_service.py` (executable)
- `python3 -m py_compile scripts/install_service.py` (valid Python syntax)
- `grep -q "import yaml" scripts/install_service.py` (PyYAML imported)
- `grep -q "config/backend.yaml\|config/network.yaml" scripts/install_service.py` (loads config)
- `grep -q "/etc/systemd/system\|systemctl" scripts/install_service.py` (systemd integration present)
- `grep -q "os.getuid\|sudo" scripts/install_service.py` (permission check present)
scripts/install_service.py created with systemd service generation, config parsing, and installation logic.
Task 4: Create scripts/export_prod.py (Production export/backup)
scripts/export_prod.py
- export_prod.sh (current bash implementation to port)
- config/backend.yaml.example (for data_dir)
- DEPLOYMENT.md (backup procedures)
Create scripts/export_prod.py to replace export_prod.sh with Python implementation (per D-05).
**Key features:**
1. **Script signature:**
```bash
python3 scripts/export_prod.py [--output=/path/to/backup.tar.gz] [--include-logs]
```
2. **Core functionality:**
- Load config from config/backend.yaml (to find data_dir, logs_dir)
- Create tar.gz archive of production data
- Include database file(s), config files (no secrets), and optionally logs
- Output to specified location or default: backups/{timestamp}.tar.gz
- Display archive size and location
3. **Config file parsing:**
- Load config/backend.yaml to get: data_dir, logs_dir
- Use defaults if missing: data_dir=./data, logs_dir=./logs
4. **Archive creation:**
- Include: {data_dir}/* (all application data, database, etc.)
- Include: config/*.yaml.example (config templates)
- Include: config/backend.yaml, config/frontend.yaml, config/network.yaml (actual configs, no secrets)
- Include: config/secrets.yaml.example (secrets template only, NOT actual secrets.yaml)
- Include: logs/* (optional, if --include-logs flag)
- Exclude: config/secrets.yaml (never backup actual secrets)
- Exclude: node_modules/, __pycache__/, .git/, .venv/
- Exclude: temporary files, cache
5. **Archive naming:**
- Default: backups/ainventory_{timestamp}.tar.gz
- Timestamp format: YYYY-MM-DD_HH-MM-SS
- Custom path via --output flag
6. **Backup directory:**
- Create backups/ directory if not exists
- Set directory permissions: 750 (rwxr-x---)
7. **Verification:**
- Verify tar.gz was created successfully
- Display archive size: X.XX MB
- Display archive contents summary: "Includes database, data, and config (secrets excluded)"
8. **Error handling:**
- If data_dir doesn't exist, error and suggest creating it
- If no write permission to backups/, error and suggest location
- If tar command fails, show error and suggest troubleshooting
9. **Output example:**
```
[INFO] Loading config from config/backend.yaml
[INFO] Data directory: ./data
[INFO] Creating backup...
[INFO] Archive created: backups/ainventory_2026-04-23_14-30-45.tar.gz
[INFO] Archive size: 125.43 MB
[INFO] Contents: database, data, config (secrets excluded)
[INFO] Backup complete!
```
10. **Required libraries:**
- sys, os, subprocess, datetime, tarfile (built-in)
- yaml (PyYAML)
- argparse (for CLI args)
- logging (for logging)
11. **Make executable:** `chmod +x scripts/export_prod.py` with shebang: `#!/usr/bin/env python3`
- `test -f scripts/export_prod.py && head -1 scripts/export_prod.py | grep -q "python3"` (shebang present)
- `test -x scripts/export_prod.py` (executable)
- `python3 -m py_compile scripts/export_prod.py` (valid Python syntax)
- `grep -q "import yaml" scripts/export_prod.py` (PyYAML imported)
- `grep -q "config/backend.yaml" scripts/export_prod.py` (loads config)
- `grep -q "tarfile\|tar.gz" scripts/export_prod.py` (tar archive creation present)
- `grep -q "secrets.yaml" scripts/export_prod.py | grep -q "exclude"` (secrets excluded from backup)
scripts/export_prod.py created with production data export, YAML config parsing, archive creation, and secrets exclusion.
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| User input → Script | Script arguments and config files must be validated |
| Script → System | Scripts execute system commands (subprocess) — must escape/quote properly |
| Script → Network | Health checks make HTTP requests (must handle timeouts) |
| Script → Filesystem | Scripts read/write files (must respect permissions) |
STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|---|---|---|---|---|
| T-07-09 | Injection | deploy.py subprocess | mitigate | Use subprocess with shell=False and list args (not f-strings). Example: subprocess.run(["docker-compose", "up", "-d"], ...) not subprocess.run(f"docker-compose up -d", shell=True). |
| T-07-10 | Elevation of Privilege | install_service.py sudo | mitigate | Check os.getuid() != 0 at start. Require sudo for systemd operations only. Log all systemctl calls. |
| T-07-11 | Information Disclosure | export_prod.py backup | mitigate | Exclude config/secrets.yaml explicitly in tarfile. Log what is excluded. Verify file permissions (backups/ dir 750). |
| T-07-12 | Denial of Service | Health checks timeout | mitigate | Set socket timeout to 10 seconds. Limit retry attempts to 12 (2 minutes total). Log timeout errors. |
</threat_model>
**Phase 7, Plan 3 Verification Checklist:**-
scripts/deploy.py
- File exists and is executable
- Shebang present:
#!/usr/bin/env python3 - Loads config/docker.yaml and config/network.yaml
- Pre-flight checks for Docker, Docker Compose, config files
- Port availability checks implemented
- Health checks poll backend and frontend endpoints
- Color output for info/warning/success/error
- Displays deployment summary and access URLs
- Valid Python syntax
-
scripts/run_standalone.py
- File exists and is executable
- Shebang present:
#!/usr/bin/env python3 - Loads config/backend.yaml and config/frontend.yaml
- Launches uvicorn for backend with correct port and settings
- Launches frontend (npm dev or node server.js) with correct port
- Signal handling (SIGTERM/SIGINT) for clean shutdown
- Process monitoring and output display with prefixes
- Valid Python syntax
-
scripts/install_service.py
- File exists and is executable
- Shebang present:
#!/usr/bin/env python3 - Checks for sudo/root permission
- Loads config/backend.yaml and config/network.yaml
- Generates systemd service file from template
- Replaces placeholders: {PROJECT_DIR}, {SERVICE_USER}, {BACKEND_PORT}, etc.
- Installs to /etc/systemd/system/ with correct permissions
- Runs systemctl daemon-reload and enable
- Valid Python syntax
-
scripts/export_prod.py
- File exists and is executable
- Shebang present:
#!/usr/bin/env python3 - Loads config/backend.yaml to find data_dir
- Creates tar.gz archive with data and config files
- Excludes config/secrets.yaml (actual secrets, not example)
- Includes config/*.yaml.example files
- Output to backups/{timestamp}.tar.gz or custom path
- Displays archive size and summary
- Valid Python syntax
-
Security (subprocess, permissions, file ops)
- All subprocess calls use shell=False with list args
- No f-strings in shell commands
- File operations respect umask/permissions
- No hardcoded credentials in scripts
-
Integration
- Each script loads YAML config files correctly
- Scripts reference new config/ structure (not inventory.env)
- Error messages are helpful and actionable
<success_criteria>
- 4 Python scripts created (deploy.py, run_standalone.py, install_service.py, export_prod.py)
- All scripts use PyYAML to parse config files
- All scripts are executable with proper shebangs
- deploy.py handles Docker deployment with health checks
- run_standalone.py launches backend and frontend without Docker
- install_service.py creates systemd service with new config paths
- export_prod.py exports production data excluding secrets
- All subprocess calls use shell=False (secure)
- Error handling and logging present in all scripts
- Scripts integrate with new config/ structure (D-05, D-06, D-07) </success_criteria>