feat(P41): per-environment CI jobs + environment workflow input
---ci---
project: acdl
phase: 41
milestone: v1.9
status: execute
---/ci---
Phase 41 — per-environment-ci-jobs (REQ-105, REQ-106, D-082):
Per-env contracts (REQ-105):
- contracts/static-assets.{dev,qa,prod,dr}.yaml + microservice.{dev,qa,prod,dr}.yaml
(8 files, each sets environment: to its own name, uses interpolation).
- Default contracts/static-assets.yaml + microservice.yaml preserved (backwards compat).
Deploy workflow environment input (REQ-106):
- .github/workflows/deploy.yml + .gitea/workflows/deploy.yml (byte-identical):
new 'environment' workflow_call input (default empty, override).
- scripts/run_platform.sh: --environment <name> flag; exports
ACDL_ENVIRONMENT_OVERRIDE; re-runs env check against the override.
- core/contract_resolver.py: resolve(environment_override=...) (D-088);
CLI honors --environment flag + ACDL_ENVIRONMENT_OVERRIDE env var.
Consumer guide (REQ-106):
- docs/consumer-guide.md: 'Per-environment deployment' section with 4
caller-workflow examples (dev/qa/prod/dr), HITL gate structure
(approve_qa/approve_prod/approve_dr, D-042), interpolation reference table.
- Documents promotion-without-editing + hybrid model (per-env contracts
OR single contract + env input).
Tests: +40 (test_per_env_contracts.py, test_deploy_workflow_env_input.py,
test_consumer_guide_per_env_section.py). 446 passed; run_ci.sh green;
deploy workflows byte-identical.
This commit is contained in:
+81
-1
@@ -376,4 +376,84 @@ window if needed. The CMK is permanently deleted after the window expires.
|
||||
The uptime monitoring stack (deployed with separate state) is not
|
||||
automatically destroyed by the decommission. It must be destroyed
|
||||
separately (or left running to monitor the decommissioned stack's
|
||||
endpoints going dark).
|
||||
endpoints going dark).
|
||||
## Per-environment deployment
|
||||
|
||||
ACDL supports a **promotion-without-editing** model: you do not edit the
|
||||
`environment:` field in a contract to promote dev → qa → prod → dr.
|
||||
Instead, there is **one CI job per environment**, each pointing at its
|
||||
respective contract (or the same contract + the `environment` workflow
|
||||
input). Promotion = running the matching job.
|
||||
|
||||
### Two shapes (both supported)
|
||||
|
||||
**Shape 1 — per-environment contract files:** a consumer repo has one
|
||||
contract per environment (e.g. `.acdl/static-assets.dev.yaml`,
|
||||
`.acdl/static-assets.qa.yaml`, …). Each sets `environment:` to its own
|
||||
name and uses interpolation so env-specific values differ automatically:
|
||||
|
||||
```yaml
|
||||
# .acdl/static-assets.qa.yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: static-assets
|
||||
environment: qa
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
```
|
||||
|
||||
**Shape 2 — single contract + `environment` workflow input:** the
|
||||
reusable deploy workflow (`acdl/.github/workflows/deploy.yml@v1.9`)
|
||||
declares an `environment` input. When non-empty, it overrides the
|
||||
contract's `environment` field at load time (before interpolation), so
|
||||
the same contract can be promoted by passing a different environment:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/deploy-qa.yml (caller workflow)
|
||||
on: workflow_dispatch:
|
||||
inputs:
|
||||
approve_qa:
|
||||
description: "Set to true to approve the QA promotion"
|
||||
type: boolean
|
||||
required: true
|
||||
jobs:
|
||||
deploy-qa:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.9
|
||||
with:
|
||||
environment: qa
|
||||
contract: .acdl/contract.yaml
|
||||
```
|
||||
|
||||
### One job per environment
|
||||
|
||||
A consumer repo's `.github/workflows/` directory has one caller workflow
|
||||
per environment:
|
||||
|
||||
| File | Environment | Gate |
|
||||
|------|-------------|------|
|
||||
| `deploy-dev.yml` | dev | autonomous (no gate, confidence ≥ 0.50) |
|
||||
| `deploy-qa.yml` | qa | QA HITL (`approve_qa` workflow_dispatch input; `github.actor` is the approver of record) |
|
||||
| `deploy-prod.yml` | prod | SRE HITL (`approve_prod`; separation-of-duties enforced) |
|
||||
| `deploy-dr.yml` | dr | SRE HITL (`approve_dr`) |
|
||||
|
||||
**Promotion = running the matching job.** No `environment:` field editing.
|
||||
The approver identity is recorded to the DynamoDB outbox
|
||||
(`approver_qa` / `approver_prod` / `approver_dr`) and the separation-of-
|
||||
duties check blocks a prod promotion when `approver_qa == approver_prod`
|
||||
(see `core/hitl_matrix_design.md`).
|
||||
|
||||
### Interpolation reference
|
||||
|
||||
| Token | Resolves to | Example |
|
||||
|-------|-------------|---------|
|
||||
| `${env.environment}` | the environment name (dev/qa/prod/dr) | `qa` |
|
||||
| `${env.region}` | the environment's AWS region | `us-east-1` |
|
||||
| `${env.account_id}` | the environment's AWS account id | `123456789012` |
|
||||
| `${env.state_backend.bucket}` | the environment's state bucket | `acdl-qa-state` |
|
||||
| `${env.network.vpc_cidr}` | the environment's VPC CIDR | `10.1.0.0/16` |
|
||||
| `${contract.module}` | the contract's module name | `static-assets` |
|
||||
| `${contract.environment}` | the contract's environment field | `qa` |
|
||||
| `${contract.inputs.<name>}` | a contract input value | (as declared) |
|
||||
|
||||
Unknown tokens raise `ValueError` (fail loud). Expansion is recursive
|
||||
(nested map/list values expand too).
|
||||
|
||||
Reference in New Issue
Block a user