feat(P4): transparent terraform + feature flags + run_platform.sh split (REQ-233..238)
Create run_codegen.sh (pre-TF: env check, validate, resolve, adapt). Create run_postapply.sh (post-TF: Checkov, confidence, HITL, outbox, SSM, uptime). Add variable 'enabled' (bool, default true) + count=var.enabled?1:0 to all 12 L1 modules (alb, cloudfront, ecr, ecs-cluster, ecs-service, iam-role, kms-key, rds, s3, uptime, vpc, waf). Fix all cross-resource references with [0] indexing. Update interface.json for all modules to declare 'enabled' input. Fix stale artifact path /tmp/acdl_platform_run_v18 → /tmp/nova_platform_run (REQ-238). run_platform.sh remains as backward-compat shim for local-dev usage. ---ci--- project: acdl phase: 4 milestone: v1.20 status: execute requirements: [REQ-233, REQ-234, REQ-235, REQ-236, REQ-237, REQ-238] ---/ci---
This commit is contained in:
Executable
+147
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/run_codegen.sh — pre-Terraform codegen for the Nova platform pipeline.
|
||||
#
|
||||
# Performs steps 0–3b of run_platform.sh:
|
||||
# 0. Environment onboarding check
|
||||
# 1. Validate contract against contract.schema.json
|
||||
# 2. Resolve contract → Target Stack instance (contract_resolver.py)
|
||||
# 3. Adapter compiles stack → Terraform (adapter.py)
|
||||
# 3b. Structural validation of emitted TF (offline)
|
||||
#
|
||||
# Emits Terraform files to $NOVA_WORK_DIR/tf/{main.tf,terraform.tf,providers.tf}
|
||||
# and prints the work dir path for the caller (workflow or run_platform.sh)
|
||||
# to use for native terraform init/validate/plan/apply steps.
|
||||
#
|
||||
# Usage:
|
||||
# run_codegen.sh <contract.yml> [--environment <name>] [--check-only]
|
||||
#
|
||||
# When --check-only is passed, exits 0 after structural validation (no AWS).
|
||||
# Otherwise, loads AWS credentials from .env.secrets if not already set,
|
||||
# and exits 0 with the work dir ready for terraform.
|
||||
#
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CALLER_CWD="$(pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
CHECK_ONLY=0
|
||||
ENVIRONMENT_OVERRIDE=""
|
||||
CONTRACT=""
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--check-only) CHECK_ONLY=1; shift ;;
|
||||
--environment) shift; ENVIRONMENT_OVERRIDE="$1"; shift ;;
|
||||
--environment=*) ENVIRONMENT_OVERRIDE="${1#--environment=}"; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: run_codegen.sh <contract.yml> [--environment <name>] [--check-only]"
|
||||
exit 0 ;;
|
||||
*) CONTRACT="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$CONTRACT" ] || { echo "FAIL: no contract file specified" >&2; exit 1; }
|
||||
|
||||
# Resolve relative contract path against caller's CWD
|
||||
if ! [[ "$CONTRACT" = /* ]]; then
|
||||
CONTRACT="$CALLER_CWD/$CONTRACT"
|
||||
fi
|
||||
[ -f "$CONTRACT" ] || { echo "FAIL: contract not found: $CONTRACT" >&2; exit 1; }
|
||||
|
||||
if [ -n "$ENVIRONMENT_OVERRIDE" ]; then
|
||||
export NOVA_ENVIRONMENT_OVERRIDE="$ENVIRONMENT_OVERRIDE"
|
||||
fi
|
||||
|
||||
CONTRACT_ID="${NOVA_CONTRACT_ID:-11111111-1111-1111-1111-111111111111}"
|
||||
WORK="${NOVA_WORK_DIR:-/tmp/nova_platform_run}"
|
||||
TF_DIR="$WORK/tf"
|
||||
rm -rf "$WORK"; mkdir -p "$TF_DIR"
|
||||
|
||||
echo "=== Step 0: environment onboarding check ==="
|
||||
if [ -n "$ENVIRONMENT_OVERRIDE" ]; then
|
||||
python3 core/environment_check.py --env="$ENVIRONMENT_OVERRIDE" || {
|
||||
echo "FAIL: environment onboarding check failed for $ENVIRONMENT_OVERRIDE" >&2
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
python3 core/environment_check.py || true
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Step 1: validate contract against contract.schema.json ==="
|
||||
python3 -c "
|
||||
import json, sys, yaml
|
||||
from jsonschema import validate
|
||||
schema = json.load(open('schemas/contract.schema.json'))
|
||||
doc = yaml.safe_load(open('$CONTRACT'))
|
||||
validate(instance=doc, schema=schema)
|
||||
print(f'contract valid: {doc.get(\"name\", \"unnamed\")} (env={doc.get(\"environment\",\"dev\")})')
|
||||
"
|
||||
|
||||
echo ""
|
||||
echo "=== Step 2: resolve contract → Target Stack instance ==="
|
||||
python3 -c "
|
||||
import json, sys
|
||||
sys.path.insert(0, '$ROOT')
|
||||
from core.contract_resolver import resolve
|
||||
stack = resolve('$CONTRACT', environment_override='${ENVIRONMENT_OVERRIDE}' or None)
|
||||
json.dump(stack, open('$WORK/stack.json', 'w'), indent=2)
|
||||
print(f'stack resolved: {stack[\"stack\"][\"name\"]} ({len(stack[\"resources\"])} resource(s))')
|
||||
"
|
||||
|
||||
echo ""
|
||||
echo "=== Step 3: adapter compiles stack → $TF_DIR/*.tf ==="
|
||||
python3 -c "
|
||||
import json, sys
|
||||
sys.path.insert(0, '$ROOT')
|
||||
from adapters.terraform.adapter import TerraformAdapter
|
||||
stack = json.load(open('$WORK/stack.json'))
|
||||
adapter = TerraformAdapter()
|
||||
adapter.compile(stack, '$TF_DIR')
|
||||
print('adapter: main.tf + terraform.tf + providers.tf written')
|
||||
"
|
||||
|
||||
if [ "$CHECK_ONLY" = "1" ]; then
|
||||
echo ""
|
||||
echo "=== Step 3b: validate adapter output structure (offline) ==="
|
||||
python3 -c "
|
||||
import json, os
|
||||
d = json.load(open('$WORK/stack.json'))
|
||||
assert d['stack']['name'], 'stack name missing'
|
||||
assert len(d['resources']) >= 1, 'expected at least 1 resource'
|
||||
tf_dir = '$TF_DIR'
|
||||
for f in ('main.tf', 'terraform.tf', 'providers.tf'):
|
||||
assert os.path.isfile(os.path.join(tf_dir, f)), f'{f} missing'
|
||||
main = open(os.path.join(tf_dir, 'main.tf')).read()
|
||||
assert len(main) > 0, 'main.tf is empty'
|
||||
tf = open(os.path.join(tf_dir, 'terraform.tf')).read()
|
||||
assert 'backend' in tf
|
||||
assert 'required_version' in tf
|
||||
prov = open(os.path.join(tf_dir, 'providers.tf')).read()
|
||||
assert 'provider \"aws\"' in prov
|
||||
print(f\"adapter output: OK ({d['stack']['name']}, {len(d['resources'])} resource(s))\")
|
||||
"
|
||||
echo ""
|
||||
echo "=== CODEGEN CHECK OK ==="
|
||||
echo "contract → resolver → stack → adapter → structure validated (offline, no AWS)"
|
||||
echo "WORK_DIR=$WORK"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Load AWS credentials if not already set (for non-check-only modes)
|
||||
if [ -z "${AWS_ACCESS_KEY_ID:-}" ] || [ -z "${AWS_SECRET_ACCESS_KEY:-}" ]; then
|
||||
ENV_FILE="$ROOT/.env.secrets"
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
set -a
|
||||
. "$ENV_FILE"
|
||||
set +a
|
||||
export AWS_ACCESS_KEY_ID="$NOVA_AWS_ACCESS_KEY_ID"
|
||||
export AWS_SECRET_ACCESS_KEY="$NOVA_AWS_SECRET_ACCESS_KEY"
|
||||
export AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== CODEGEN OK ==="
|
||||
echo "Terraform files ready in: $TF_DIR"
|
||||
echo "WORK_DIR=$WORK"
|
||||
Reference in New Issue
Block a user