feat(P18): testing + CI/CD pipelines - pytest suite, check-only mode, Gitea + GitHub workflows (v1.3.2)

90 offline tests covering adapter, confidence_signal, checkov_adapter,
outbox_writer, and pipeline integration. Identical CI/CD workflows for
Gitea Actions (dev) and GitHub Actions (production). New --check-only
mode for run_platform.sh (offline, no AWS).

---ci---
project: acdl
phase: 18
milestone: v1.3
status: verify
---/ci---
This commit is contained in:
Jon Chery
2026-07-22 14:26:03 +00:00
parent 2c6464afd4
commit 1598c54a8b
17 changed files with 1005 additions and 50 deletions
+47 -14
View File
@@ -1,11 +1,13 @@
#!/usr/bin/env bash
# scripts/run_platform.sh - the ACDL platform pipeline.
#
# Default: full end-to-end pipeline (load pre-existing IR instance ->
# adapter -> terraform plan (real AWS) -> Checkov -> PolicyCheckResult ->
# confidence signal -> evidence event to DynamoDB outbox).
# --plan-only: load IR + adapter + terraform init/validate/plan (steps
# 1-4), then exit.
# Modes:
# --check-only (offline, no AWS/Checkov/DynamoDB — for CI)
# load IR -> adapter -> validate output structure -> exit 0
# --plan-only (requires AWS creds, no Checkov/outbox)
# load IR -> adapter -> terraform init/validate/plan -> exit 0
# (default) (requires AWS creds + Checkov + DynamoDB)
# load IR -> adapter -> terraform plan -> Checkov -> confidence -> outbox
#
# NOTE: contract resolution (contract_resolver.py) was removed when the
# thin-composition layer was taken out. The pipeline now starts from a
@@ -18,9 +20,11 @@ set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
CHECK_ONLY=0
PLAN_ONLY=0
for arg in "$@"; do
case "$arg" in
--check-only) CHECK_ONLY=1 ;;
--plan-only) PLAN_ONLY=1 ;;
*) echo "FAIL: unknown argument: $arg" >&2; exit 1 ;;
esac
@@ -28,15 +32,6 @@ done
fail() { echo "FAIL: $*" >&2; exit 1; }
ENV_FILE="$ROOT/.env.secrets"
[ -f "$ENV_FILE" ] || fail ".env.secrets missing (run scripts/rotate_spike_key.sh)"
set -a
. "$ENV_FILE"
set +a
export AWS_ACCESS_KEY_ID="$ACDL_AWS_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$ACDL_AWS_SECRET_ACCESS_KEY"
export AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION"
CONTRACT_ID="11111111-1111-1111-1111-111111111111" # spike fixed UUID
WORK="/tmp/spike_e2e"
rm -rf "$WORK"; mkdir -p "$WORK"
@@ -51,6 +46,44 @@ echo "=== Step 3: adapter compiles IR -> terraform/spike/*.tf (regenerate) ==="
python3 adapters/terraform/adapter.py "$WORK/spike_ir.json" terraform/spike || fail "adapter failed"
echo "adapter: emitted terraform/spike/{main.tf,terraform.tf,providers.tf}"
if [ "$CHECK_ONLY" = "1" ]; then
echo ""
echo "=== Step 3b: validate adapter output structure (offline) ==="
python3 -c "
import json, os
d = json.load(open('$WORK/spike_ir.json'))
assert d['stack']['name'] == 'l1-s3'
assert len(d['resources']) == 1
tf_dir = 'terraform/spike'
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 'aws_s3_bucket' in main
assert 'acdl-spike-bucket' in main
assert 'versioning' in main
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('adapter output: OK')
"
echo ""
echo "=== PLATFORM CHECK OK ==="
echo "IR instance -> adapter -> structure validated (offline, no AWS)"
exit 0
fi
echo "=== Loading AWS credentials (not needed for --check-only) ==="
ENV_FILE="$ROOT/.env.secrets"
[ -f "$ENV_FILE" ] || fail ".env.secrets missing (run scripts/rotate_spike_key.sh)"
set -a
. "$ENV_FILE"
set +a
export AWS_ACCESS_KEY_ID="$ACDL_AWS_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$ACDL_AWS_SECRET_ACCESS_KEY"
export AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION"
echo "=== Step 4: terraform init + validate + plan -lock=false (real AWS) ==="
cd terraform/spike
terraform init -reconfigure -lock=false -input=false >> "$WORK/tf.log" 2>&1 || fail "terraform init failed"