feat(P26): 3 platform pipelines + release job with semver/tag updates
Phase 26 — platform-pipelines-and-release-automation: - platform-test.yml: PR pipeline (lint + unit-test + integration-test + schema-validation) replacing ci.yml for PRs; integration-test runs run_platform.sh --check-only for every contracts/*.yaml - primitives-plan.yml: PR pipeline with matrix over all 9 L1 primitives (s3, vpc, ecs-cluster, ecs-service, iam-role, alb, ecr, cloudfront, waf) - patterns-plan.yml: PR pipeline with matrix over all 2 L2 modules (static-assets, microservice) - release.yml: push-to-main pipeline computing next semver tag (PATCH for regular phases, MINOR for milestone completions), updating floating MAJOR.MINOR + MAJOR tags, and creating GitHub releases - run_primitive_plan.sh: plan-only/check-only runner for a single L1 primitive (adapter compile + structure validation offline) - run_pattern_plan.sh: plan-only/check-only runner for a single L2 pattern (environment check + contract validate + resolve + adapter + structure validation offline) - contracts/microservice.yaml: sample consumer contract for the microservice L2 module (schema-compliant scalar inputs) - instance.json for 8 L1 primitives (vpc, ecs-cluster, ecs-service, iam-role, alb, ecr, cloudfront, waf) so the primitives-plan matrix can run the adapter offline; s3 already had one - tests/test_release_logic.py: unit test for semver computation (PATCH bump, MINOR bump on milestone, floating tag format) - tests/test_pipeline_contract.py: 19 new tests validating the 4 platform workflows exist and conform (stages, matrices, triggers, permissions) DEVIATION: The microservice pattern (run_pattern_plan.sh --check-only microservice + run_platform.sh --check-only contracts/microservice.yaml) fails at the adapter stage due to a pre-existing resolver ref-id mismatch for multi-resource L1s (resolver emits ref:vpc.subnet_ids but the expanded resource id is vpc-subnet). This predates Phase 26 and is out of scope for pipeline automation; the static-assets pattern passes end-to-end. The microservice contract is schema-valid and resolves correctly (11 resources); only the adapter compilation of multi-resource L1 refs fails. VERIFICATION: - bash scripts/run_ci.sh: PASS (lint + test + check-only) - python3 -m pytest tests/ -v: 266 passed - bash scripts/run_primitive_plan.sh --check-only s3: PASS - bash scripts/run_pattern_plan.sh --check-only static-assets: PASS - All 9 primitives pass run_primitive_plan.sh --check-only - All instance.json validate against stack.schema.json ---ci--- project: acdl phase: 26 milestone: v1.7 status: execute ---/ci---
This commit is contained in:
@@ -360,4 +360,134 @@ class TestSampleContractVersioning:
|
||||
uses = contract["uses"]
|
||||
assert "@v" in uses, "sample contract must use a versioned @vX.Y tag"
|
||||
assert "@main" not in uses, "sample contract must not use @main"
|
||||
assert uses == "acdl/pipelines/deploy.yaml@v1.6"
|
||||
assert uses == "acdl/pipelines/deploy.yaml@v1.6"
|
||||
|
||||
|
||||
class TestPlatformWorkflows:
|
||||
"""Validate the Phase 26 platform pipelines exist and conform."""
|
||||
|
||||
def test_platform_test_workflow_exists(self):
|
||||
assert (ROOT / ".github/workflows/platform-test.yml").is_file()
|
||||
|
||||
def test_primitives_plan_workflow_exists(self):
|
||||
assert (ROOT / ".github/workflows/primitives-plan.yml").is_file()
|
||||
|
||||
def test_patterns_plan_workflow_exists(self):
|
||||
assert (ROOT / ".github/workflows/patterns-plan.yml").is_file()
|
||||
|
||||
def test_release_workflow_exists(self):
|
||||
assert (ROOT / ".github/workflows/release.yml").is_file()
|
||||
|
||||
def test_platform_test_has_four_stages(self):
|
||||
wf = _load_workflow(".github/workflows/platform-test.yml")
|
||||
job_names = set(wf["jobs"].keys())
|
||||
assert job_names == {"lint", "unit-test", "integration-test", "schema-validation"}
|
||||
|
||||
def test_platform_test_lint_compiles_python(self):
|
||||
wf = _load_workflow(".github/workflows/platform-test.yml")
|
||||
lint_job = wf["jobs"]["lint"]
|
||||
run_step = next(s for s in lint_job["steps"] if "run" in s)
|
||||
assert "py_compile" in run_step["run"]
|
||||
for py_file in [
|
||||
"core/confidence_signal.py",
|
||||
"core/outbox_writer.py",
|
||||
"core/contract_resolver.py",
|
||||
"core/environment_check.py",
|
||||
"core/output_publisher.py",
|
||||
"core/lambda/contract_ingestor.py",
|
||||
"adapters/terraform/adapter.py",
|
||||
"adapters/terraform/policy/checkov_adapter.py",
|
||||
"adapters/wiz/wiz_adapter.py",
|
||||
"adapters/kyverno/kyverno_adapter.py",
|
||||
"scripts/push_consumer_image.py",
|
||||
]:
|
||||
assert py_file in run_step["run"], f"{py_file} missing from platform-test lint"
|
||||
|
||||
def test_platform_test_unit_test_runs_pytest(self):
|
||||
wf = _load_workflow(".github/workflows/platform-test.yml")
|
||||
test_job = wf["jobs"]["unit-test"]
|
||||
run_step = next(s for s in test_job["steps"] if "run" in s and "pytest" in s["run"])
|
||||
assert "pytest" in run_step["run"]
|
||||
|
||||
def test_platform_test_integration_runs_all_contracts(self):
|
||||
wf = _load_workflow(".github/workflows/platform-test.yml")
|
||||
integ_job = wf["jobs"]["integration-test"]
|
||||
run_step = next(
|
||||
s for s in integ_job["steps"] if "run" in s and "run_platform" in s["run"]
|
||||
)
|
||||
assert "run_platform.sh" in run_step["run"]
|
||||
assert "--check-only" in run_step["run"]
|
||||
assert "contracts/*.yaml" in run_step["run"]
|
||||
|
||||
def test_platform_test_schema_validation_validates_schemas(self):
|
||||
wf = _load_workflow(".github/workflows/platform-test.yml")
|
||||
schema_job = wf["jobs"]["schema-validation"]
|
||||
steps_text = " ".join(s.get("run", "") for s in schema_job["steps"])
|
||||
assert "jsonschema" in steps_text
|
||||
assert "stack.schema.json" in steps_text
|
||||
|
||||
def test_platform_test_triggers_pr_only(self):
|
||||
wf = _load_workflow(".github/workflows/platform-test.yml")
|
||||
assert "pull_request" in wf["on"]
|
||||
assert "main" in wf["on"]["pull_request"]["branches"]
|
||||
# platform-test should NOT trigger on push (ci.yml handles push-to-main)
|
||||
assert "push" not in wf["on"]
|
||||
|
||||
def test_primitives_plan_has_matrix_with_all_l1_primitives(self):
|
||||
wf = _load_workflow(".github/workflows/primitives-plan.yml")
|
||||
job = wf["jobs"]["primitive-plan"]
|
||||
matrix = job["strategy"]["matrix"]
|
||||
expected = ["s3", "vpc", "ecs-cluster", "ecs-service", "iam-role", "alb", "ecr", "cloudfront", "waf"]
|
||||
assert sorted(matrix["primitive"]) == sorted(expected)
|
||||
|
||||
def test_primitives_plan_runs_run_primitive_plan(self):
|
||||
wf = _load_workflow(".github/workflows/primitives-plan.yml")
|
||||
job = wf["jobs"]["primitive-plan"]
|
||||
run_step = next(s for s in job["steps"] if "run" in s and "run_primitive_plan" in s["run"])
|
||||
assert "run_primitive_plan.sh" in run_step["run"]
|
||||
assert "--check-only" in run_step["run"]
|
||||
|
||||
def test_primitives_plan_triggers_pr_only(self):
|
||||
wf = _load_workflow(".github/workflows/primitives-plan.yml")
|
||||
assert "pull_request" in wf["on"]
|
||||
assert "main" in wf["on"]["pull_request"]["branches"]
|
||||
assert "push" not in wf["on"]
|
||||
|
||||
def test_patterns_plan_has_matrix_with_all_l2_modules(self):
|
||||
wf = _load_workflow(".github/workflows/patterns-plan.yml")
|
||||
job = wf["jobs"]["pattern-plan"]
|
||||
matrix = job["strategy"]["matrix"]
|
||||
expected = ["static-assets", "microservice"]
|
||||
assert sorted(matrix["module"]) == sorted(expected)
|
||||
|
||||
def test_patterns_plan_runs_run_pattern_plan(self):
|
||||
wf = _load_workflow(".github/workflows/patterns-plan.yml")
|
||||
job = wf["jobs"]["pattern-plan"]
|
||||
run_step = next(s for s in job["steps"] if "run" in s and "run_pattern_plan" in s["run"])
|
||||
assert "run_pattern_plan.sh" in run_step["run"]
|
||||
assert "--check-only" in run_step["run"]
|
||||
|
||||
def test_patterns_plan_triggers_pr_only(self):
|
||||
wf = _load_workflow(".github/workflows/patterns-plan.yml")
|
||||
assert "pull_request" in wf["on"]
|
||||
assert "main" in wf["on"]["pull_request"]["branches"]
|
||||
assert "push" not in wf["on"]
|
||||
|
||||
def test_release_workflow_triggers_push_main(self):
|
||||
wf = _load_workflow(".github/workflows/release.yml")
|
||||
assert "push" in wf["on"]
|
||||
assert "main" in wf["on"]["push"]["branches"]
|
||||
|
||||
def test_release_workflow_has_contents_write_permission(self):
|
||||
wf = _load_workflow(".github/workflows/release.yml")
|
||||
# permissions are declared at the job level (the release job)
|
||||
release_job = wf["jobs"]["release"]
|
||||
assert release_job["permissions"]["contents"] == "write"
|
||||
|
||||
def test_release_workflow_fetch_depth_zero(self):
|
||||
wf = _load_workflow(".github/workflows/release.yml")
|
||||
release_job = wf["jobs"]["release"]
|
||||
checkout = next(
|
||||
s for s in release_job["steps"] if "checkout" in s.get("uses", "")
|
||||
)
|
||||
assert checkout["with"]["fetch-depth"] == 0
|
||||
Reference in New Issue
Block a user