752 lines
21 KiB
Markdown
752 lines
21 KiB
Markdown
# Environment Detection Guide
|
|
|
|
This document defines **environment.json** — the environment contract between ecl-harness-engineer and harness-executor — and explains how to detect, collect, and generate environment information for a project.
|
|
|
|
> **Key Insight**: Environment is not just configuration — it's the complete runtime ecosystem including databases, services, secrets, and the **executable scripts** to bring it all up.
|
|
|
|
---
|
|
|
|
## 1. environment.json Overview
|
|
|
|
### 1.1 Purpose
|
|
|
|
| File | Purpose | Consumer |
|
|
|------|---------|----------|
|
|
| `environment.json` | "What the environment is" — full ecosystem description | `preflight.py`, `verifier subagent`, setup scripts |
|
|
|
|
`ecl-harness-engineer` creates `environment.json` only. Runtime verification plans such as
|
|
`verify.json` are generated later by harness-executor or another runtime from `environment.json`
|
|
plus the active task context.
|
|
|
|
### 1.2 Location
|
|
|
|
```
|
|
harness/
|
|
├── config/
|
|
│ └── environment.json # Environment ecosystem contract
|
|
└── scripts/
|
|
├── setup-env.sh # Start dependencies (DB, Redis, etc.)
|
|
├── start-server.sh # Start the application
|
|
├── teardown-env.sh # Stop and cleanup
|
|
└── seed-data.sh # Seed test data
|
|
```
|
|
|
|
### 1.3 When to Generate
|
|
|
|
| Mode | Trigger |
|
|
|------|---------|
|
|
| **Greenfield** | Always generate (scaffold includes environment.json) |
|
|
| **Create** | Always generate (detected from code analysis) |
|
|
| **Improve** | Generate if missing; audit and update if exists |
|
|
|
|
---
|
|
|
|
## 2. environment.json Schema
|
|
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"project_name": "my-project",
|
|
"generated_at": "2026-03-27T10:00:00Z",
|
|
"generated_by": "ecl-harness-engineer",
|
|
|
|
"runtime": {
|
|
"language": "go | typescript | python | java",
|
|
"version": "1.22+ | 20+ | 3.11+ | 21+",
|
|
"package_manager": "npm | pnpm | yarn | pip | poetry | maven | gradle",
|
|
"build_command": "go build ./... | npm run build | ...",
|
|
"dev_command": "go run cmd/server/main.go | npm run dev | ..."
|
|
},
|
|
|
|
"databases": [
|
|
{
|
|
"name": "primary_db",
|
|
"type": "postgres | mysql | mongodb | redis | sqlite",
|
|
"purpose": "Main application data store",
|
|
"required": true,
|
|
"connection": {
|
|
"host_env": "DB_HOST",
|
|
"port_env": "DB_PORT",
|
|
"default_port": 5432,
|
|
"user_env": "DB_USER",
|
|
"password_env": "DB_PASSWORD",
|
|
"database_env": "DB_NAME",
|
|
"url_env": "DATABASE_URL"
|
|
},
|
|
"setup": {
|
|
"docker_image": "postgres:16",
|
|
"docker_compose_service": "postgres",
|
|
"migration_command": "go run cmd/migrate/main.go up",
|
|
"seed_command": "go run cmd/seed/main.go"
|
|
},
|
|
"test_alternatives": {
|
|
"sqlite_in_memory": "DB_DRIVER=sqlite3 DB_URL=:memory:",
|
|
"docker": "docker run -d --name test-pg -p 5433:5432 -e POSTGRES_PASSWORD=test postgres:16"
|
|
}
|
|
}
|
|
],
|
|
|
|
"services": [
|
|
{
|
|
"name": "redis_cache",
|
|
"type": "redis | http | grpc | kafka | rabbitmq | s3",
|
|
"purpose": "Session storage and caching",
|
|
"required": false,
|
|
"connection": {
|
|
"url_env": "REDIS_URL",
|
|
"default_url": "redis://localhost:6379"
|
|
},
|
|
"setup": {
|
|
"docker_image": "redis:7",
|
|
"docker_compose_service": "redis"
|
|
},
|
|
"fallback": "In-memory cache used when Redis unavailable"
|
|
},
|
|
{
|
|
"name": "auth_service",
|
|
"type": "http",
|
|
"purpose": "External authentication provider",
|
|
"required": true,
|
|
"connection": {
|
|
"url_env": "AUTH_SERVICE_URL",
|
|
"health_endpoint": "/health"
|
|
},
|
|
"test_alternatives": {
|
|
"mock": "AUTH_SERVICE_URL=http://localhost:9999 (use mock server in test/mock/auth.go)"
|
|
}
|
|
}
|
|
],
|
|
|
|
"secrets": [
|
|
{
|
|
"name": "JWT_SECRET",
|
|
"purpose": "Signs JWT tokens for authentication",
|
|
"required": true,
|
|
"test_value_ok": true,
|
|
"test_value": "test-jwt-secret-not-for-production"
|
|
},
|
|
{
|
|
"name": "STRIPE_SECRET_KEY",
|
|
"purpose": "Payment processing",
|
|
"required": false,
|
|
"test_value_ok": false,
|
|
"skip_when_missing": "Payment features disabled in test mode"
|
|
}
|
|
],
|
|
|
|
"ports": [
|
|
{
|
|
"name": "http_server",
|
|
"env": "PORT",
|
|
"default": 8080,
|
|
"test_port": 8081,
|
|
"purpose": "Main HTTP server"
|
|
}
|
|
],
|
|
|
|
"files": {
|
|
"required": [
|
|
{ "path": ".env", "template": ".env.example", "purpose": "Local env config" }
|
|
],
|
|
"generated": [
|
|
{ "path": "internal/generated/schema.go", "command": "go generate ./...", "purpose": "Generated code" }
|
|
]
|
|
},
|
|
|
|
"functional_scenarios": [
|
|
{
|
|
"name": "user_registration_flow",
|
|
"description": "Register a new user, verify data stored, login with credentials",
|
|
"requires": ["primary_db", "JWT_SECRET"],
|
|
"category": "auth",
|
|
"steps_hint": [
|
|
"POST /api/v1/register with valid user data -> 201",
|
|
"GET /api/v1/users/{id} -> 200 with matching data",
|
|
"POST /api/v1/login with same credentials -> 200 with JWT token",
|
|
"GET /api/v1/profile with JWT token -> 200 with user info"
|
|
],
|
|
"priority": "high"
|
|
}
|
|
],
|
|
|
|
"test_environment": {
|
|
"env_vars": {
|
|
"ENV": "test",
|
|
"LOG_LEVEL": "error",
|
|
"PORT": "8081",
|
|
"DB_DRIVER": "sqlite3",
|
|
"DB_URL": ":memory:",
|
|
"JWT_SECRET": "test-jwt-secret-not-for-production"
|
|
},
|
|
"setup_commands": ["go mod download", "go generate ./..."],
|
|
"teardown_commands": []
|
|
},
|
|
|
|
"scripts": {
|
|
"setup_env": "harness/scripts/setup-env.sh",
|
|
"start_server": "harness/scripts/start-server.sh",
|
|
"teardown_env": "harness/scripts/teardown-env.sh",
|
|
"seed_data": "harness/scripts/seed-data.sh"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Environment Scripts Generation
|
|
|
|
Beyond the JSON configuration, ecl-harness-engineer must generate **executable scripts** that actually bring up the environment.
|
|
|
|
### 3.1 Script Templates
|
|
|
|
#### `harness/scripts/setup-env.sh`
|
|
|
|
Sets up all dependencies (databases, external services) using Docker or local services.
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# setup-env.sh - Start all required dependencies for local development
|
|
# Generated by ecl-harness-engineer from environment.json
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
|
cd "$PROJECT_ROOT"
|
|
|
|
echo "==> Setting up environment for ${PROJECT_NAME}..."
|
|
|
|
# --- Database: ${DB_NAME} ---
|
|
{{#if databases}}
|
|
{{#each databases}}
|
|
{{#if (eq type "postgres")}}
|
|
if ! docker ps -q -f name={{name}} | grep -q .; then
|
|
echo "Starting PostgreSQL ({{name}})..."
|
|
docker run -d \
|
|
--name {{name}} \
|
|
-p 127.0.0.1:{{connection.default_port}}:5432 \
|
|
-e POSTGRES_USER=${{{connection.user_env}}:-postgres} \
|
|
-e POSTGRES_PASSWORD=${{{connection.password_env}}:-postgres} \
|
|
-e POSTGRES_DB=${{{connection.database_env}}:-{{../project_name}}} \
|
|
{{setup.docker_image}}
|
|
echo "Waiting for PostgreSQL to be ready..."
|
|
sleep 3
|
|
until docker exec {{name}} pg_isready -U postgres > /dev/null 2>&1; do
|
|
sleep 1
|
|
done
|
|
echo "PostgreSQL ready."
|
|
fi
|
|
{{/if}}
|
|
{{#if (eq type "mysql")}}
|
|
if ! docker ps -q -f name={{name}} | grep -q .; then
|
|
echo "Starting MySQL ({{name}})..."
|
|
docker run -d \
|
|
--name {{name}} \
|
|
-p 127.0.0.1:{{connection.default_port}}:3306 \
|
|
-e MYSQL_ROOT_PASSWORD=${{{connection.password_env}}:-root} \
|
|
-e MYSQL_DATABASE=${{{connection.database_env}}:-{{../project_name}}} \
|
|
{{setup.docker_image}}
|
|
echo "Waiting for MySQL to be ready..."
|
|
sleep 5
|
|
until docker exec {{name}} mysqladmin ping -h localhost --silent; do
|
|
sleep 1
|
|
done
|
|
echo "MySQL ready."
|
|
fi
|
|
{{/if}}
|
|
{{/each}}
|
|
{{/if}}
|
|
|
|
# --- Services ---
|
|
{{#if services}}
|
|
{{#each services}}
|
|
{{#if (eq type "redis")}}
|
|
if ! docker ps -q -f name={{name}} | grep -q .; then
|
|
echo "Starting Redis ({{name}})..."
|
|
docker run -d --name {{name}} -p 127.0.0.1:6379:6379 {{setup.docker_image}}
|
|
echo "Redis started."
|
|
fi
|
|
{{/if}}
|
|
{{/each}}
|
|
{{/if}}
|
|
|
|
# --- Run migrations ---
|
|
{{#if databases}}
|
|
{{#each databases}}
|
|
{{#if setup.migration_command}}
|
|
echo "Running migrations for {{name}}..."
|
|
{{setup.migration_command}}
|
|
{{/if}}
|
|
{{/each}}
|
|
{{/if}}
|
|
|
|
echo "==> Environment setup complete!"
|
|
```
|
|
|
|
#### `harness/scripts/start-server.sh`
|
|
|
|
Starts the application with the correct environment variables.
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# start-server.sh - Start the application server
|
|
# Generated by ecl-harness-engineer from environment.json
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
|
cd "$PROJECT_ROOT"
|
|
|
|
# Load .env if exists
|
|
if [ -f .env ]; then
|
|
export $(grep -v '^#' .env | xargs)
|
|
fi
|
|
|
|
# Set defaults from environment.json
|
|
{{#each test_environment.env_vars}}
|
|
export {{@key}}=${{{@key}}:-{{this}}}
|
|
{{/each}}
|
|
|
|
# Build if needed
|
|
{{#if runtime.build_command}}
|
|
echo "Building..."
|
|
{{runtime.build_command}}
|
|
{{/if}}
|
|
|
|
# Start the server
|
|
echo "Starting server on port ${PORT:-8080}..."
|
|
{{runtime.dev_command}}
|
|
```
|
|
|
|
#### `harness/scripts/teardown-env.sh`
|
|
|
|
Stops and cleans up all dependencies.
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# teardown-env.sh - Stop and cleanup environment
|
|
# Generated by ecl-harness-engineer from environment.json
|
|
set -e
|
|
|
|
echo "==> Tearing down environment..."
|
|
|
|
{{#if databases}}
|
|
{{#each databases}}
|
|
docker stop {{name}} 2>/dev/null || true
|
|
docker rm {{name}} 2>/dev/null || true
|
|
{{/each}}
|
|
{{/if}}
|
|
|
|
{{#if services}}
|
|
{{#each services}}
|
|
{{#if setup.docker_image}}
|
|
docker stop {{name}} 2>/dev/null || true
|
|
docker rm {{name}} 2>/dev/null || true
|
|
{{/if}}
|
|
{{/each}}
|
|
{{/if}}
|
|
|
|
echo "==> Teardown complete."
|
|
```
|
|
|
|
#### `harness/scripts/seed-data.sh`
|
|
|
|
Seeds the database with test data.
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# seed-data.sh - Seed database with test data
|
|
# Generated by ecl-harness-engineer from environment.json
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
|
cd "$PROJECT_ROOT"
|
|
|
|
{{#if databases}}
|
|
{{#each databases}}
|
|
{{#if setup.seed_command}}
|
|
echo "Seeding {{name}}..."
|
|
{{setup.seed_command}}
|
|
{{/if}}
|
|
{{/each}}
|
|
{{/if}}
|
|
|
|
echo "==> Seeding complete."
|
|
```
|
|
|
|
### 3.2 When to Generate Scripts
|
|
|
|
| Scenario | Generate Scripts? |
|
|
|----------|-------------------|
|
|
| Greenfield mode | Yes, always (part of scaffold) |
|
|
| Create mode with detected DB/services | Yes |
|
|
| Create mode with no dependencies | Minimal (just start-server.sh) |
|
|
| Improve mode, scripts missing | Yes, if dependencies detected |
|
|
| Improve mode, scripts exist | Keep existing, suggest updates if outdated |
|
|
|
|
### 3.3 Script vs docker-compose.yml
|
|
|
|
If project already has `docker-compose.yml`:
|
|
- **Do not generate duplicate scripts** that replicate docker-compose functionality
|
|
- Instead, generate thin wrapper scripts that call `docker-compose up -d` etc.
|
|
- Reference the docker-compose service names in environment.json
|
|
|
|
```bash
|
|
# setup-env.sh when docker-compose.yml exists
|
|
#!/bin/bash
|
|
set -e
|
|
docker-compose up -d postgres redis
|
|
echo "Waiting for services..."
|
|
sleep 5
|
|
docker-compose exec postgres pg_isready
|
|
echo "Services ready."
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Environment Detection Strategies
|
|
|
|
### 4.1 Code Dependency Analysis
|
|
|
|
Scan dependency files to detect what the project uses:
|
|
|
|
| Pattern | Detection | Implies |
|
|
|---------|-----------|---------|
|
|
| `go.mod`: `github.com/lib/pq`, `github.com/jackc/pgx` | PostgreSQL | Database, connection env vars |
|
|
| `go.mod`: `github.com/go-redis/redis` | Redis | Cache service |
|
|
| `package.json`: `pg`, `mysql2`, `mongoose` | DB drivers | Database dependencies |
|
|
| `package.json`: `@aws-sdk/*` | AWS services | Cloud service credentials |
|
|
| `requirements.txt`: `psycopg2`, `sqlalchemy` | PostgreSQL | Database |
|
|
| `requirements.txt`: `boto3` | AWS | Cloud credentials |
|
|
|
|
**Go detection patterns:**
|
|
```go
|
|
// Scan import statements
|
|
"github.com/lib/pq" // PostgreSQL
|
|
"github.com/jackc/pgx" // PostgreSQL
|
|
"github.com/go-sql-driver/mysql" // MySQL
|
|
"go.mongodb.org/mongo-driver" // MongoDB
|
|
"github.com/go-redis/redis" // Redis
|
|
"github.com/nats-io/nats.go" // NATS
|
|
"github.com/segmentio/kafka-go" // Kafka
|
|
```
|
|
|
|
**TypeScript/JavaScript patterns:**
|
|
```javascript
|
|
// package.json dependencies
|
|
"pg" // PostgreSQL
|
|
"mysql2" // MySQL
|
|
"mongodb" // MongoDB
|
|
"ioredis" // Redis
|
|
"kafkajs" // Kafka
|
|
"@aws-sdk/*" // AWS services
|
|
```
|
|
|
|
**Python patterns:**
|
|
```python
|
|
# requirements.txt or pyproject.toml
|
|
psycopg2 # PostgreSQL
|
|
mysql-connector-python # MySQL
|
|
pymongo # MongoDB
|
|
redis # Redis
|
|
boto3 # AWS
|
|
```
|
|
|
|
### 4.2 Environment Variable Collection
|
|
|
|
Scan code for all environment variable references:
|
|
|
|
```go
|
|
// Go
|
|
os.Getenv("DB_HOST")
|
|
os.LookupEnv("REDIS_URL")
|
|
viper.GetString("jwt.secret") // with config binding
|
|
|
|
// TypeScript
|
|
process.env.DB_HOST
|
|
config.get('database.url')
|
|
|
|
// Python
|
|
os.environ.get("DB_HOST")
|
|
os.getenv("REDIS_URL")
|
|
```
|
|
|
|
Also check:
|
|
- `.env.example` / `.env.template` for expected variables
|
|
- Config struct definitions (Go struct tags, TypeScript interfaces)
|
|
- README mentions of required environment variables
|
|
|
|
### 4.3 Functional Scenario Inference
|
|
|
|
Analyze routes to infer functional scenarios:
|
|
|
|
**Route patterns → Scenarios:**
|
|
|
|
| Detected Routes | Inferred Scenario |
|
|
|-----------------|-------------------|
|
|
| `POST /register`, `POST /login`, `GET /profile` | `user_auth_flow` |
|
|
| `POST /users`, `GET /users/:id`, `PUT /users/:id`, `DELETE /users/:id` | `user_crud` |
|
|
| `POST /orders`, `GET /orders`, middleware `auth` | `authenticated_orders_flow` |
|
|
| `GET /health`, `GET /ready` | `health_check` (always include) |
|
|
|
|
**Middleware analysis:**
|
|
- Auth middleware on routes → scenario needs authentication first
|
|
- Rate limit middleware → include rate limit boundary test
|
|
|
|
**Data model analysis:**
|
|
- User model with password field → auth scenarios
|
|
- Relations (User has Orders) → relational query scenarios
|
|
|
|
### 4.4 Docker/K8s Manifest Analysis
|
|
|
|
If `docker-compose.yml` or Kubernetes manifests exist:
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
postgres:
|
|
image: postgres:16
|
|
ports:
|
|
- "127.0.0.1:5432:5432"
|
|
environment:
|
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
```
|
|
|
|
Extract:
|
|
- Service names → `databases[].setup.docker_compose_service`
|
|
- Images → `databases[].setup.docker_image`
|
|
- Port mappings → `databases[].connection.default_port`
|
|
- Environment variables → `databases[].connection.*_env`
|
|
|
|
---
|
|
|
|
## 5. Security Guidelines
|
|
|
|
### 5.1 Never Hardcode Secrets
|
|
|
|
```json
|
|
// WRONG - never do this
|
|
"secrets": [{ "value": "sk-abc123..." }]
|
|
|
|
// CORRECT - reference via environment variable
|
|
"secrets": [{ "name": "API_KEY", "purpose": "..." }]
|
|
```
|
|
|
|
### 5.2 Test Values
|
|
|
|
Only allow `test_value` for secrets that are:
|
|
- Self-contained (JWT signing key with no external dependency)
|
|
- Clearly marked as test-only
|
|
|
|
```json
|
|
{
|
|
"name": "JWT_SECRET",
|
|
"test_value_ok": true,
|
|
"test_value": "test-jwt-secret-not-for-production"
|
|
}
|
|
```
|
|
|
|
### 5.3 Sensitive Patterns
|
|
|
|
Detect and warn about:
|
|
- API keys: `sk-`, `pk-`, `api_`, `token_`
|
|
- Connection strings with passwords
|
|
- Private keys (RSA, EC)
|
|
|
|
When detected, prompt user to confirm before including in environment.json.
|
|
|
|
---
|
|
|
|
## 6. Integration with harness-executor
|
|
|
|
### 6.1 Relationship
|
|
|
|
ecl-harness-engineer generates `environment.json` to describe the runtime ecosystem. harness-executor consumes it at task runtime to dynamically generate `verify.json` for verification.
|
|
|
|
```
|
|
environment.json (ecl-harness-engineer) verify.json (harness-executor, runtime)
|
|
════════════════════════════════════ ═══════════════════════════════════════
|
|
databases[] prerequisites.database_checks[]
|
|
└─ auto-derived ─────────────────► (TCP connectivity)
|
|
|
|
services[] prerequisites.service_checks[]
|
|
└─ auto-derived ─────────────────► (HTTP health, TCP)
|
|
|
|
secrets[] prerequisites.env_checks[]
|
|
└─ auto-derived ─────────────────► (required env vars)
|
|
|
|
ports[] prerequisites.port_checks[]
|
|
└─ auto-derived ─────────────────► (port availability)
|
|
|
|
functional_scenarios[] (not in verify.json)
|
|
└─ consumed by ──────────────────► verifier subagent
|
|
```
|
|
|
|
> **Note**: ecl-harness-engineer does NOT generate `verify.json`. It only provides `environment.json` as the foundation. harness-executor dynamically generates `verify.json` at task runtime based on environment.json + task context.
|
|
|
|
### 6.2 Auto-Derivation Rules
|
|
|
|
When harness-executor generates `verify.json`, it automatically derives `prerequisites` from `environment.json`:
|
|
|
|
```python
|
|
def derive_prerequisites(env_config):
|
|
prereqs = {"database_checks": [], "service_checks": [], ...}
|
|
|
|
for db in env_config.get("databases", []):
|
|
if db["required"]:
|
|
prereqs["database_checks"].append({
|
|
"type": db["type"],
|
|
"host_env": db["connection"].get("host_env", "localhost"),
|
|
"port": db["connection"].get("default_port")
|
|
})
|
|
|
|
for svc in env_config.get("services", []):
|
|
if svc["required"]:
|
|
prereqs["service_checks"].append({
|
|
"type": svc["type"],
|
|
"url_env": svc["connection"].get("url_env"),
|
|
"health_endpoint": svc["connection"].get("health_endpoint")
|
|
})
|
|
|
|
for secret in env_config.get("secrets", []):
|
|
if secret["required"]:
|
|
prereqs["env_checks"].append({
|
|
"name": secret["name"],
|
|
"required": True
|
|
})
|
|
|
|
return prereqs
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Checklist for Modes
|
|
|
|
### 7.1 Greenfield Mode
|
|
|
|
- [ ] Generate `environment.json` with detected/default values
|
|
- [ ] Generate all four scripts (setup-env, start-server, teardown-env, seed-data)
|
|
- [ ] Include at least `health_check` functional scenario
|
|
- [ ] Set up `test_environment` with safe defaults
|
|
|
|
### 7.2 Create Mode
|
|
|
|
- [ ] Analyze codebase for dependencies (Section 4.1)
|
|
- [ ] Collect all environment variables (Section 4.2)
|
|
- [ ] Infer functional scenarios from routes (Section 4.3)
|
|
- [ ] Check existing docker-compose.yml (Section 4.4)
|
|
- [ ] Generate `environment.json`
|
|
- [ ] Generate scripts (or thin wrappers if docker-compose exists)
|
|
- [ ] Validate security (no hardcoded secrets)
|
|
|
|
### 7.3 Improve Mode
|
|
|
|
- [ ] Check if `environment.json` exists
|
|
- If missing: run Create mode detection and generate
|
|
- If exists: audit for completeness
|
|
- [ ] Audit checklist:
|
|
- [ ] All detected DB drivers have corresponding `databases[]` entry
|
|
- [ ] All required env vars from code are in `secrets[]` or `test_environment`
|
|
- [ ] `functional_scenarios[]` covers main user flows
|
|
- [ ] Scripts exist and are executable
|
|
- [ ] Scripts match `environment.json` (not outdated)
|
|
- [ ] Generate missing components
|
|
- [ ] Update outdated components
|
|
|
|
---
|
|
|
|
## 8. Example: Complete Go Web API
|
|
|
|
Given a Go web API with PostgreSQL and Redis:
|
|
|
|
**Detected from code:**
|
|
- `go.mod`: `github.com/jackc/pgx/v5`, `github.com/go-redis/redis/v9`
|
|
- Env vars: `DATABASE_URL`, `REDIS_URL`, `JWT_SECRET`, `PORT`
|
|
- Routes: `/api/v1/register`, `/api/v1/login`, `/api/v1/users/:id`, `/health`
|
|
- Auth middleware on `/api/v1/users/:id`
|
|
|
|
**Generated environment.json:**
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"project_name": "my-api",
|
|
"runtime": {
|
|
"language": "go",
|
|
"version": "1.22+",
|
|
"build_command": "go build -o bin/server ./cmd/server",
|
|
"dev_command": "go run ./cmd/server"
|
|
},
|
|
"databases": [{
|
|
"name": "primary_db",
|
|
"type": "postgres",
|
|
"purpose": "Main application database",
|
|
"required": true,
|
|
"connection": {
|
|
"url_env": "DATABASE_URL",
|
|
"default_port": 5432
|
|
},
|
|
"setup": {
|
|
"docker_image": "postgres:16",
|
|
"migration_command": "go run ./cmd/migrate up"
|
|
}
|
|
}],
|
|
"services": [{
|
|
"name": "cache",
|
|
"type": "redis",
|
|
"purpose": "Session and cache storage",
|
|
"required": false,
|
|
"connection": { "url_env": "REDIS_URL" },
|
|
"setup": { "docker_image": "redis:7" },
|
|
"fallback": "In-memory cache when Redis unavailable"
|
|
}],
|
|
"secrets": [{
|
|
"name": "JWT_SECRET",
|
|
"purpose": "JWT token signing",
|
|
"required": true,
|
|
"test_value_ok": true,
|
|
"test_value": "test-secret-do-not-use-in-production"
|
|
}],
|
|
"ports": [{
|
|
"name": "http_server",
|
|
"env": "PORT",
|
|
"default": 8080,
|
|
"test_port": 8081
|
|
}],
|
|
"functional_scenarios": [
|
|
{
|
|
"name": "user_auth_flow",
|
|
"description": "Register, login, access protected resource",
|
|
"requires": ["primary_db", "JWT_SECRET"],
|
|
"category": "auth",
|
|
"steps_hint": [
|
|
"POST /api/v1/register -> 201",
|
|
"POST /api/v1/login -> 200 with token",
|
|
"GET /api/v1/users/{id} with token -> 200"
|
|
],
|
|
"priority": "high"
|
|
},
|
|
{
|
|
"name": "health_check",
|
|
"description": "Basic health and readiness",
|
|
"requires": [],
|
|
"category": "infra",
|
|
"steps_hint": ["GET /health -> 200"],
|
|
"priority": "high"
|
|
}
|
|
],
|
|
"test_environment": {
|
|
"env_vars": {
|
|
"ENV": "test",
|
|
"PORT": "8081",
|
|
"DATABASE_URL": "postgres://postgres:postgres@localhost:5432/testdb?sslmode=disable",
|
|
"JWT_SECRET": "test-secret-do-not-use-in-production"
|
|
}
|
|
},
|
|
"scripts": {
|
|
"setup_env": "harness/scripts/setup-env.sh",
|
|
"start_server": "harness/scripts/start-server.sh",
|
|
"teardown_env": "harness/scripts/teardown-env.sh"
|
|
}
|
|
}
|
|
```
|