- pdf-creator v1.2.0: theme system (default/warm-terra), dual backend (weasyprint/chrome auto-detect), argparse CLI, extracted CSS to themes/ - terraform-skill: operational traps from real deployments (provisioner timing, DNS duplication, multi-env isolation, pre-deploy validation) - asr-transcribe-to-text: add security scan marker Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
83 lines
2.8 KiB
Markdown
83 lines
2.8 KiB
Markdown
# Pre-Deploy Validation Pattern
|
|
|
|
Run before `terraform apply` to catch configuration errors locally. Eliminates the deploy→discover→fix→redeploy cycle that wastes hours.
|
|
|
|
## Why this matters
|
|
|
|
Every hardcoded value becomes a bug when creating a second environment. Production accumulates implicit state over time (cached TLS certs, manually created databases, hand-edited configs). Fresh instances expose all of these as failures. A pre-deploy script catches them before they reach the remote.
|
|
|
|
## Validation categories
|
|
|
|
### 1. Terraform syntax
|
|
```bash
|
|
terraform validate
|
|
```
|
|
|
|
### 2. Hardcoded domains
|
|
```bash
|
|
# Caddyfiles: should use {$VAR} not literal domains
|
|
grep -v "^#" gateway/conf.d/*.caddy | grep -c "example\.com" # should be 0
|
|
|
|
# Compose: should use ${VAR:?required} not literal domains
|
|
grep -v "^#" docker-compose.production.yml | grep -c "example\.com" # should be 0
|
|
```
|
|
|
|
### 3. Required env vars
|
|
Check that every `${VAR:?required}` in compose has a matching entry in `.env`:
|
|
```bash
|
|
for VAR in LOBEHUB_DOMAIN CLAUDE4DEV_DOMAIN CLOUDFLARE_API_TOKEN APP_URL AUTH_URL; do
|
|
grep -q "^$VAR=" .env || echo "FAIL: $VAR missing"
|
|
done
|
|
```
|
|
|
|
### 4. Cloudflare credential format
|
|
Caddy's Cloudflare plugin uses Bearer auth. Global API Keys (37 hex chars) fail with `Invalid format for Authorization header`.
|
|
```bash
|
|
TOKEN=$(grep CLOUDFLARE_API_TOKEN .env | cut -d= -f2)
|
|
echo "$TOKEN" | grep -qE "^cfut_|^[A-Za-z0-9_-]{40,}$" || echo "FAIL: looks like Global API Key, not API Token"
|
|
```
|
|
|
|
### 5. DNS ↔ Caddy consistency
|
|
Every domain Caddy serves needs a DNS record. Check live resolution:
|
|
```bash
|
|
for DOMAIN in staging.example.com auth.staging.example.com; do
|
|
curl -sf "https://dns.google/resolve?name=$DOMAIN&type=A" | python3 -c \
|
|
"import sys,json; d=json.load(sys.stdin); exit(0 if d.get('Answer') else 1)" \
|
|
|| echo "FAIL: $DOMAIN not resolving"
|
|
done
|
|
```
|
|
|
|
### 6. Casdoor issuer consistency
|
|
`AUTH_CASDOOR_ISSUER` must point to `auth.<domain>`, not the app's root domain:
|
|
```bash
|
|
ISSUER=$(grep AUTH_CASDOOR_ISSUER .env | cut -d= -f2)
|
|
DOMAIN=$(grep LOBEHUB_DOMAIN .env | cut -d= -f2)
|
|
[ "$ISSUER" = "https://auth.$DOMAIN" ] || echo "FAIL: issuer should be https://auth.$DOMAIN"
|
|
```
|
|
|
|
### 7. SSH key exists
|
|
```bash
|
|
[ -f ~/.ssh/id_ed25519 ] || echo "FAIL: SSH key not found"
|
|
```
|
|
|
|
## Makefile integration
|
|
|
|
```makefile
|
|
pre-deploy:
|
|
@./scripts/validate-env.sh $(ENV)
|
|
|
|
# Enforce: plan requires pre-deploy to pass
|
|
plan: pre-deploy
|
|
cd $(ENV_DIR) && terraform plan -out=tfplan
|
|
```
|
|
|
|
## Anti-pattern: deploy-and-pray
|
|
|
|
The opposite of pre-deploy validation is the "deploy and see what breaks" cycle:
|
|
1. `terraform apply` → fails
|
|
2. SSH in to debug → discover error
|
|
3. Fix locally → commit → re-apply → fails differently
|
|
4. Repeat 5-10 times
|
|
|
|
Each cycle takes 3-5 minutes (plan + apply + provisioner). Pre-deploy catches 80% of issues in <5 seconds locally.
|