85c500e45a
Nova Slides Render / render (push) Failing after 1m1s
Two-stage policy scan per item 20: 1. Checkov on static code BEFORE terraform plan (fail-fast, quick dev feedback). Added to run_platform.sh Step 3c + run_codegen.sh Step 3c (runs on the authored TF dir before plan, using --framework terraform). 2. Runtime policy scan on the plan AFTER terraform plan: Wiz when configured (WIZ_API_TOKEN + WIZ_API_URL), else Checkov against the plan as a drop-in replacement (--framework terraform_plan). Wiz and Checkov are NEVER both run on the plan. Replaces the old single Checkov-on-main.tf step in run_platform.sh Step 5 + run_postapply.sh Step 5. pipelines/contract.yml: stage list updated — 'checkov' stage replaced by 'checkov-static' (before terraform-plan) + 'runtime-policy-scan' (after terraform-plan). 9 stages → 10 stages. Header comment updated. adapters/wiz/wiz_adapter.py: add --plan mode CLI (fetch_and_adapt_plan) for scanning a terraform plan; backward-compat with the positional <wiz_issues.json> <contract-id> mode. is_configured() gates the Wiz path. Tests: test_pipeline_contract.py (9 → 10 stages, new stage names); test_contract_resolver.py (rename test, assert checkov-static + runtime-policy-scan present, old 'checkov' gone). Full suite: 685 pass + 1 pre-existing attestation failure (NOVA_ATTESTATION_SIGNING_KEY_ID unset, unrelated to v1.21, fails on main without these changes too). ---ci--- project: acdl phase: 4 milestone: v1.21 status: execute phase_role: execution ---/ci---
162 lines
6.0 KiB
Bash
Executable File
162 lines
6.0 KiB
Bash
Executable File
#!/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')
|
||
"
|
||
|
||
echo "=== Step 3c: Checkov on static code (fail-fast, before terraform plan) ==="
|
||
# REQ-250 (v1.21): Checkov runs on the authored Terraform code BEFORE
|
||
# terraform plan so developers get immediate policy feedback, not a
|
||
# delayed plan-stage failure. The runtime plan scan (Wiz-or-Checkov)
|
||
# runs after the plan in run_postapply.sh Step 5.
|
||
if [ "$QUIET" = "0" ]; then
|
||
checkov -d "$TF_DIR" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ 2>&1 | tee "$WORK/checkov-static.json"
|
||
else
|
||
checkov -d "$TF_DIR" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ > "$WORK/checkov-static.json" 2> "$WORK/checkov-static.err"
|
||
fi
|
||
[ -s "$WORK/checkov-static.json" ] || { echo "FAIL: checkov (static) produced no output" >&2; exit 1; }
|
||
echo ""
|
||
echo "checkov (static) summary: $(python3 -c "import json; d=json.load(open('$WORK/checkov-static.json')); print(len(d.get('results',{}).get('failed_checks',[])), 'failed,', len(d.get('results',{}).get('passed_checks',[])), 'passed')")"
|
||
|
||
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"
|