Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2397336cbb | |||
| 10b87a644c | |||
| 031887ec56 | |||
| 7f36df5610 |
@@ -19,7 +19,8 @@
|
||||
- **v1.9.6 (complete, tag `v1.9.6`):** consolidate both Marp decks to 10 high-impact slides. PW deck 16 → 10 (merged Problem+North Star+Anti-goals, merged Policy+Secure by Default, merged Audit+HITL, folded Observability/Environments/Portability into existing slides, added Vision Realized closing). DX deck 15 → 10 (merged What Dev Does+Contract+No Platform Code, merged Feedback+Deploy Outputs, merged Promotion+Rising Bar, cut Citizen Developer standalone, kept Versioned Releases/Onboarding/Decommission). Removed '5-line YAML' claim from both decks. Source markdown unchanged. Docs-only NFR patch.
|
||||
- **v1.9.7 (complete, tag `v1.9.7`):** talking points files + 4-step process. Created two talking points markdown files (one per deck) distilling the source of truth into presenter-ready cues indexed by the Marp deck's 10-slide structure. Each file has 3-6 talking point bullets + key takeaway per slide. README updated from 3-step to 4-step process (added Step 4: talking points). Directory layout, checklist, and decks table updated. Docs-only NFR patch.
|
||||
- **v1.9.8 (complete, tag `v1.9.8`):** full presentation rework — scope, story arc, visuals, appendix. 6 new mermaid diagrams (scope boundary x2, confidence signal, attestation flow, promotion journey, road to north star). Both decks restructured to 10 main + 6 appendix slides. NEW scope slide clarifying ACDL is infrastructure only. Story beat lines on every slide. Contract examples fixed (image: removed, infra inputs instead). QA attestation reclassified (Design tested → Planned). Confidence signal + attestation flow + promotion journey visuals added. Road to the North Star phased timeline in appendix. Full Testing vs. Planned inventory + glossary in appendix. Source markdown + talking points + README all updated. Docs-only NFR patch. **Last deck-polish phase before the v1.10 deck-freeze.**
|
||||
- **v1.10 (active, tag `v1.10.0`):** pipeline regression fix + capability re-verification + verified-reality rewrite. The v1.9.1–v1.9.8 deck work is **superseded-by-reverification**: the decks presented advertised capability as current without disclosing that the platform had decayed (7 adapter defects prevented `terraform init/validate/plan` against live AWS). v1.10 re-verified every advertised capability, fixed all 7 defects in-sweep (D-090: no cap), and rewrote PROJECT/ROADMAP/decks to match verified reality. Decks unfrozen only after Phase 55 lands. See the v1.10 section below for the 4-phase breakdown.
|
||||
- **v1.10 (complete, tag `v1.10.0`):** pipeline regression fix + capability re-verification + verified-reality rewrite. The v1.9.1–v1.9.8 deck work is **superseded-by-reverification**: the decks presented advertised capability as current without disclosing that the platform had decayed (7 adapter defects prevented `terraform init/validate/plan` against live AWS). v1.10 re-verified every advertised capability, fixed all 7 defects in-sweep (D-090: no cap), and rewrote PROJECT/ROADMAP/decks to match verified reality. Decks unfrozen only after Phase 55 lands. See the v1.10 section below for the 4-phase breakdown.
|
||||
- **v1.10.1 (complete, tag `v1.10.1`):** post-v1.10 NFR patch — adversarial grill review (12 challenges, 10 binding decisions, 2 escalations: G-005 risks, G-008 budget), 4-layer verify gate (PASS), multi-persona code review (1 P1 auto-fixed: mis-citation PROJECT.md:6 → PROJECT.md:487). ACDL reclassified as OSS reference implementation (G-003). Docs-only; 518 tests pass; regression gate 16/16 Verified. Gitea release id 236.
|
||||
- **v1.0 demo URL:** https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html
|
||||
|
||||
---
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ACDL CI Pipeline — Gitea Actions (dev environment)
|
||||
#
|
||||
# This workflow implements the central pipeline contract:
|
||||
# pipelines/ci.yaml (validated against schemas/pipeline.schema.json)
|
||||
# pipelines/ci.yml (validated against schemas/pipeline.schema.json)
|
||||
#
|
||||
# The same contract is implemented by .github/workflows/ci.yml (GitHub
|
||||
# Actions, production). Both files must be byte-identical — the only
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ACDL Reusable Deploy Workflow — Gitea Actions (dev environment)
|
||||
#
|
||||
# This reusable workflow implements the central deployment pipeline contract:
|
||||
# pipelines/deploy.yaml (validated against schemas/deploy-pipeline.schema.json)
|
||||
# pipelines/contract.yml (validated against schemas/deploy-pipeline.schema.json)
|
||||
#
|
||||
# The same contract is implemented by .github/workflows/deploy.yml (GitHub
|
||||
# Actions, production). Both files must be byte-identical — the only
|
||||
@@ -26,7 +26,7 @@
|
||||
# platform log) for auditability.
|
||||
#
|
||||
# Inputs:
|
||||
# contract — path to the consumer's contract YAML (default .acdl/contract.yaml)
|
||||
# contract — path to the consumer's contract YAML (default .acdl/contract.yml)
|
||||
# mode — full | plan-only | check-only (default full; dev = full apply,
|
||||
# higher environments hold for HITL — the calling repo or the
|
||||
# forge environment gate enforces that)
|
||||
@@ -51,7 +51,7 @@ on:
|
||||
contract:
|
||||
description: Path to the consumer contract YAML (in the consumer repo)
|
||||
type: string
|
||||
default: .acdl/contract.yaml
|
||||
default: .acdl/contract.yml
|
||||
mode:
|
||||
description: Pipeline mode — full (apply), plan-only, check-only, or decommission
|
||||
type: string
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ACDL CI Pipeline — Gitea Actions (dev environment)
|
||||
#
|
||||
# This workflow implements the central pipeline contract:
|
||||
# pipelines/ci.yaml (validated against schemas/pipeline.schema.json)
|
||||
# pipelines/ci.yml (validated against schemas/pipeline.schema.json)
|
||||
#
|
||||
# The same contract is implemented by .github/workflows/ci.yml (GitHub
|
||||
# Actions, production). Both files must be byte-identical — the only
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ACDL Reusable Deploy Workflow — Gitea Actions (dev environment)
|
||||
#
|
||||
# This reusable workflow implements the central deployment pipeline contract:
|
||||
# pipelines/deploy.yaml (validated against schemas/deploy-pipeline.schema.json)
|
||||
# pipelines/contract.yml (validated against schemas/deploy-pipeline.schema.json)
|
||||
#
|
||||
# The same contract is implemented by .github/workflows/deploy.yml (GitHub
|
||||
# Actions, production). Both files must be byte-identical — the only
|
||||
@@ -26,7 +26,7 @@
|
||||
# platform log) for auditability.
|
||||
#
|
||||
# Inputs:
|
||||
# contract — path to the consumer's contract YAML (default .acdl/contract.yaml)
|
||||
# contract — path to the consumer's contract YAML (default .acdl/contract.yml)
|
||||
# mode — full | plan-only | check-only (default full; dev = full apply,
|
||||
# higher environments hold for HITL — the calling repo or the
|
||||
# forge environment gate enforces that)
|
||||
@@ -51,7 +51,7 @@ on:
|
||||
contract:
|
||||
description: Path to the consumer contract YAML (in the consumer repo)
|
||||
type: string
|
||||
default: .acdl/contract.yaml
|
||||
default: .acdl/contract.yml
|
||||
mode:
|
||||
description: Pipeline mode — full (apply), plan-only, check-only, or decommission
|
||||
type: string
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Shell reproducibility: scripts/run_ci.sh runs lint + test + check-only locally.
|
||||
# The integration-test stage runs run_platform.sh --check-only for every
|
||||
# contracts/*.yaml file. The schema-validation stage validates schemas, module
|
||||
# contracts/*.yml file. The schema-validation stage validates schemas, module
|
||||
# interfaces, compositions, and example contracts.
|
||||
name: acdl-platform-test
|
||||
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
run: pip install jsonschema pyyaml boto3
|
||||
- name: Run platform check-only for every sample contract
|
||||
run: |
|
||||
for contract in contracts/*.yaml; do
|
||||
for contract in contracts/*.yml; do
|
||||
echo "--- Testing $contract ---"
|
||||
bash scripts/run_platform.sh --check-only "$contract"
|
||||
done
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
except Exception as e:
|
||||
print(f'{example}: SKIP (not a contract or invalid: {e})')
|
||||
# Also validate all sample contracts in contracts/
|
||||
for contract_file in glob.glob('contracts/*.yaml'):
|
||||
for contract_file in glob.glob('contracts/*.yml'):
|
||||
contract = yaml.safe_load(open(contract_file))
|
||||
jsonschema.validate(contract, schema)
|
||||
print(f'{contract_file}: valid contract')
|
||||
|
||||
@@ -26,9 +26,9 @@ There are two kinds of repository in the ACDL model:
|
||||
A **consumer never clones it.**
|
||||
- **Consumer repo (yours).** A consumer repo contains only:
|
||||
1. **Its application code** — the service or site being deployed.
|
||||
2. **One or more contracts** — small YAML files at `.acdl/contract.yaml`
|
||||
that reference the central pipeline, name a module, select an
|
||||
environment, and supply module-specific inputs.
|
||||
2. **One or more contracts** — small YAML files at `.acdl/contract.yml`
|
||||
that declare infrastructure (one or more modules by name + version),
|
||||
select an environment, and supply module-specific inputs.
|
||||
3. **One or more CI definitions** — thin `.github/workflows/*.yml` files
|
||||
that `uses:` the central reusable deploy workflow, pointing at the
|
||||
appropriate environment + contract.
|
||||
@@ -93,9 +93,9 @@ intent via a contract; the platform delivers the deployment through the
|
||||
same contract schema, the same policy envelope, and the same evidence
|
||||
stream.
|
||||
|
||||
Consumers have their own repos and consume ACDL by referencing `uses:` the
|
||||
central pipeline definitions. A consumer declares a contract (module +
|
||||
environment + inputs); the platform resolves it to a stack instance,
|
||||
Consumers have their own repos and consume ACDL by writing a contract that
|
||||
declares infrastructure. A consumer declares a contract (id + name +
|
||||
environment + infrastructure); the platform resolves it to a stack instance,
|
||||
compiles it, runs security + policy checks, computes a confidence signal,
|
||||
writes an evidence event to the audit outbox, and applies the
|
||||
infrastructure.
|
||||
@@ -104,7 +104,7 @@ infrastructure.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["consumer contract<br/>(uses + module + environment + inputs)"] --> B
|
||||
A["consumer contract<br/>(id + name + environment + infrastructure)"] --> B
|
||||
B["schema validation<br/>(contract schema)"] --> C
|
||||
C["resolve to Target Stack<br/>(contract resolver)"] --> D
|
||||
D["security checks<br/>(adapter)"] --> E
|
||||
@@ -155,7 +155,7 @@ ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \
|
||||
# 3. Run the full platform pipeline (contract -> environment check -> stack ->
|
||||
# adapter -> security checks -> infrastructure plan -> policy checks ->
|
||||
# confidence -> evidence event -> apply). Output is streamed to stdout.
|
||||
bash scripts/run_platform.sh contracts/static-assets.yaml
|
||||
bash scripts/run_platform.sh contracts/static-assets.yml
|
||||
# Expected: "=== PLATFORM E2E OK ==="
|
||||
|
||||
# Or plan-only (contract -> stack -> adapter -> infrastructure plan; no
|
||||
@@ -189,7 +189,7 @@ bash scripts/run_ci.sh
|
||||
### CI/CD pipelines
|
||||
|
||||
The CI/CD pipeline is defined by a **central pipeline contract** — a
|
||||
declarative YAML instance (`pipelines/ci.yaml`) validated against a JSON
|
||||
declarative YAML instance (`pipelines/ci.yml`) validated against a JSON
|
||||
Schema (`schemas/pipeline.schema.json`). Both platform-runner workflows
|
||||
implement the same contract:
|
||||
|
||||
@@ -212,13 +212,13 @@ bash scripts/run_ci.sh --quiet # suppress per-stage banners
|
||||
### Reusable deploy workflow
|
||||
|
||||
The deployment pipeline is defined by a **central deployment pipeline
|
||||
contract** (`pipelines/deploy.yaml`, validated against
|
||||
contract** (`pipelines/contract.yml`, validated against
|
||||
`schemas/deploy-pipeline.schema.json`) and exposed to consumer repos as a
|
||||
**reusable workflow**:
|
||||
|
||||
- `.github/workflows/deploy.yml` — GitHub Actions (production)
|
||||
|
||||
The workflow implements the same stages as `pipelines/deploy.yaml`
|
||||
The workflow implements the same stages as `pipelines/contract.yml`
|
||||
(validate-contract → resolve-stack → security checks → infrastructure plan
|
||||
→ policy checks → confidence → evidence event → apply). A consumer repo
|
||||
invokes the reusable workflow via a **versioned tag** (floating MAJOR +
|
||||
@@ -257,7 +257,7 @@ across all modules; `static-assets` is the worked example.
|
||||
|------|---------|--------|
|
||||
| `core/` | Platform code: contract resolver, confidence signal, outbox writer, environment check, environments, separation of duties, HITL/ledger designs | active |
|
||||
| `schemas/` | JSON Schemas: stack, contract, PolicyCheckResult, pipeline contract, deploy pipeline contract (draft 2020-12) | active |
|
||||
| `pipelines/` | Central pipeline contracts: `ci.yaml` (CI), `deploy.yaml` (deployment) | active |
|
||||
| `pipelines/` | Central pipeline contracts: `ci.yml` (CI), `contract.yml` (deployment) | active |
|
||||
| `adapters/` | Angine adapters — the engine adapter (the only engine-specific code per §12) + the policy adapter | active |
|
||||
| `terraform/` | State backend (S3 + DynamoDB) + platform TF (`terraform/spike/`) + bootstrap scripts (`terraform/bootstrap/`) | active |
|
||||
| `modules/` | Primitives + modules + `registry.json`. Primitives: s3, vpc, ecs-cluster, ecs-service, iam-role, alb, ecr, cloudfront, waf, rds. Modules: microservice, static-assets. Each module has a `examples/` directory with validated contract examples | active |
|
||||
|
||||
@@ -53,12 +53,12 @@ invoke it. The `engine: "kyverno"` enum value is present in
|
||||
The `policies/` directory holds three valid Kyverno `ClusterPolicy`
|
||||
manifests (documentation-only today — the platform does not run them):
|
||||
|
||||
- `disallow-privileged-containers.yaml` — fail pods with
|
||||
- `disallow-privileged-containers.yml` — fail pods with
|
||||
`securityContext.privileged: true`.
|
||||
- `require-resource-labels.yaml` — require `acdl:owner` and
|
||||
- `require-resource-labels.yml` — require `acdl:owner` and
|
||||
`acdl:environment` labels on all pods (mirrors the ACDL tagging standard
|
||||
in [`schemas/tagging-standard.json`](../../schemas/tagging-standard.json)).
|
||||
- `require-image-digests.yaml` — require container images to reference a
|
||||
- `require-image-digests.yml` — require container images to reference a
|
||||
digest (`image@sha256:...`), not a mutable tag.
|
||||
|
||||
## Schema path
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# ACDL sample consumer contract — microservice module (dev)
|
||||
# Per-environment contract (REQ-105). Promotion = running the dev job;
|
||||
# no environment field editing. Interpolation resolves against dev.json.
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: microservice
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -0,0 +1,14 @@
|
||||
# ACDL sample consumer contract — microservice module (dev)
|
||||
# Per-environment contract (REQ-105). Promotion = running the dev job;
|
||||
# no environment field editing. Interpolation resolves against dev.json.
|
||||
id: msvc
|
||||
name: microservice
|
||||
environment: dev
|
||||
infrastructure:
|
||||
microservice:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -1,11 +0,0 @@
|
||||
# ACDL sample consumer contract — microservice module (dr)
|
||||
# Per-environment contract (REQ-105). Promotion = running the dr job;
|
||||
# no environment field editing. Interpolation resolves against dr.json.
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: microservice
|
||||
environment: dr
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -0,0 +1,14 @@
|
||||
# ACDL sample consumer contract — microservice module (dr)
|
||||
# Per-environment contract (REQ-105). Promotion = running the dr job;
|
||||
# no environment field editing. Interpolation resolves against dr.json.
|
||||
id: msvc
|
||||
name: microservice
|
||||
environment: dr
|
||||
infrastructure:
|
||||
microservice:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -1,11 +0,0 @@
|
||||
# ACDL sample consumer contract — microservice module (prod)
|
||||
# Per-environment contract (REQ-105). Promotion = running the prod job;
|
||||
# no environment field editing. Interpolation resolves against prod.json.
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: microservice
|
||||
environment: prod
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -0,0 +1,14 @@
|
||||
# ACDL sample consumer contract — microservice module (prod)
|
||||
# Per-environment contract (REQ-105). Promotion = running the prod job;
|
||||
# no environment field editing. Interpolation resolves against prod.json.
|
||||
id: msvc
|
||||
name: microservice
|
||||
environment: prod
|
||||
infrastructure:
|
||||
microservice:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -1,11 +0,0 @@
|
||||
# ACDL sample consumer contract — microservice module (qa)
|
||||
# Per-environment contract (REQ-105). Promotion = running the qa job;
|
||||
# no environment field editing. Interpolation resolves against qa.json.
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: microservice
|
||||
environment: qa
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -0,0 +1,14 @@
|
||||
# ACDL sample consumer contract — microservice module (qa)
|
||||
# Per-environment contract (REQ-105). Promotion = running the qa job;
|
||||
# no environment field editing. Interpolation resolves against qa.json.
|
||||
id: msvc
|
||||
name: microservice
|
||||
environment: qa
|
||||
infrastructure:
|
||||
microservice:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -1,14 +0,0 @@
|
||||
# ACDL sample consumer contract — microservice module (dev)
|
||||
#
|
||||
# Reference example for an ECS Fargate microservice deployment.
|
||||
# Interpolation (D-081): bucket_name uses the naming pattern that includes
|
||||
# region, aws account id, and environment:
|
||||
# acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: microservice
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -0,0 +1,17 @@
|
||||
# ACDL sample consumer contract — microservice module (dev)
|
||||
#
|
||||
# Reference example for an ECS Fargate microservice deployment.
|
||||
# Interpolation (D-081): bucket_name uses the naming pattern that includes
|
||||
# region, aws account id, and environment:
|
||||
# acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
id: msvc
|
||||
name: microservice
|
||||
environment: dev
|
||||
infrastructure:
|
||||
microservice:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -1,10 +0,0 @@
|
||||
# ACDL sample consumer contract — static-assets module (dev)
|
||||
# Per-environment contract (REQ-105). The dev default
|
||||
# (contracts/static-assets.yaml) remains for backwards compat; this file
|
||||
# is the explicit per-env dev contract. Interpolation resolves against dev.json.
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: static-assets
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
@@ -0,0 +1,13 @@
|
||||
# ACDL sample consumer contract — static-assets module (dev)
|
||||
# Per-environment contract (REQ-105). The dev default
|
||||
# (contracts/static-assets.yml) remains for backwards compat; this file
|
||||
# is the explicit per-env dev contract. Interpolation resolves against dev.json.
|
||||
id: assets
|
||||
name: static-assets
|
||||
environment: dev
|
||||
infrastructure:
|
||||
static-assets:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
@@ -1,9 +1,12 @@
|
||||
# ACDL sample consumer contract — static-assets module (dr)
|
||||
# Per-environment contract (REQ-105). Promotion = running the dr job;
|
||||
# no environment field editing. Interpolation resolves against dr.json.
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: static-assets
|
||||
id: assets
|
||||
name: static-assets
|
||||
environment: dr
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
infrastructure:
|
||||
static-assets:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
@@ -1,9 +1,12 @@
|
||||
# ACDL sample consumer contract — static-assets module (prod)
|
||||
# Per-environment contract (REQ-105). Promotion = running the prod job;
|
||||
# no environment field editing. Interpolation resolves against prod.json.
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: static-assets
|
||||
id: assets
|
||||
name: static-assets
|
||||
environment: prod
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
infrastructure:
|
||||
static-assets:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
@@ -1,9 +1,12 @@
|
||||
# ACDL sample consumer contract — static-assets module (qa)
|
||||
# Per-environment contract (REQ-105). Promotion = running the qa job;
|
||||
# no environment field editing. Interpolation resolves against qa.json.
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: static-assets
|
||||
id: assets
|
||||
name: static-assets
|
||||
environment: qa
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
infrastructure:
|
||||
static-assets:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
@@ -1,23 +0,0 @@
|
||||
# ACDL sample consumer contract — static-assets module (dev)
|
||||
#
|
||||
# This is the reference example for a consumer contract. It declares:
|
||||
# uses: the central ACDL deployment pipeline to reference
|
||||
# module: which module to deploy (must match a registry key)
|
||||
# environment: which environment to deploy to (dev = autonomous)
|
||||
# inputs: module-specific inputs
|
||||
#
|
||||
# Validated against schemas/contract.schema.json.
|
||||
# Resolved by core/contract_resolver.py to a Target Stack instance.
|
||||
#
|
||||
# Interpolation (D-081): ${env.<field>} + ${contract.<field>} tokens are
|
||||
# expanded by the resolver from the environment onboarding JSON. The
|
||||
# bucket_name below demonstrates the naming pattern that includes region,
|
||||
# aws account id, and environment:
|
||||
# acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: static-assets
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
@@ -0,0 +1,29 @@
|
||||
# ACDL sample consumer contract — static-assets module (dev)
|
||||
#
|
||||
# This is the reference example for a consumer contract. It declares:
|
||||
# id: short operational acronym (becomes stack.name for state, tags, evidence)
|
||||
# name: full human-readable stack name (becomes stack.title for display)
|
||||
# environment: which environment to deploy to (dev = autonomous)
|
||||
# infrastructure: map of modules to deploy (keyed by module registry name)
|
||||
# <module>:
|
||||
# version: module version pin (defaults to latest published)
|
||||
# inputs: module-specific inputs
|
||||
#
|
||||
# Validated against schemas/contract.schema.json.
|
||||
# Resolved by core/contract_resolver.py to a Target Stack instance.
|
||||
#
|
||||
# Interpolation (D-081): ${env.<field>} + ${contract.<field>} tokens are
|
||||
# expanded by the resolver from the environment onboarding JSON. The
|
||||
# bucket_name below demonstrates the naming pattern that includes region,
|
||||
# aws account id, and environment:
|
||||
# acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
|
||||
id: assets
|
||||
name: static-assets
|
||||
environment: dev
|
||||
infrastructure:
|
||||
static-assets:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
@@ -5,17 +5,27 @@ The contract resolver is the bridge between the consumer's declared intent
|
||||
Stack JSON instance). It:
|
||||
|
||||
1. Loads and validates the contract against schemas/contract.schema.json.
|
||||
2. Looks up the module name in modules/registry.json.
|
||||
3. If the module is an L1 primitive: builds a stack instance directly from
|
||||
the interface.json + contract inputs.
|
||||
4. If the module is an L2 composition: loads the composition.json, expands
|
||||
children to stack resources, resolves wires to ref: expressions, and
|
||||
emits the full stack instance.
|
||||
2. For each module in the contract's `infrastructure` map:
|
||||
a. Looks up the module name + version in modules/registry.json
|
||||
(version defaults to the latest non-deprecated entry when omitted).
|
||||
b. If the module is an L1 primitive: builds a stack fragment from
|
||||
the interface.json + module inputs.
|
||||
c. If the module is an L2 composition: loads the composition.json,
|
||||
expands children to stack resources, resolves wires to ref:
|
||||
expressions, and emits the fragment.
|
||||
3. Merges all module fragments into a single Target Stack instance:
|
||||
- stack.name = contract.id (the short operational acronym)
|
||||
- stack.title = contract.name (the full human-readable name)
|
||||
- When the contract has one module: resource IDs are unprefixed
|
||||
(backward-compatible with existing stack consumers).
|
||||
- When the contract has multiple modules: resource IDs are prefixed
|
||||
with the module name (e.g. `microservice-vpc`) to avoid collisions,
|
||||
and all ref:/parent references are rewritten to match.
|
||||
|
||||
The output is a JSON instance valid against schemas/stack.schema.json,
|
||||
ready for the Terraform adapter to compile.
|
||||
|
||||
CLI: contract_resolver.py <contract.yaml> <out.json>
|
||||
CLI: contract_resolver.py <contract.yml> <out.json>
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -150,68 +160,77 @@ def _resolve_wire_value(wire, contract_inputs, child_outputs):
|
||||
return None
|
||||
|
||||
|
||||
def resolve_l1(contract, registry, repo_root):
|
||||
"""Resolve a contract referencing an L1 primitive to a stack instance."""
|
||||
module_name = contract["module"]
|
||||
module_ref = f"{module_name}@1.0.0"
|
||||
inputs = contract.get("inputs", {})
|
||||
environment = contract.get("environment", "dev")
|
||||
def _latest_version(registry, module_name):
|
||||
"""Return the latest non-deprecated version string for a module.
|
||||
|
||||
Falls back to the highest version even if all are deprecated.
|
||||
"""
|
||||
versions = registry[module_name]
|
||||
non_deprecated = [(v, e) for v, e in versions.items()
|
||||
if not e.get("deprecated", False)]
|
||||
if not non_deprecated:
|
||||
non_deprecated = list(versions.items())
|
||||
non_deprecated.sort(key=lambda x: [int(p) for p in x[0].split(".")],
|
||||
reverse=True)
|
||||
return non_deprecated[0][0]
|
||||
|
||||
|
||||
def _resolve_l1(module_name, version, inputs, registry, repo_root):
|
||||
"""Resolve a single L1 primitive module to a stack-fragment (resources list)."""
|
||||
module_ref = f"{module_name}@{version}"
|
||||
|
||||
# Load the interface
|
||||
entry = registry[module_name]["1.0.0"]
|
||||
entry = registry[module_name][version]
|
||||
iface_path = os.path.join(repo_root, entry["interface"])
|
||||
iface = _load_json(iface_path)
|
||||
|
||||
# Build the stack instance
|
||||
stack_instance = {
|
||||
"version": "1.0.0",
|
||||
"stack": {
|
||||
"name": module_name,
|
||||
"kind": "l1",
|
||||
"depth": 1,
|
||||
# Build the resource
|
||||
resource = {
|
||||
"id": iface.get("type", module_name).split(":")[-1]
|
||||
if ":" in iface.get("type", "") else module_name,
|
||||
"type": iface["type"],
|
||||
"module": module_ref,
|
||||
"inputs": dict(inputs),
|
||||
"outputs": {
|
||||
out_name: {"type": out_spec.get("type", "string")}
|
||||
for out_name, out_spec in iface.get("outputs", {}).items()
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": iface.get("type", module_name).split(":")[-1]
|
||||
if ":" in iface.get("type", "") else module_name,
|
||||
"type": iface["type"],
|
||||
"module": module_ref,
|
||||
"inputs": dict(inputs),
|
||||
"outputs": {
|
||||
out_name: {"type": out_spec.get("type", "string")}
|
||||
for out_name, out_spec in iface.get("outputs", {}).items()
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
# Add NFRs if present in the interface
|
||||
nfrs = iface.get("nfrs", {})
|
||||
if nfrs:
|
||||
stack_instance["resources"][0]["nfrs"] = nfrs
|
||||
resource["nfrs"] = nfrs
|
||||
|
||||
return stack_instance
|
||||
return {
|
||||
"kind": "l1",
|
||||
"depth": 1,
|
||||
"resources": [resource],
|
||||
"features": {},
|
||||
"outputs": {},
|
||||
}
|
||||
|
||||
|
||||
def resolve_l2(contract, registry, repo_root):
|
||||
"""Resolve a contract referencing an L2 composition to a stack instance."""
|
||||
module_name = contract["module"]
|
||||
inputs = contract.get("inputs", {})
|
||||
def _resolve_l2(module_name, version, inputs, registry, repo_root):
|
||||
"""Resolve a single L2 composition module to a stack-fragment.
|
||||
|
||||
Returns a dict with: kind, depth, resources, features, outputs.
|
||||
The caller is responsible for merging fragments and setting stack.name/title.
|
||||
"""
|
||||
# Load the composition
|
||||
entry = registry[module_name]["1.0.0"]
|
||||
entry = registry[module_name][version]
|
||||
comp_path = os.path.join(repo_root, entry["interface"])
|
||||
composition = _load_json(comp_path)
|
||||
|
||||
# Track child outputs for wire resolution
|
||||
# child_outputs[childId] = {outputName: resourceId}
|
||||
# child_outputs[childId] = {outputName -> resourceId}
|
||||
# For single-resource L1s, resourceId == childId
|
||||
# For multi-resource L1s, resourceId is the expanded sub-resource id
|
||||
child_outputs = {}
|
||||
# child_input_map[childId] = {inputName: sub_resource_id} for multi-resource L1s
|
||||
# child_input_map[childId] = {inputName -> sub_resource_id} for multi-resource L1s
|
||||
# so a wire targeting <childId>.inputs.<name> routes to the sub-resource
|
||||
# that actually declares that input (P1-1 — desired_count → aws:ecs:service,
|
||||
# family → aws:ecs:task_definition).
|
||||
# that actually declares that input (P1-1 — desired_count -> aws:ecs:service,
|
||||
# family -> aws:ecs:task_definition).
|
||||
child_input_map = {}
|
||||
resources = []
|
||||
|
||||
@@ -220,9 +239,10 @@ def resolve_l2(contract, registry, repo_root):
|
||||
child_id = child["id"]
|
||||
child_module = child["module"]
|
||||
child_name = child_module.split("@")[0]
|
||||
child_version = child_module.split("@")[1] if "@" in child_module else "1.0.0"
|
||||
|
||||
# Load the child's interface to get type and outputs
|
||||
child_entry = registry[child_name]["1.0.0"]
|
||||
child_entry = registry[child_name][child_version]
|
||||
child_iface_path = os.path.join(repo_root, child_entry["interface"])
|
||||
child_iface = _load_json(child_iface_path)
|
||||
|
||||
@@ -309,20 +329,10 @@ def resolve_l2(contract, registry, repo_root):
|
||||
res["inputs"][input_name] = value
|
||||
break
|
||||
|
||||
# Build the stack instance
|
||||
stack_instance = {
|
||||
"version": "1.0.0",
|
||||
"stack": {
|
||||
"name": module_name,
|
||||
"kind": "l2",
|
||||
"depth": composition.get("depth", 1),
|
||||
},
|
||||
"resources": resources,
|
||||
}
|
||||
|
||||
# REQ-87: Propagate deletion_protection feature flag from contract inputs
|
||||
# to all children's NFRs. When inputs.deletion_protection is false,
|
||||
# all resources get deletion_protection=false (used by decommission).
|
||||
features = {}
|
||||
deletion_protection_input = inputs.get("deletion_protection", True)
|
||||
if deletion_protection_input is not True:
|
||||
for res in resources:
|
||||
@@ -331,9 +341,7 @@ def resolve_l2(contract, registry, repo_root):
|
||||
res["nfrs"]["deletion_protection"] = deletion_protection_input
|
||||
# Also record the feature flag on the stack object for introspection.
|
||||
if "deletion_protection" in inputs:
|
||||
stack_instance["stack"]["features"] = {
|
||||
"deletion_protection": deletion_protection_input
|
||||
}
|
||||
features["deletion_protection"] = deletion_protection_input
|
||||
|
||||
# P1-7: Process the composition's outputs[] array to build stack.outputs.
|
||||
# Each output wire: {"from": "<childId>.outputs.<name>", "to": "stack.outputs.<outName>"}
|
||||
@@ -363,10 +371,55 @@ def resolve_l2(contract, registry, repo_root):
|
||||
"from": src_resource_id,
|
||||
"output": src_output,
|
||||
}
|
||||
if stack_outputs:
|
||||
stack_instance["outputs"] = stack_outputs
|
||||
|
||||
return stack_instance
|
||||
return {
|
||||
"kind": "l2",
|
||||
"depth": composition.get("depth", 1),
|
||||
"resources": resources,
|
||||
"features": features,
|
||||
"outputs": stack_outputs,
|
||||
}
|
||||
|
||||
|
||||
def _namespace_resources(resources, module_name):
|
||||
"""Prefix all resource IDs with the module name for multi-module contracts.
|
||||
|
||||
Rewrites resource 'id', 'parent', and ref: expressions in inputs/outputs
|
||||
so cross-references stay consistent within the module fragment.
|
||||
"""
|
||||
prefix = f"{module_name}-"
|
||||
# Build the old->new id mapping
|
||||
id_map = {res["id"]: f"{prefix}{res['id']}" for res in resources}
|
||||
|
||||
def _rewrite_ref(val):
|
||||
"""Recursively rewrite ref:<id>.<out> and parent:<id> strings."""
|
||||
if isinstance(val, str):
|
||||
if val.startswith("ref:"):
|
||||
# ref:<resourceId>.<outputName>
|
||||
rest = val[4:]
|
||||
if "." in rest:
|
||||
rid, outname = rest.split(".", 1)
|
||||
if rid in id_map:
|
||||
return f"ref:{id_map[rid]}.{outname}"
|
||||
return val
|
||||
return val
|
||||
if isinstance(val, dict):
|
||||
return {k: _rewrite_ref(v) for k, v in val.items()}
|
||||
if isinstance(val, list):
|
||||
return [_rewrite_ref(v) for v in val]
|
||||
return val
|
||||
|
||||
for res in resources:
|
||||
res["id"] = id_map[res["id"]]
|
||||
# Rewrite parent
|
||||
if "parent" in res and res["parent"] in id_map:
|
||||
res["parent"] = id_map[res["parent"]]
|
||||
# Rewrite all ref: expressions in inputs and outputs
|
||||
res["inputs"] = _rewrite_ref(res.get("inputs", {}))
|
||||
if "outputs" in res:
|
||||
res["outputs"] = _rewrite_ref(res["outputs"])
|
||||
|
||||
return resources, id_map
|
||||
|
||||
|
||||
def decommission_transform(stack_instance):
|
||||
@@ -432,24 +485,105 @@ def resolve(contract_path, repo_root=None, environment_override=None):
|
||||
# reference the environment by ${env.environment}).
|
||||
env["environment"] = env.get("name", env_name)
|
||||
context = {"env": env, "contract": contract}
|
||||
contract["inputs"] = _expand_vars(contract.get("inputs", {}), context)
|
||||
|
||||
# Expand interpolation tokens in each module's inputs
|
||||
infrastructure = contract.get("infrastructure", {})
|
||||
for module_name, module_entry in infrastructure.items():
|
||||
module_entry["inputs"] = _expand_vars(
|
||||
module_entry.get("inputs", {}), context)
|
||||
|
||||
# Load registry
|
||||
registry = _load_json(os.path.join(repo_root, "modules", "registry.json"))
|
||||
|
||||
module_name = contract["module"]
|
||||
if module_name not in registry:
|
||||
raise ValueError(f"module '{module_name}' not found in registry")
|
||||
# Validate every module exists in the registry, then resolve each
|
||||
module_names = list(infrastructure.keys())
|
||||
fragments = []
|
||||
for module_name in module_names:
|
||||
if module_name not in registry:
|
||||
raise ValueError(f"module '{module_name}' not found in registry")
|
||||
module_entry = infrastructure[module_name]
|
||||
# Default version to latest non-deprecated
|
||||
version = module_entry.get("version")
|
||||
if version is None:
|
||||
version = _latest_version(registry, module_name)
|
||||
elif version not in registry[module_name]:
|
||||
raise ValueError(
|
||||
f"module '{module_name}' version '{version}' not found in registry")
|
||||
module_inputs = module_entry.get("inputs", {})
|
||||
|
||||
# Determine if L1 or L2
|
||||
entry = registry[module_name]["1.0.0"]
|
||||
interface_path = entry["interface"]
|
||||
is_l2 = "l2" in interface_path or "composition" in interface_path
|
||||
# Determine if L1 or L2
|
||||
entry = registry[module_name][version]
|
||||
interface_path = entry["interface"]
|
||||
is_l2 = "l2" in interface_path or "composition" in interface_path
|
||||
|
||||
if is_l2:
|
||||
stack_instance = resolve_l2(contract, registry, repo_root)
|
||||
if is_l2:
|
||||
fragment = _resolve_l2(module_name, version, module_inputs,
|
||||
registry, repo_root)
|
||||
else:
|
||||
fragment = _resolve_l1(module_name, version, module_inputs,
|
||||
registry, repo_root)
|
||||
fragments.append((module_name, fragment))
|
||||
|
||||
# Merge fragments into a single stack instance
|
||||
all_resources = []
|
||||
max_depth = 1
|
||||
any_l2 = False
|
||||
merged_features = {}
|
||||
merged_outputs = {}
|
||||
|
||||
multi_module = len(fragments) > 1
|
||||
|
||||
for module_name, fragment in fragments:
|
||||
if fragment["kind"] == "l2":
|
||||
any_l2 = True
|
||||
max_depth = max(max_depth, fragment["depth"])
|
||||
merged_features.update(fragment.get("features", {}))
|
||||
|
||||
if multi_module:
|
||||
# Namespace resource IDs to avoid cross-module collisions
|
||||
namespaced, id_map = _namespace_resources(
|
||||
fragment["resources"], module_name)
|
||||
# Namespace the fragment's stack outputs (from refs)
|
||||
for out_name, out_spec in fragment.get("outputs", {}).items():
|
||||
src_id = out_spec.get("from", "")
|
||||
if src_id in id_map:
|
||||
out_spec["from"] = id_map[src_id]
|
||||
merged_outputs[f"{module_name}-{out_name}"] = out_spec
|
||||
all_resources.extend(namespaced)
|
||||
else:
|
||||
# Single module: keep IDs as-is (backward compatible)
|
||||
merged_outputs.update(fragment.get("outputs", {}))
|
||||
all_resources.extend(fragment["resources"])
|
||||
|
||||
# Determine stack kind: L2 if any module is L2 or if multi-module
|
||||
if multi_module:
|
||||
kind = "l2"
|
||||
elif any_l2:
|
||||
kind = "l2"
|
||||
else:
|
||||
stack_instance = resolve_l1(contract, registry, repo_root)
|
||||
kind = "l1"
|
||||
|
||||
stack_instance = {
|
||||
"version": "1.0.0",
|
||||
"stack": {
|
||||
"name": contract["id"],
|
||||
"kind": kind,
|
||||
"depth": max_depth,
|
||||
},
|
||||
"resources": all_resources,
|
||||
}
|
||||
|
||||
# Add the human-readable title
|
||||
if contract.get("name"):
|
||||
stack_instance["stack"]["title"] = contract["name"]
|
||||
|
||||
# Add features if any were set
|
||||
if merged_features:
|
||||
stack_instance["stack"]["features"] = merged_features
|
||||
|
||||
# Add stack-level outputs
|
||||
if merged_outputs:
|
||||
stack_instance["outputs"] = merged_outputs
|
||||
|
||||
# Validate against stack schema
|
||||
stack_schema = _load_json(os.path.join(repo_root, "schemas", "stack.schema.json"))
|
||||
@@ -460,7 +594,7 @@ def resolve(contract_path, repo_root=None, environment_override=None):
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 3:
|
||||
print("usage: contract_resolver.py <contract.yaml> <out.json> [--environment <name>]", file=sys.stderr)
|
||||
print("usage: contract_resolver.py <contract.yml> <out.json> [--environment <name>]", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
contract_path = sys.argv[1]
|
||||
out_path = sys.argv[2]
|
||||
@@ -474,5 +608,4 @@ if __name__ == "__main__":
|
||||
env_override = os.environ["ACDL_ENVIRONMENT_OVERRIDE"]
|
||||
result = resolve(contract_path, environment_override=env_override)
|
||||
with open(out_path, "w") as fh:
|
||||
json.dump(result, fh, indent=2)
|
||||
print(f"resolver: resolved {contract_path} -> {out_path}", file=sys.stderr)
|
||||
json.dump(result, fh, indent=2)
|
||||
@@ -488,7 +488,7 @@ def run_local_e2e(contract_path: str, repo_root: Optional[Path] = None) -> Dict[
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
contract = sys.argv[1] if len(sys.argv) > 1 else "contracts/microservice.yaml"
|
||||
contract = sys.argv[1] if len(sys.argv) > 1 else "contracts/microservice.yml"
|
||||
os.environ["ACDL_LOCAL_TIER"] = "1"
|
||||
result = run_local_e2e(contract)
|
||||
print(json.dumps(result, indent=2))
|
||||
@@ -120,7 +120,7 @@ def _check_contract_schema_validation() -> Tuple[Status, str]:
|
||||
"import json, yaml, jsonschema; "
|
||||
"s=json.load(open('schemas/contract.schema.json')); "
|
||||
"[jsonschema.validate(yaml.safe_load(open(f)), s) "
|
||||
" for f in ['contracts/static-assets.yaml','contracts/microservice.yaml']]; "
|
||||
" for f in ['contracts/static-assets.yml','contracts/microservice.yml']]; "
|
||||
"print('2 sample contracts validate')",
|
||||
])
|
||||
|
||||
@@ -144,7 +144,7 @@ def _check_resolver_static_assets() -> Tuple[Status, str]:
|
||||
try:
|
||||
return _check_subprocess([
|
||||
"python3", "core/contract_resolver.py",
|
||||
"contracts/static-assets.yaml", out,
|
||||
"contracts/static-assets.yml", out,
|
||||
])
|
||||
finally:
|
||||
try:
|
||||
@@ -160,7 +160,7 @@ def _check_resolver_microservice() -> Tuple[Status, str]:
|
||||
try:
|
||||
return _check_subprocess([
|
||||
"python3", "core/contract_resolver.py",
|
||||
"contracts/microservice.yaml", out,
|
||||
"contracts/microservice.yml", out,
|
||||
])
|
||||
finally:
|
||||
try:
|
||||
@@ -177,7 +177,7 @@ def _check_adapter_emits_terraform() -> Tuple[Status, str]:
|
||||
os.makedirs(tf_dir, exist_ok=True)
|
||||
rc, out, err = _run_subprocess([
|
||||
"python3", "core/contract_resolver.py",
|
||||
"contracts/static-assets.yaml", stack_path,
|
||||
"contracts/static-assets.yml", stack_path,
|
||||
])
|
||||
if rc != 0:
|
||||
return "Broken", f"resolver failed: {err.strip()[-200:]}"
|
||||
@@ -192,13 +192,17 @@ def _check_adapter_emits_terraform() -> Tuple[Status, str]:
|
||||
|
||||
|
||||
def _check_interpolation() -> Tuple[Status, str]:
|
||||
"""CAP-006: contract interpolation expands ${env.*} / ${contract.*}."""
|
||||
"""CAP-006: contract interpolation expands ${env.*} / ${contract.*}.
|
||||
|
||||
P57: the contract's `module` field was dropped in favor of `id`
|
||||
(short acronym) + `infrastructure` map; the interpolation check uses
|
||||
`contract.id` (the surviving field)."""
|
||||
return _check_subprocess([
|
||||
"python3", "-c",
|
||||
"import sys; sys.path.insert(0,'.'); "
|
||||
"from core.contract_resolver import _expand_vars; "
|
||||
"ctx={'env':{'environment':'qa','account_id':'123'},'contract':{'module':'ms'}}; "
|
||||
"assert _expand_vars('acdl-${env.environment}-${contract.module}', ctx)=='acdl-qa-ms'; "
|
||||
"ctx={'env':{'environment':'qa','account_id':'123'},'contract':{'id':'assets'}}; "
|
||||
"assert _expand_vars('acdl-${env.environment}-${contract.id}', ctx)=='acdl-qa-assets'; "
|
||||
"print('interpolation ok')",
|
||||
])
|
||||
|
||||
@@ -276,7 +280,7 @@ def _check_local_e2e_microservice() -> Tuple[Status, str]:
|
||||
This is the local-tier half of the headline E2E; the live-AWS half
|
||||
lands in Phase 54 (D-093)."""
|
||||
return _check_subprocess(
|
||||
["python3", "core/local_emulators.py", "contracts/microservice.yaml"],
|
||||
["python3", "core/local_emulators.py", "contracts/microservice.yml"],
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
@@ -284,7 +288,7 @@ def _check_local_e2e_microservice() -> Tuple[Status, str]:
|
||||
def _check_local_e2e_static_assets() -> Tuple[Status, str]:
|
||||
"""CAP-012: local E2E on the static-assets stack (no ECS service)."""
|
||||
return _check_subprocess(
|
||||
["python3", "core/local_emulators.py", "contracts/static-assets.yaml"],
|
||||
["python3", "core/local_emulators.py", "contracts/static-assets.yml"],
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
@@ -324,7 +328,7 @@ def _check_live_terraform_plan_microservice() -> Tuple[Status, str]:
|
||||
os.makedirs(tf_dir, exist_ok=True)
|
||||
rc, out, err = _run_subprocess([
|
||||
"python3", "core/contract_resolver.py",
|
||||
"contracts/microservice.yaml", stack_path,
|
||||
"contracts/microservice.yml", stack_path,
|
||||
])
|
||||
if rc != 0:
|
||||
return "Broken", f"resolver failed: {err.strip()[-200:]}"
|
||||
@@ -364,7 +368,7 @@ def _check_live_terraform_plan_static_assets() -> Tuple[Status, str]:
|
||||
os.makedirs(tf_dir, exist_ok=True)
|
||||
rc, out, err = _run_subprocess([
|
||||
"python3", "core/contract_resolver.py",
|
||||
"contracts/static-assets.yaml", stack_path,
|
||||
"contracts/static-assets.yml", stack_path,
|
||||
])
|
||||
if rc != 0:
|
||||
return "Broken", f"resolver failed: {err.strip()[-200:]}"
|
||||
|
||||
@@ -7,10 +7,10 @@ step applies to `microservice` and any future module.
|
||||
|
||||
## The model
|
||||
|
||||
Consumers have their own repos and consume ACDL by referencing `uses:` the
|
||||
central pipeline definitions. The consumer declares a **contract** (which
|
||||
module, which environment, which inputs); the ACDL platform owns the
|
||||
pipelines, modules, engine adapter, and evidence stream.
|
||||
Consumers have their own repos and consume ACDL by writing a contract
|
||||
that declares infrastructure (one or more modules), an environment, and inputs. The consumer declares a **contract** (which infrastructure, which
|
||||
environment, which inputs); the ACDL platform owns the pipelines, modules,
|
||||
engine adapter, and evidence stream.
|
||||
|
||||
You do not write infrastructure modules, workflow YAML, or adapter code.
|
||||
You write a contract YAML file and the platform does the rest. Your
|
||||
@@ -27,13 +27,13 @@ flowchart LR
|
||||
## Versioning the `uses:` reference
|
||||
|
||||
The central deployment pipeline is **always versioned with floating MAJOR
|
||||
and MINOR tags** (e.g. `acdl/pipelines/deploy.yaml@v1.9`). Version
|
||||
and MINOR tags** (e.g. `acdl/pipelines/contract.yml@v1.9`). Version
|
||||
constraints cannot be expressed inside the contract, so the tag in
|
||||
`uses:` is the only immutability lever a consumer has. See
|
||||
[Versioning](pipeline/versioning) for the full rationale.
|
||||
|
||||
**Unversioned references are discouraged.** Do not use `@main` or a bare
|
||||
`acdl/pipelines/deploy.yaml`.
|
||||
`acdl/pipelines/contract.yml`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -53,7 +53,7 @@ platform-managed. See [Environments](environments/).
|
||||
## Step 1 — Create a consumer repo
|
||||
|
||||
Create a repository for your application. The top level holds your app
|
||||
code; your contract lives at `.acdl/contract.yaml`. Example for a static
|
||||
code; your contract lives at `.acdl/contract.yml`. Example for a static
|
||||
site:
|
||||
|
||||
```
|
||||
@@ -83,53 +83,64 @@ my-microservice/
|
||||
```
|
||||
|
||||
Your app code lives at the top level. Your contract lives at
|
||||
`.acdl/contract.yaml` regardless of the module you deploy. Your CI
|
||||
`.acdl/contract.yml` regardless of the module you deploy. Your CI
|
||||
definition lives at `.github/workflows/deploy.yml`.
|
||||
|
||||
## Step 2 — Reference the central pipeline
|
||||
|
||||
In your contract YAML, declare `uses:` pointing at the central ACDL
|
||||
deployment pipeline with a **versioned tag** (floating MAJOR + MINOR):
|
||||
In your CI workflow (`.github/workflows/deploy.yml`), reference the central
|
||||
ACDL deployment workflow with a **versioned tag** (floating MAJOR + MINOR):
|
||||
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
jobs:
|
||||
deploy:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.9
|
||||
with:
|
||||
contract: .acdl/contract.yml
|
||||
environment: dev
|
||||
```
|
||||
|
||||
This tells the platform to run the standard deployment pipeline:
|
||||
validate-contract → resolve-stack → security checks → infrastructure plan →
|
||||
policy checks → confidence → evidence event → apply.
|
||||
The versioned tag is the only immutability lever — the consumer's CI workflow
|
||||
pins the platform version. The contract itself no longer carries a `uses:`
|
||||
field; the version pin lives in the CI workflow reference.
|
||||
|
||||
## Step 3 — Define the contract
|
||||
|
||||
Write `.acdl/contract.yaml`. The `static-assets` example:
|
||||
Write `.acdl/contract.yml`. The `static-assets` example:
|
||||
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: static-assets
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_name: my-static-site-assets
|
||||
region: us-east-1
|
||||
id: assets
|
||||
infrastructure:
|
||||
static-assets:
|
||||
inputs:
|
||||
bucket_name: my-static-site-assets
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: static-assets
|
||||
```
|
||||
|
||||
A `microservice` example:
|
||||
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
module: microservice
|
||||
environment: dev
|
||||
inputs:
|
||||
image: my-registry/my-microservice:latest
|
||||
port: 8080
|
||||
env:
|
||||
LOG_LEVEL: info
|
||||
id: msvc
|
||||
infrastructure:
|
||||
microservice:
|
||||
inputs:
|
||||
env:
|
||||
LOG_LEVEL: info
|
||||
image: my-registry/my-microservice:latest
|
||||
port: 8080
|
||||
version: 1.0.0
|
||||
name: microservice
|
||||
```
|
||||
|
||||
### Contract fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/deploy.yaml@v1.9`). Bare or `@main` references are discouraged. See [Versioning](pipeline/versioning). |
|
||||
| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/contract.yml@v1.9`). Bare or `@main` references are discouraged. See [Versioning](pipeline/versioning). |
|
||||
| `module` | string | yes | Module name from the registry — any primitive or module (e.g. `static-assets`, `microservice`, `s3`). See the [module catalog](modules/). |
|
||||
| `environment` | string | yes | The platform-managed environment to deploy to (e.g. `dev`). See [Environments](environments/). |
|
||||
| `inputs` | object | yes | Module-specific inputs (see the module's README). |
|
||||
@@ -168,7 +179,7 @@ jobs:
|
||||
deploy:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.9
|
||||
with:
|
||||
contract: .acdl/contract.yaml
|
||||
contract: .acdl/contract.yml
|
||||
```
|
||||
|
||||
That is the entire consumer-side workflow. When you push to `main`:
|
||||
@@ -181,7 +192,7 @@ That is the entire consumer-side workflow. When you push to `main`:
|
||||
never clone the platform repo yourself.
|
||||
4. The runner installs the runtime dependencies the platform requires.
|
||||
5. The runner invokes `scripts/run_platform.sh` against your
|
||||
`.acdl/contract.yaml`.
|
||||
`.acdl/contract.yml`.
|
||||
|
||||
You see the streamed output (infrastructure plan, policy-check results,
|
||||
confidence signal) in your run logs. The `--check-only` and `--plan-only`
|
||||
@@ -202,7 +213,7 @@ static key in `.env.secrets` (gitignored) is rotated **out of band by you**
|
||||
locally-held copies.
|
||||
|
||||
```bash
|
||||
bash scripts/run_platform.sh --check-only path/to/your/.acdl/contract.yaml
|
||||
bash scripts/run_platform.sh --check-only path/to/your/.acdl/contract.yml
|
||||
```
|
||||
|
||||
## Step 5 — What the pipeline does
|
||||
@@ -278,11 +289,16 @@ push your container image to the ECR repo the platform created.
|
||||
|
||||
## Step 8 — Promote to qa / prod
|
||||
|
||||
Change `environment` in your contract (keeping the same versioned `uses:`):
|
||||
Change `environment` in your contract (the infrastructure stays the same):
|
||||
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.9
|
||||
id: assets
|
||||
name: static-assets
|
||||
environment: qa # QA attestation + confidence >= 0.75
|
||||
infrastructure:
|
||||
static-assets:
|
||||
version: "1.0.0"
|
||||
inputs: { ... }
|
||||
```
|
||||
|
||||
Higher environments require human attestation (a platform-runner deployment
|
||||
@@ -305,7 +321,7 @@ per-module extension points. Common examples:
|
||||
|
||||
| Resource | Path | Description |
|
||||
|----------|------|-------------|
|
||||
| Central deployment pipeline contract | `pipelines/deploy.yaml` | The pipeline stages your contract references. |
|
||||
| Central deployment pipeline contract | `pipelines/contract.yml` | The pipeline stages your contract references. |
|
||||
| Reusable deploy workflow | `.github/workflows/deploy.yml` | The workflow your repo invokes via `uses:`. |
|
||||
| Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. |
|
||||
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
|
||||
@@ -339,7 +355,7 @@ destruction:
|
||||
```yaml
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.8
|
||||
with:
|
||||
contract: .acdl/contract.yaml
|
||||
contract: .acdl/contract.yml
|
||||
mode: decommission
|
||||
changeRequestId: "CHG0678912"
|
||||
```
|
||||
@@ -388,18 +404,20 @@ 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
|
||||
contract per environment (e.g. `.acdl/static-assets.dev.yml`,
|
||||
`.acdl/static-assets.qa.yml`, …). 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}
|
||||
id: assets
|
||||
infrastructure:
|
||||
static-assets:
|
||||
inputs:
|
||||
bucket_name: acdl-${env.environment}-${contract.id}-${env.account_id}-${env.region}
|
||||
region: ${env.region}
|
||||
version: 1.0.0
|
||||
name: static-assets
|
||||
```
|
||||
|
||||
**Shape 2 — single contract + `environment` workflow input:** the
|
||||
@@ -421,7 +439,7 @@ jobs:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.9
|
||||
with:
|
||||
environment: qa
|
||||
contract: .acdl/contract.yaml
|
||||
contract: .acdl/contract.yml
|
||||
```
|
||||
|
||||
### One job per environment
|
||||
@@ -451,7 +469,7 @@ duties check blocks a prod promotion when `approver_qa == approver_prod`
|
||||
| `${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.id}` | the contract's operational acronym | `assets` |
|
||||
| `${contract.environment}` | the contract's environment field | `qa` |
|
||||
| `${contract.inputs.<name>}` | a contract input value | (as declared) |
|
||||
|
||||
|
||||
@@ -1,44 +1,57 @@
|
||||
# Contracts
|
||||
|
||||
A consumer declares intent in a **contract** — a small YAML file that
|
||||
references the central deploy pipeline, names a module, selects an
|
||||
environment, and supplies module-specific inputs. The platform validates,
|
||||
resolves, and deploys it.
|
||||
names infrastructure (one or more modules), selects an environment, and
|
||||
supplies module-specific inputs. The platform validates, resolves, and
|
||||
deploys it.
|
||||
|
||||
## The contract file
|
||||
|
||||
A consumer repo keeps its contract at `.acdl/contract.yaml`. A minimal
|
||||
A consumer repo keeps its contract at `.acdl/contract.yml`. A minimal
|
||||
example (the `static-assets` module):
|
||||
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: static-assets
|
||||
id: assets
|
||||
name: static-assets
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_name: my-static-site-assets
|
||||
region: us-east-1
|
||||
infrastructure:
|
||||
static-assets:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
bucket_name: my-static-site-assets
|
||||
region: us-east-1
|
||||
```
|
||||
|
||||
A `microservice` example:
|
||||
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: microservice
|
||||
id: msvc
|
||||
name: microservice
|
||||
environment: dev
|
||||
inputs:
|
||||
image: my-registry/my-microservice:latest
|
||||
port: 8080
|
||||
env:
|
||||
LOG_LEVEL: info
|
||||
infrastructure:
|
||||
microservice:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
image: my-registry/my-microservice:latest
|
||||
port: 8080
|
||||
env:
|
||||
LOG_LEVEL: info
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `uses` | string | yes | Reference to the central deploy pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/deploy.yaml@v1.6`). Bare or `@main` references are discouraged. See [Versioning](../pipeline/versioning). |
|
||||
| `module` | string | yes | Module name from the registry — any primitive or module (e.g. `static-assets`, `microservice`, `s3`). See the [module catalog](../modules/). |
|
||||
| `environment` | string | yes | The platform-managed environment to deploy to (e.g. `dev`). See [Environments](../environments/). |
|
||||
| `id` | string | yes | Short operational acronym (3-6 chars, `^[a-z][a-z0-9-]{2,5}$`). Becomes the stack name used for the Terraform state key, ECS service name, outbox event identity, and resource naming prefix. |
|
||||
| `name` | string | yes | Full human-readable stack name (min 3 chars). Becomes the stack title used for display in PR comments, evidence records, and leadership dashboards. |
|
||||
| `environment` | string | yes | The platform-managed environment to deploy to (`dev`/`qa`/`prod`/`dr`). See [Environments](../environments/). |
|
||||
| `infrastructure` | object | yes | Map of modules to deploy, keyed by module registry name. Each entry has an optional `version` (defaults to latest published) and required `inputs`. One entry = single-module deploy; N entries = multi-module manifest deployed in one pipeline run. |
|
||||
|
||||
### Infrastructure entry fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `version` | string | no | Module version pin (semver `X.Y.Z`). Omitted = latest non-deprecated version from the registry. |
|
||||
| `inputs` | object | yes | Module-specific inputs (see the module's README). |
|
||||
|
||||
## Validation
|
||||
@@ -52,19 +65,39 @@ validate-contract stage with a clear error.
|
||||
|
||||
Two reference examples exist in `contracts/`:
|
||||
|
||||
- [`contracts/static-assets.yaml`](https://github.com/acdl/acdl/blob/main/contracts/static-assets.yaml)
|
||||
— the `static-assets` module (uses `@v1.6`).
|
||||
- [`contracts/microservice.yaml`](https://github.com/acdl/acdl/blob/main/contracts/microservice.yaml)
|
||||
— the `microservice` module (uses `@v1.6`).
|
||||
- [`contracts/static-assets.yml`](https://github.com/acdl/acdl/blob/main/contracts/static-assets.yml)
|
||||
— the `static-assets` module.
|
||||
- [`contracts/microservice.yml`](https://github.com/acdl/acdl/blob/main/contracts/microservice.yml)
|
||||
— the `microservice` module.
|
||||
|
||||
Additionally, every module has a `modules/<name>/examples/` directory with
|
||||
validated example contracts (`simple.yaml` + `complex.yaml` + variation
|
||||
validated example contracts (`simple.yml` + `complex.yml` + variation
|
||||
files). See the [module catalog](../modules/) for the full list.
|
||||
|
||||
## Multiple modules per contract
|
||||
|
||||
A contract may declare multiple modules under the `infrastructure` map.
|
||||
All modules deploy to the same `environment` in one pipeline run. Resource
|
||||
IDs are namespaced with the module name to avoid collisions (e.g.
|
||||
`microservice-vpc`, `static-assets-s3`).
|
||||
|
||||
```yaml
|
||||
id: app
|
||||
name: pricing-service-api
|
||||
environment: dev
|
||||
infrastructure:
|
||||
microservice:
|
||||
version: "1.0.0"
|
||||
inputs: { ... }
|
||||
static-assets:
|
||||
version: "1.0.0"
|
||||
inputs: { ... }
|
||||
```
|
||||
|
||||
## Multiple contracts
|
||||
|
||||
A consumer repo may contain more than one contract (e.g. one per service or
|
||||
one per environment). Each contract is a separate deployment; each is
|
||||
referenced by a CI definition in `.github/workflows/` that invokes the
|
||||
central reusable workflow with the contract path. See the
|
||||
A consumer repo may also contain more than one contract file (e.g. one per
|
||||
environment). Each contract is a separate deployment; each is referenced by a
|
||||
CI definition in `.github/workflows/` that invokes the central reusable
|
||||
workflow with the contract path. See the
|
||||
[Consumer Guide](../consumer-guide/) for the multi-contract pattern.
|
||||
@@ -16,7 +16,7 @@ There are two kinds of repository in the ACDL model:
|
||||
and the reusable workflow files. Platform engineers work here. A consumer
|
||||
never clones it.
|
||||
- **Consumer repo (yours).** A consumer repo contains only its application
|
||||
code, one or more contracts (`.acdl/contract.yaml`), and one or more CI
|
||||
code, one or more contracts (`.acdl/contract.yml`), and one or more CI
|
||||
definitions (a thin `.github/workflows/deploy.yml` that `uses:` the central
|
||||
reusable workflow, pointing at the appropriate environment + contract).
|
||||
The consumer does not write infrastructure modules, workflow YAML, or
|
||||
|
||||
@@ -6,7 +6,7 @@ are the single source of truth for the workflow files.
|
||||
## CI pipeline
|
||||
|
||||
The CI pipeline runs on every push and pull request to `main`. It is defined
|
||||
by [`pipelines/ci.yaml`](https://github.com/acdl/acdl/blob/main/pipelines/ci.yaml),
|
||||
by [`pipelines/ci.yml`](https://github.com/acdl/acdl/blob/main/pipelines/ci.yml),
|
||||
validated against
|
||||
[`schemas/pipeline.schema.json`](https://github.com/acdl/acdl/blob/main/schemas/pipeline.schema.json).
|
||||
Both platform-runner workflow files implement the same contract and are
|
||||
@@ -31,7 +31,7 @@ bash scripts/run_ci.sh --quiet # suppress per-stage banners
|
||||
## Deployment pipeline
|
||||
|
||||
The deployment pipeline runs when a consumer submits a contract. It is
|
||||
defined by [`pipelines/deploy.yaml`](https://github.com/acdl/acdl/blob/main/pipelines/deploy.yaml),
|
||||
defined by [`pipelines/contract.yml`](https://github.com/acdl/acdl/blob/main/pipelines/contract.yml),
|
||||
validated against
|
||||
[`schemas/deploy-pipeline.schema.json`](https://github.com/acdl/acdl/blob/main/schemas/deploy-pipeline.schema.json).
|
||||
It is exposed to consumer repos as a **reusable workflow**:
|
||||
|
||||
@@ -18,21 +18,26 @@ primitives by `name@semver`; the resolver picks the highest compatible.
|
||||
Module versions are tracked in
|
||||
[`registry.json`](https://github.com/acdl/acdl/blob/main/modules/registry.json).
|
||||
|
||||
## Deploy-pipeline versioning (the `uses:` tag)
|
||||
## Deploy-pipeline versioning (the CI workflow `uses:` tag)
|
||||
|
||||
The central deploy pipeline is referenced by a **floating MAJOR + MINOR
|
||||
tag** in a consumer's contract and CI definition:
|
||||
tag** in a consumer's CI workflow definition:
|
||||
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
jobs:
|
||||
deploy:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
||||
with:
|
||||
contract: .acdl/contract.yml
|
||||
```
|
||||
|
||||
Version constraints cannot be expressed inside the contract, so the tag in
|
||||
`uses:` is the only immutability lever a consumer has.
|
||||
The version pin lives in the CI workflow reference (not in the contract
|
||||
itself — the contract no longer carries a `uses:` field). The CI workflow
|
||||
`uses:` tag is the only immutability lever a consumer has.
|
||||
|
||||
**Unversioned references are discouraged.** Do not use `@main` or a bare
|
||||
`acdl/pipelines/deploy.yaml` — `main` is constantly updated and can cause
|
||||
unexpected failures. Pinning to a MAJOR+MINOR tag means:
|
||||
`acdl/.github/workflows/deploy.yml` — `main` is constantly updated and can
|
||||
cause unexpected failures. Pinning to a MAJOR+MINOR tag means:
|
||||
|
||||
- **Immutability** — the pipeline behavior you tested is the behavior you
|
||||
get. Patch fixes flow within the tag; breaking changes land under the
|
||||
|
||||
@@ -155,6 +155,7 @@ docs/presentations/
|
||||
└── assets/
|
||||
├── puppeteer-config.json ← no-sandbox config for mmdc
|
||||
├── mmd/ ← mermaid source files (Step 2 input)
|
||||
│ ├── sp-theme.json ← S&P Red/Black/White theme (mermaid-cli --configFile)
|
||||
│ ├── platform-works-01-contract-driven.mmd
|
||||
│ ├── platform-works-02-end-to-end-flow.mmd
|
||||
│ ├── platform-works-03-scope-boundary.mmd
|
||||
@@ -256,12 +257,18 @@ for f in mmd/*.mmd; do
|
||||
PUPPETEER_EXECUTABLE_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \
|
||||
npx --yes @mermaid-js/mermaid-cli@latest \
|
||||
-i "$f" -o "png/$name.png" \
|
||||
-p puppeteer-config.json -s 2 -b transparent
|
||||
-p puppeteer-config.json -s 2 -b transparent \
|
||||
--configFile mmd/sp-theme.json
|
||||
done
|
||||
```
|
||||
|
||||
The `puppeteer-config.json` passes `--no-sandbox` to the headless browser
|
||||
(required when running as root in this environment).
|
||||
(required when running as root in this environment). The `--configFile
|
||||
mmd/sp-theme.json` applies the S&P Global Red/Black/White theme (dark
|
||||
`#1B1B1B` accent nodes with `#D6002A` red borders, white supporting nodes,
|
||||
`#F0F0F0` subgraph backgrounds). Each `.mmd` file also carries the same
|
||||
theme inline via a `%%{init:...}%%` block so it renders correctly even
|
||||
without the `--configFile` flag.
|
||||
|
||||
### Export a Marp deck to HTML (committed to repo)
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart LR
|
||||
subgraph UP ["Upstream — anything"]
|
||||
direction TB
|
||||
@@ -18,4 +20,8 @@ flowchart LR
|
||||
C --> D
|
||||
C --> E
|
||||
D --> F
|
||||
F --> G
|
||||
F --> G
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
class C,D,E accent
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart LR
|
||||
A["1. App code<br/>(top level of the repo)"] --> D["Push to main"]
|
||||
B["2. Contract<br/>(.acdl/contract.yaml)"] --> D
|
||||
B["2. Contract<br/>(.acdl/contract.yml)"] --> D
|
||||
C["3. CI definition<br/>(.github/workflows/deploy.yml<br/>— one 'uses:' line)"] --> D
|
||||
D --> E["Platform does the rest"]
|
||||
D --> E["Platform does the rest"]
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
class E accent
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart LR
|
||||
A["Consumer repo<br/>app + contract + 'uses:'"] -->|triggers on push to main| B["Platform runner"]
|
||||
B -->|checks out the consumer repo| A
|
||||
B -->|checks out the ACDL platform repo<br/>into the workspace| C["Platform code<br/>(modules, adapters, schemas)"]
|
||||
C --> B
|
||||
B -->|runs the pipeline against<br/>the consumer's contract| D["Consumer's resources in AWS"]
|
||||
B -->|runs the pipeline against<br/>the consumer's contract| D["Consumer's resources in AWS"]
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
class B,C accent
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart LR
|
||||
A["dev\n≥ 0.50\nautonomous"] -->|promotion| B["qa\n≥ 0.75\nQA attests"]
|
||||
B -->|promotion| C["prod\n≥ 0.90\nSRE attests"]
|
||||
@@ -5,4 +7,7 @@ flowchart LR
|
||||
A -.->|"Testing\n(pilot-ready)"| A
|
||||
B -.->|"Planned"| B
|
||||
C -.->|"Planned"| C
|
||||
D -.->|"Planned"| D
|
||||
D -.->|"Planned"| D
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart LR
|
||||
A["Consumer<br/>writes a contract"] --> B["Platform resolves,<br/>compiles, checks,<br/>deploys, records"]
|
||||
B --> C["Resources running in AWS<br/>+ tamper-evident evidence"]
|
||||
B --> C["Resources running in AWS<br/>+ tamper-evident evidence"]
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
class B accent
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart TD
|
||||
subgraph R1 [" "]
|
||||
direction LR
|
||||
@@ -7,4 +9,8 @@ flowchart TD
|
||||
direction LR
|
||||
F["Policy<br/>checks"] --> G["Confidence<br/>signal"] --> H["Evidence<br/>event"] --> I["Infrastructure<br/>apply"]
|
||||
end
|
||||
E --> F
|
||||
E --> F
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
class C,D,E,G,H accent
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart LR
|
||||
subgraph UP ["Upstream — anything"]
|
||||
direction TB
|
||||
@@ -22,4 +24,8 @@ flowchart LR
|
||||
E --> F
|
||||
E --> G
|
||||
F --> H
|
||||
H --> I
|
||||
H --> I
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
class D,E,F,G accent
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart LR
|
||||
subgraph IN ["6 weighted inputs"]
|
||||
direction TB
|
||||
@@ -12,4 +14,8 @@ flowchart LR
|
||||
G --> H{"Threshold\ngate"}
|
||||
H -->|Meets threshold| I["Proceed"]
|
||||
H -->|Below threshold| J["Halt +\nexplainable reason"]
|
||||
H -->|Critical finding| J
|
||||
H -->|Critical finding| J
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
class G,H,J accent
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart LR
|
||||
A["Deployment arrives\nat env gate"] --> B["Confidence signal\ncomputed"]
|
||||
B --> C{"Meets\nthreshold?"}
|
||||
@@ -10,4 +12,8 @@ flowchart LR
|
||||
H -->|Reject| J["Halt — rejection\nextends audit chain"]
|
||||
I --> K["Deployment\nproceeds"]
|
||||
F --> K
|
||||
K --> L["Evidence written\nRPO=0"]
|
||||
K --> L["Evidence written\nRPO=0"]
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
class C,E,I,K,L accent
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||
|
||||
flowchart LR
|
||||
A["v1.0\nDEMO\ncomplete"] --> B["v1.1–v1.8\nPLATFORM BUILD\ncomplete"]
|
||||
B --> C["v1.9\nPRESENTATIONS + PATCHES\ncomplete"]
|
||||
@@ -8,4 +10,8 @@ flowchart LR
|
||||
B -.->|"IR + OIDC + ABAC +\nmodule catalog +\nencryption + decommission"| B
|
||||
C -.->|"10-slide decks +\ntalking points +\nS&P theme"| C
|
||||
D -.->|"proposed phasing\nnot formally planned"| D
|
||||
E -.->|"proposed phasing\nnot formally planned"| E
|
||||
E -.->|"proposed phasing\nnot formally planned"| E
|
||||
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||
class F accent
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"theme": "base",
|
||||
"themeVariables": {
|
||||
"primaryColor": "#1B1B1B",
|
||||
"primaryBorderColor": "#D6002A",
|
||||
"primaryTextColor": "#fff",
|
||||
"secondaryColor": "#fff",
|
||||
"secondaryBorderColor": "#D6002A",
|
||||
"secondaryTextColor": "#1B1B1B",
|
||||
"tertiaryColor": "#F0F0F0",
|
||||
"clusterBkg": "#F0F0F0",
|
||||
"lineColor": "#1B1B1B",
|
||||
"fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 42 KiB |
@@ -45,7 +45,7 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
||||
|
||||
# The Problem & The North Star
|
||||
|
||||
<em class="story">Story beat: Here's the problem we're solving and where we're going.</em>
|
||||
<em class="story">Here's the problem we're solving and where we're going.</em>
|
||||
|
||||
Four frictions slow every team:
|
||||
|
||||
@@ -64,7 +64,7 @@ Four frictions slow every team:
|
||||
|
||||
# Where ACDL Sits in Your World
|
||||
|
||||
<em class="story">Story beat: Now that we know the problem, here's where ACDL fits — and where it doesn't.</em>
|
||||
<em class="story">Now that we know the problem, here's where ACDL fits — and where it doesn't.</em>
|
||||
|
||||

|
||||
|
||||
@@ -77,7 +77,7 @@ Four frictions slow every team:
|
||||
|
||||
# The Contract-Driven Model
|
||||
|
||||
<em class="story">Story beat: The contract is the boundary between upstream and ACDL. It's all a consumer writes.</em>
|
||||
<em class="story">The contract is the boundary between upstream and ACDL. It's all a consumer writes.</em>
|
||||
|
||||
A single YAML contract — **module, environment, inputs**. The platform owns everything else.
|
||||
|
||||
@@ -92,7 +92,7 @@ A single YAML contract — **module, environment, inputs**. The platform owns ev
|
||||
|
||||
# The End-to-End Flow
|
||||
|
||||
<em class="story">Story beat: Once the contract is written, here's what the platform does with it — every time.</em>
|
||||
<em class="story">Once the contract is written, here's what the platform does with it — every time.</em>
|
||||
|
||||
Every deployment runs the same stages, in the same order, with the same checks — no team-specific pipelines, no tribal runbooks.
|
||||
|
||||
@@ -105,7 +105,7 @@ Every deployment runs the same stages, in the same order, with the same checks
|
||||
|
||||
# Zero-Trust by Default
|
||||
|
||||
<em class="story">Story beat: Before any infrastructure is created, here's how access is scoped.</em>
|
||||
<em class="story">Before any infrastructure is created, here's how access is scoped.</em>
|
||||
|
||||
Consumer repositories hold **no long-lived cloud credentials.** Ever.
|
||||
|
||||
@@ -120,7 +120,7 @@ Consumer repositories hold **no long-lived cloud credentials.** Ever.
|
||||
|
||||
# Safety is Computed, Not Assumed
|
||||
|
||||
<em class="story">Story beat: Now let's look at how the platform decides whether a deployment is safe.</em>
|
||||
<em class="story">Now let's look at how the platform decides whether a deployment is safe.</em>
|
||||
|
||||
Every delivery action produces a **measurable, explainable confidence signal** — a weighted sum of observable facts, not a black box. <span class="badge agentic">Agentic</span>
|
||||
|
||||
@@ -141,7 +141,7 @@ Every delivery action produces a **measurable, explainable confidence signal**
|
||||
|
||||
# Security by Construction
|
||||
|
||||
<em class="story">Story beat: Beyond the confidence signal, security defaults are on by construction — not by opt-in.</em>
|
||||
<em class="story">Beyond the confidence signal, security defaults are on by construction — not by opt-in.</em>
|
||||
|
||||
Security defaults that **do not require a team to opt in.** Checks run on **every** deployment, normalized to a single schema. <span class="badge testing">Testing</span>
|
||||
|
||||
@@ -154,7 +154,7 @@ Security defaults that **do not require a team to opt in.** Checks run on **ever
|
||||
|
||||
# Accountability & Audit
|
||||
|
||||
<em class="story">Story beat: Computed safety handles the gate. But humans still matter — here's how accountability works.</em>
|
||||
<em class="story">Computed safety handles the gate. But humans still matter — here's how accountability works.</em>
|
||||
|
||||

|
||||
|
||||
@@ -171,7 +171,7 @@ Security defaults that **do not require a team to opt in.** Checks run on **ever
|
||||
|
||||
# Testing vs. Planned
|
||||
|
||||
<em class="story">Story beat: Let's be honest about what works today and what's on the roadmap.</em>
|
||||
<em class="story">Let's be honest about what works today and what's on the roadmap.</em>
|
||||
|
||||
<style>
|
||||
section { font-size: 20px; }
|
||||
@@ -192,6 +192,9 @@ section { font-size: 20px; }
|
||||
- Dynamic module creation <span class="badge agentic">Agentic</span> · Pattern recognition <span class="badge agentic">Agentic</span>
|
||||
- Additional engine adapters · Deeper observability bootstrap
|
||||
|
||||
**Verification Coverage** — 6 cloud capabilities are design-verified + locally emulated, deploy-unverified (IAM drift):
|
||||
DynamoDB contracts table · Lambda contract-ingestor · ECS service live · CloudFront prod stack · uptime-kuma · OIDC role
|
||||
|
||||
*Full inventory + phased roadmap in the appendix.*
|
||||
|
||||
---
|
||||
@@ -201,7 +204,7 @@ section { font-size: 20px; }
|
||||
|
||||
# The Vision Realized
|
||||
|
||||
<em class="story">Story beat: Here's what success looks like when the North Star is reached.</em>
|
||||
<em class="story">Here's what success looks like when the North Star is reached.</em>
|
||||
|
||||
- **Velocity without sacrificing safety.** Speed is in the ergonomics (a simple contract, a one-line `uses:`); safety is in the gates the consumer cannot bypass.
|
||||
- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation.
|
||||
@@ -226,6 +229,7 @@ section { font-size: 20px; }
|
||||
3. The Road to the North Star (phased roadmap)
|
||||
4. Testing vs. Planned (full inventory)
|
||||
5. Glossary
|
||||
6. Operating Model & Cost
|
||||
|
||||
---
|
||||
|
||||
@@ -332,4 +336,17 @@ li { margin-bottom: 2px; }
|
||||
| **HITL** | Human-in-the-Loop — deliberate human attestation required for qa/prod/dr environments |
|
||||
| **VCS** | Version Control System — the git hosting platform (GitHub, Gitea, GitLab) |
|
||||
| **NFR** | Non-Functional Requirement — encryption, tagging, observability standards |
|
||||
| **IR** | Intermediate Representation — the engine-agnostic stack definition between contract and Terraform |
|
||||
| **IR** | Intermediate Representation — the engine-agnostic stack definition between contract and Terraform |
|
||||
|
||||
---
|
||||
|
||||
# A6 — Operating Model & Cost
|
||||
|
||||
ACDL runs at **zero cloud cost** for day-to-day development.
|
||||
|
||||
- **Local emulators are the primary tier** — the full pipeline (contract → resolver → adapter → local ECS → flat-file outbox → local Lambda) runs in-process, no AWS credentials, no Checkov, no DynamoDB. <span class="badge testing">Testing</span>
|
||||
- **Live-AWS is a one-off spike per milestone** — `terraform init/validate/plan` against the real account verifies the adapter emits valid Terraform. No BAU cloud spend.
|
||||
- **No running infrastructure between milestones** — state is in S3 (one bucket), the outbox is in DynamoDB (one table), both are query-only between spikes.
|
||||
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents). No ECS, no CloudFront, no Lambda running persistently.
|
||||
|
||||
**The operating model:** local-first development, milestone-scoped verification, zero BAU cloud spend.
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
## Slide 2 — The Problem & The North Star
|
||||
|
||||
Here's the problem we're solving and where we're going.
|
||||
|
||||
Software delivery scales with the **coordination surface around it**, not the engineering inside it. Most teams can write code; far fewer get the infrastructure right.
|
||||
|
||||
Four frictions slow every team:
|
||||
@@ -44,6 +46,8 @@ Four frictions slow every team:
|
||||
|
||||
Now that we know the problem, here's where ACDL fits — and where it doesn't.
|
||||
|
||||
Now that we know the problem, here's where ACDL fits — and where it doesn't.
|
||||
|
||||
- **Upstream is anything** — your IDE, an agentic SDLC, or a citizen developer vibe coding on a laptop. ACDL doesn't care how the contract was produced.
|
||||
- **ACDL is infrastructure only** — it provisions and governs AWS resources. It does not build, test, or deploy your application code. That's upstream.
|
||||
- **Not a general-purpose AI** — autonomy is narrow, scoped to delivery, bounded by strict policy envelopes.
|
||||
@@ -57,6 +61,8 @@ Now that we know the problem, here's where ACDL fits — and where it doesn't.
|
||||
|
||||
The contract is the boundary between upstream and ACDL. It's all a consumer writes.
|
||||
|
||||
The contract is the boundary between upstream and ACDL. It's all a consumer writes.
|
||||
|
||||
A single YAML contract — **module, environment, inputs**. The platform owns everything else.
|
||||
|
||||
```mermaid
|
||||
@@ -82,6 +88,8 @@ The consumer does **not** write infrastructure modules, workflow logic, or adapt
|
||||
|
||||
Once the contract is written, here's what the platform does with it — every time.
|
||||
|
||||
Once the contract is written, here's what the platform does with it — every time.
|
||||
|
||||
Every deployment runs the same stages, in the same order, with the same checks — no team-specific pipelines, no tribal runbooks.
|
||||
|
||||
```mermaid
|
||||
@@ -109,6 +117,8 @@ Two properties matter to leadership:
|
||||
|
||||
Before any infrastructure is created, here's how access is scoped.
|
||||
|
||||
Before any infrastructure is created, here's how access is scoped.
|
||||
|
||||
Consumer repositories hold **no long-lived cloud credentials.** Ever.
|
||||
|
||||
- **Authentication is OIDC federation** between the platform runners and the cloud provider. Each job mints a short-lived token; no credential is stored in the consumer repo or in a runner secret. *(Testing on GitHub Actions runners; planned for all platform runners.)*
|
||||
@@ -126,6 +136,8 @@ Consumer repositories hold **no long-lived cloud credentials.** Ever.
|
||||
|
||||
Now let's look at how the platform decides whether a deployment is safe.
|
||||
|
||||
Now let's look at how the platform decides whether a deployment is safe.
|
||||
|
||||
Every delivery action produces a **measurable, explainable confidence signal** — a weighted sum of observable facts, not a black box. *(Agentic.)*
|
||||
|
||||
- **Six weighted inputs** — policy conformance, validation, freshness, source provenance, history, and non-functional requirements (NFRs). The weights are **manually tuned**, the inputs are **observable**, and the breakdown is **auditable** — if a consumer asks "why 0.62?", the platform answers with a per-input breakdown.
|
||||
@@ -149,6 +161,8 @@ Every delivery action produces a **measurable, explainable confidence signal**
|
||||
|
||||
Beyond the confidence signal, security defaults are on by construction — not by opt-in.
|
||||
|
||||
Beyond the confidence signal, security defaults are on by construction — not by opt-in.
|
||||
|
||||
Security defaults that **do not require a team to opt in.** Checks run on **every** deployment, normalized to a single schema regardless of which engine produced them. *(Testing.)*
|
||||
|
||||
- **Infrastructure-as-code policy** (Checkov) — secrets in plaintext, public ingress, IAM wildcards, KMS key references, **required tagging standards** (`acdl:owner`, `acdl:contract`, `acdl:environment`, `acdl:cost-center`). All run *before* infra is created.
|
||||
@@ -168,6 +182,8 @@ Security defaults that **do not require a team to opt in.** Checks run on **ever
|
||||
|
||||
Computed safety handles the gate. But humans still matter — here's how accountability works.
|
||||
|
||||
Computed safety handles the gate. But humans still matter — here's how accountability works.
|
||||
|
||||
- **Dev is fully autonomous.** The confidence signal (≥ 0.50) is the only gate. Queue-based handoffs are eliminated from lower environments. *(Testing, Agentic.)*
|
||||
- **qa, prod, and dr require deliberate human attestation** — not rubber stamps, but policy-mandated acts of accountability via protected deployment approvals. The approver reviews the contract, the planned Terraform changes, and the accumulated evidence. *(Planned.)*
|
||||
- **QA attests to infrastructure readiness — the contract, the planned Terraform changes, and the accumulated evidence. QA does not review application code (that's upstream).**
|
||||
@@ -189,6 +205,8 @@ Version control is a **coordination tool, not an evidentiary fortress.** True co
|
||||
|
||||
Here's what success looks like when the North Star is reached.
|
||||
|
||||
Here's what success looks like when the North Star is reached.
|
||||
|
||||
- **Velocity without sacrificing safety.** Speed is in the ergonomics (a simple contract, a one-line `uses:`); safety is in the gates the consumer cannot bypass.
|
||||
- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation.
|
||||
- **Auditability as a byproduct, not a project.** Every production change is traceable to a human attestation and a tamper-evident evidence event.
|
||||
@@ -218,6 +236,8 @@ For deep dives — these slides cover details omitted from the main 10.
|
||||
|
||||
## A1 — Platform-Managed Environments
|
||||
|
||||
For deep dives — these slides cover details omitted from the main 10.
|
||||
|
||||
A consumer provides **no AWS account, no VPC, no subnet, no state backend, no runner key.** The platform owns the blast radius.
|
||||
|
||||
A named environment is a platform-owned bundle of:
|
||||
|
||||
@@ -45,9 +45,9 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
||||
|
||||
---
|
||||
|
||||
# Where ACDL Sits in Your World
|
||||
# Where Agentic Cloud Delivery (ACDL) Sits in Your World
|
||||
|
||||
<em class="story">Story beat: Here's who uses the platform and where the boundary is.</em>
|
||||
<em class="story">Here's who uses the platform and where the boundary is.</em>
|
||||
|
||||

|
||||
|
||||
@@ -60,24 +60,27 @@ section.title h3 { color: #F0F0F0; font-weight: 400; font-size: 22px; margin-top
|
||||
|
||||
# The Contract — The Entire Consumer Surface
|
||||
|
||||
<em class="story">Story beat: Now let's look at what a consumer actually writes — it's tiny.</em>
|
||||
<em class="story">Now let's look at what a consumer actually writes — it's tiny.</em>
|
||||
|
||||
Three things. That is the entire consumer-side surface.
|
||||
|
||||
<img src="assets/png/developer-experience-02-what-dev-does.png" style="float: right; width: 38%; margin-left: 20px; margin-bottom: 10px;" />
|
||||
|
||||
- **1. App code** — the consumer's service, at the top level of the repo
|
||||
- **2. A contract** — a single YAML file: module, environment, inputs
|
||||
- **2. A contract** — a single YAML file: id, name, environment, infrastructure
|
||||
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: microservice
|
||||
id: msvc
|
||||
name: microservice
|
||||
environment: dev
|
||||
inputs:
|
||||
cpu: 256
|
||||
memory: 512
|
||||
desired_count: 2
|
||||
port: 8080
|
||||
infrastructure:
|
||||
microservice:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
cpu: 256
|
||||
memory: 512
|
||||
desired_count: 2
|
||||
port: 8080
|
||||
```
|
||||
|
||||
- **3. A one-line CI definition** — a thin `uses:` wrapper pointing at a versioned platform workflow
|
||||
@@ -87,7 +90,7 @@ inputs:
|
||||
|
||||
# The Developer Feedback Loop
|
||||
|
||||
<em class="story">Story beat: Once you push, here's what you see — in real time, in your own logs.</em>
|
||||
<em class="story">Once you push, here's what you see — in real time, in your own logs.</em>
|
||||
|
||||
Developers see **what the platform is doing**, in real time. <span class="badge testing">Testing</span>
|
||||
|
||||
@@ -102,11 +105,11 @@ Developers see **what the platform is doing**, in real time. <span class="badge
|
||||
|
||||
# Versioned, Predictable Releases
|
||||
|
||||
<em class="story">Story beat: You control when you absorb platform improvements — no surprise upgrades.</em>
|
||||
<em class="story">You control when you absorb platform improvements — no surprise upgrades.</em>
|
||||
|
||||
Consumers control **when** they absorb platform improvements. <span class="badge testing">Testing</span>
|
||||
|
||||
- **Floating MAJOR + MINOR tags** (e.g. `@v1.6`) — a consumer automatically receives patch updates within the line
|
||||
- **Floating MAJOR + MINOR tags** (e.g. `@v1.10`) — a consumer automatically receives patch updates within the line
|
||||
- **Semantic versioning with a clear contract:** interface → MAJOR, behavior → MINOR, lifecycle → PATCH
|
||||
- **A consumer can pin to an exact version** for maximum stability, or float on MAJOR only (`@v1`) to absorb new features on their own cadence
|
||||
- **Unversioned references (`@main`, bare) are discouraged** — the versioned tag is the only immutability lever
|
||||
@@ -116,7 +119,7 @@ Consumers control **when** they absorb platform improvements. <span class="badge
|
||||
|
||||
# Friendly Onboarding
|
||||
|
||||
<em class="story">Story beat: First impressions matter — the platform fails gracefully, not opaquely.</em>
|
||||
<em class="story">First impressions matter — the platform fails gracefully, not opaquely.</em>
|
||||
|
||||
First impressions of a platform are made **when it fails for the first time.** The platform fails gracefully. <span class="badge testing">Testing</span>
|
||||
|
||||
@@ -135,7 +138,7 @@ The pipeline then **exits without attempting a deployment** — no partial state
|
||||
|
||||
# Safe Promotion Path
|
||||
|
||||
<em class="story">Story beat: Promotion is a workflow choice, not a contract edit — and the bar rises automatically.</em>
|
||||
<em class="story">Promotion is a workflow choice, not a contract edit — and the bar rises automatically.</em>
|
||||
|
||||
The contract is environment-agnostic. The platform raises the bar automatically.
|
||||
|
||||
@@ -150,12 +153,12 @@ The contract is environment-agnostic. The platform raises the bar automatically.
|
||||
```yaml
|
||||
jobs:
|
||||
dev:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
||||
with: { contract: .acdl/contract.yaml, environment: dev }
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with: { contract: .acdl/contract.yml, environment: dev }
|
||||
qa:
|
||||
needs: dev
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
||||
with: { contract: .acdl/contract.yaml, environment: qa }
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with: { contract: .acdl/contract.yml, environment: qa }
|
||||
```
|
||||
|
||||
</td>
|
||||
@@ -166,11 +169,11 @@ jobs:
|
||||
```yaml
|
||||
jobs:
|
||||
dev:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with: { contract: .acdl/contract-dev.yaml }
|
||||
qa:
|
||||
needs: dev
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with: { contract: .acdl/contract-qa.yaml }
|
||||
```
|
||||
|
||||
@@ -189,14 +192,14 @@ td { font-size: 14px; }
|
||||
|
||||
# Safe Decommission
|
||||
|
||||
<em class="story">Story beat: Tearing down is as deliberate as deploying — and just as gated.</em>
|
||||
<em class="story">Tearing down is as deliberate as deploying — and just as gated.</em>
|
||||
|
||||
Tearing down a stack is **as deliberate as deploying one.** <span class="badge testing">Testing</span>
|
||||
|
||||
```yaml
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.8
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with:
|
||||
contract: .acdl/contract.yaml
|
||||
contract: .acdl/contract.yml
|
||||
mode: decommission
|
||||
changeRequestId: "CHG0678912"
|
||||
```
|
||||
@@ -212,7 +215,7 @@ The per-stack encryption key enters a **grace window** (default 30 days) so encr
|
||||
|
||||
# Self-Service Module Catalog
|
||||
|
||||
<em class="story">Story beat: You don't author infrastructure — you pick from pre-built, security-reviewed building blocks.</em>
|
||||
<em class="story">You don't author infrastructure — you pick from pre-built, security-reviewed building blocks.</em>
|
||||
|
||||
Developers pick from **pre-built, security-reviewed building blocks.** <span class="badge testing">Testing</span>
|
||||
|
||||
@@ -229,7 +232,7 @@ Developers pick from **pre-built, security-reviewed building blocks.** <span cla
|
||||
|
||||
# The Desired Outcomes
|
||||
|
||||
<em class="story">Story beat: Here's what this delivers to the organization.</em>
|
||||
<em class="story">Here's what this delivers to the organization.</em>
|
||||
|
||||
- **Velocity without sacrificing safety.** Speed is in the ergonomics (a simple contract, a one-line `uses:`); safety is in the gates the consumer cannot bypass.
|
||||
- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation.
|
||||
@@ -255,6 +258,7 @@ Developers pick from **pre-built, security-reviewed building blocks.** <span cla
|
||||
3. Local Reproducibility (detail)
|
||||
4. The Road to the North Star (phased roadmap)
|
||||
5. Glossary
|
||||
6. Operating Model & Cost
|
||||
|
||||
---
|
||||
|
||||
@@ -322,4 +326,21 @@ The entire CI pipeline runs **from the shell**, not just in CI. <span class="bad
|
||||
| **RPO** | Recovery Point Objective — RPO = 0 means evidence is written synchronously, no data loss |
|
||||
| **HITL** | Human-in-the-Loop — deliberate human attestation required for qa/prod/dr environments |
|
||||
| **VCS** | Version Control System — the git hosting platform (GitHub, Gitea, GitLab) |
|
||||
| **NFR** | Non-Functional Requirement — encryption, tagging, observability standards |
|
||||
| **NFR** | Non-Functional Requirement — encryption, tagging, observability standards |
|
||||
| **IR** | Intermediate Representation — the engine-agnostic stack definition between contract and Terraform |
|
||||
|
||||
---
|
||||
|
||||
# A6 — Operating Model & Cost
|
||||
|
||||
ACDL runs at **zero cloud cost** for day-to-day development.
|
||||
|
||||
- **Local emulators are the primary tier** — the full pipeline runs in-process, no AWS credentials, no Checkov, no DynamoDB. <span class="badge testing">Testing</span>
|
||||
- **Live-AWS is a one-off spike per milestone** — `terraform init/validate/plan` verifies the adapter. No BAU cloud spend.
|
||||
- **No running infrastructure between milestones** — state in S3 (one bucket), outbox in DynamoDB (one table), both query-only.
|
||||
- **Cost drivers** are spike-scoped: Terraform plan reads (free), S3 state storage (cents), DynamoDB outbox (cents).
|
||||
|
||||
**Verification Coverage** — 6 cloud capabilities are design-verified + locally emulated, deploy-unverified (IAM drift):
|
||||
DynamoDB contracts table · Lambda contract-ingestor · ECS service live · CloudFront prod stack · uptime-kuma · OIDC role
|
||||
|
||||
**The operating model:** local-first development, milestone-scoped verification, zero BAU cloud spend.
|
||||
@@ -17,21 +17,36 @@ The consumer surface is intentionally tiny. The platform's surface is large and
|
||||
|
||||
---
|
||||
|
||||
## Slide 2 — Where ACDL Sits in Your World
|
||||
## Slide 2 — Where Agentic Cloud Delivery (ACDL) Sits in Your World
|
||||
|
||||
Story beat: Here's who uses the platform and where the boundary is.
|
||||
Here's who uses the platform and where the boundary is.
|
||||
|
||||
The platform serves **two kinds of consumer** through two coordinated paths — but both converge on the **same contract, the same policy envelope, and the same evidence stream.**
|
||||
|
||||
**Agentic Cloud Delivery (ACDL)** sits between upstream (anything that produces a contract) and downstream (AWS resources running + the consumer's image pipeline).
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
U1["Anything upstream<br/>(IDE / agentic SDLC / vibe coding)"] --> T["Technical developer<br/>writes app + contract"]
|
||||
U1 --> C["Citizen developer<br/>declares intent"]
|
||||
T --> K["Contract YAML"]
|
||||
C --> AI["An AI agent maps intent<br/>to a reviewed-skill contract"]
|
||||
AI --> K
|
||||
K --> ACDL["ACDL — infrastructure only<br/>resolve → check → plan → policy<br/>→ confidence → evidence → apply"]
|
||||
ACDL --> AWS["AWS resources provisioned + governed"]
|
||||
flowchart LR
|
||||
subgraph UP ["Upstream — anything"]
|
||||
direction TB
|
||||
A["Technical dev\n(app code + contract)"]
|
||||
B["Citizen dev\n(intent → AI agent\n→ contract)"]
|
||||
end
|
||||
subgraph ACDL ["ACDL — infrastructure only"]
|
||||
C["Same contract\nSame pipeline\nSame safety"]
|
||||
D["Provision\nAWS resources"]
|
||||
E["Evidence\nhash-chained"]
|
||||
end
|
||||
subgraph DOWN ["Downstream"]
|
||||
F["AWS resources\nrunning"]
|
||||
G["Consumer pipeline\ndeploys image"]
|
||||
end
|
||||
A --> C
|
||||
B --> C
|
||||
C --> D
|
||||
C --> E
|
||||
D --> F
|
||||
F --> G
|
||||
```
|
||||
|
||||
- **Technical developer** — owns app code + a contract + a thin CI definition. Uses the full module catalog and inputs.
|
||||
@@ -47,23 +62,26 @@ The platform is **opinionated in what it accepts, regardless of who is declaring
|
||||
|
||||
## Slide 3 — The Contract — The Entire Consumer Surface
|
||||
|
||||
Story beat: Now let's look at what a consumer actually writes — it's tiny.
|
||||
Now let's look at what a consumer actually writes — it's tiny.
|
||||
|
||||
Three things. That is the entire consumer-side surface.
|
||||
|
||||
1. **App code** — the consumer's service, at the top level of the repo
|
||||
2. **A contract** — a single YAML file: module, environment, inputs
|
||||
2. **A contract** — a single YAML file: id, name, environment, infrastructure
|
||||
3. **A one-line CI definition** — a thin `uses:` wrapper pointing at a versioned platform workflow
|
||||
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: microservice
|
||||
id: msvc
|
||||
name: microservice
|
||||
environment: dev
|
||||
inputs:
|
||||
cpu: 256
|
||||
memory: 512
|
||||
desired_count: 2
|
||||
port: 8080
|
||||
infrastructure:
|
||||
microservice:
|
||||
version: "1.0.0"
|
||||
inputs:
|
||||
cpu: 256
|
||||
memory: 512
|
||||
desired_count: 2
|
||||
port: 8080
|
||||
```
|
||||
|
||||
The developer does **not**:
|
||||
@@ -80,7 +98,7 @@ The developer does **not**:
|
||||
|
||||
## Slide 4 — The Developer Feedback Loop
|
||||
|
||||
Story beat: Once you push, here's what you see — in real time, in your own logs.
|
||||
Once you push, here's what you see — in real time, in your own logs.
|
||||
|
||||
Developers see **what the platform is doing**, in real time. <span class="badge testing">Testing</span>
|
||||
|
||||
@@ -96,11 +114,11 @@ Developers see **what the platform is doing**, in real time. <span class="badge
|
||||
|
||||
## Slide 5 — Versioned, Predictable Releases
|
||||
|
||||
Story beat: You control when you absorb platform improvements — no surprise upgrades.
|
||||
You control when you absorb platform improvements — no surprise upgrades.
|
||||
|
||||
Consumers control **when** they absorb platform improvements. <span class="badge testing">Testing</span>
|
||||
|
||||
- **Floating MAJOR + MINOR tags** (e.g. `@v1.6`) — a consumer automatically receives patch updates within the line.
|
||||
- **Floating MAJOR + MINOR tags** (e.g. `@v1.10`) — a consumer automatically receives patch updates within the line.
|
||||
- **Semantic versioning with a clear contract:** interface → MAJOR, behavior → MINOR, lifecycle → PATCH.
|
||||
- **A consumer can pin to an exact version** for maximum stability, or float on MAJOR only (`@v1`) to absorb new features on their own cadence.
|
||||
- **Unversioned references (`@main`, bare) are discouraged** — the versioned tag is the only immutability lever a consumer has.
|
||||
@@ -112,7 +130,7 @@ Consumers control **when** they absorb platform improvements. <span class="badge
|
||||
|
||||
## Slide 6 — Friendly Onboarding
|
||||
|
||||
Story beat: First impressions matter — the platform fails gracefully, not opaquely.
|
||||
First impressions matter — the platform fails gracefully, not opaquely.
|
||||
|
||||
First impressions of a platform are made **when it fails for the first time.** The platform fails gracefully. <span class="badge testing">Testing</span>
|
||||
|
||||
@@ -133,7 +151,7 @@ The pipeline then **exits without attempting a deployment** — no partial state
|
||||
|
||||
## Slide 7 — Safe Promotion Path
|
||||
|
||||
Story beat: Promotion is a workflow choice, not a contract edit — and the bar rises automatically.
|
||||
Promotion is a workflow choice, not a contract edit — and the bar rises automatically.
|
||||
|
||||
The contract is environment-agnostic. The platform raises the bar automatically.
|
||||
|
||||
@@ -149,12 +167,12 @@ flowchart LR
|
||||
```yaml
|
||||
jobs:
|
||||
dev:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
||||
with: { contract: .acdl/contract.yaml, environment: dev }
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with: { contract: .acdl/contract.yml, environment: dev }
|
||||
qa:
|
||||
needs: dev
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
||||
with: { contract: .acdl/contract.yaml, environment: qa }
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with: { contract: .acdl/contract.yml, environment: qa }
|
||||
```
|
||||
|
||||
**Approach B — Environment-specific contracts.** When inputs genuinely differ per environment, each job points at its own contract file. The pipeline, policy, and confidence model stay identical.
|
||||
@@ -162,11 +180,11 @@ jobs:
|
||||
```yaml
|
||||
jobs:
|
||||
dev:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with: { contract: .acdl/contract-dev.yaml }
|
||||
qa:
|
||||
needs: dev
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.6
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with: { contract: .acdl/contract-qa.yaml }
|
||||
```
|
||||
|
||||
@@ -189,14 +207,14 @@ Whichever approach a team picks, the platform applies the same rising bar:
|
||||
|
||||
## Slide 8 — Safe Decommission
|
||||
|
||||
Story beat: Tearing down is as deliberate as deploying — and just as gated.
|
||||
Tearing down is as deliberate as deploying — and just as gated.
|
||||
|
||||
Tearing down a stack is **as deliberate as deploying one.** <span class="badge testing">Testing</span>
|
||||
|
||||
```yaml
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.8
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.10
|
||||
with:
|
||||
contract: .acdl/contract.yaml
|
||||
contract: .acdl/contract.yml
|
||||
mode: decommission
|
||||
changeRequestId: "CHG0678912"
|
||||
```
|
||||
@@ -214,7 +232,7 @@ The per-stack encryption key enters a **grace window** (default 30 days) so encr
|
||||
|
||||
## Slide 9 — Self-Service Module Catalog
|
||||
|
||||
Story beat: You don't author infrastructure — you pick from pre-built, security-reviewed building blocks.
|
||||
You don't author infrastructure — you pick from pre-built, security-reviewed building blocks.
|
||||
|
||||
Developers pick from **pre-built, security-reviewed building blocks.** <span class="badge testing">Testing</span>
|
||||
|
||||
@@ -230,7 +248,7 @@ Developers pick from **pre-built, security-reviewed building blocks.** <span cla
|
||||
|
||||
## Slide 10 — The Desired Outcomes
|
||||
|
||||
Story beat: Here's what this delivers to the organization.
|
||||
Here's what this delivers to the organization.
|
||||
|
||||
- **Velocity without sacrificing safety.** Speed is in the ergonomics (a simple contract, a one-line `uses:`); safety is in the gates the consumer cannot bypass.
|
||||
- **Security, observability, and compliance as platform defaults** — not per-team effort, not post-hoc remediation. Encryption, deletion protection, uptime monitoring, policy checks, and evidence are on by construction.
|
||||
|
||||
@@ -46,8 +46,8 @@ Every L1 primitive MUST contain, at minimum:
|
||||
| `interface.json` | Angine-agnostic declaration: inputs, outputs, NFRs, optional multi-resource graph. |
|
||||
| `instance.json` | A concrete instance used as the adapter regression baseline. |
|
||||
| `README.md` | Plain-language documentation following `README-TEMPLATE.md` (see §7). |
|
||||
| `examples/simple.yaml` | A minimal contract that uses the primitive with required inputs only. |
|
||||
| `examples/complex.yaml` | A contract that exercises optional inputs, NFRs, and (if applicable) the multi-resource graph. |
|
||||
| `examples/simple.yml` | A minimal contract that uses the primitive with required inputs only. |
|
||||
| `examples/complex.yml` | A contract that exercises optional inputs, NFRs, and (if applicable) the multi-resource graph. |
|
||||
|
||||
Directory layout:
|
||||
|
||||
@@ -57,8 +57,8 @@ modules/l1/<name>/
|
||||
instance.json
|
||||
README.md
|
||||
examples/
|
||||
simple.yaml
|
||||
complex.yaml
|
||||
simple.yml
|
||||
complex.yml
|
||||
```
|
||||
|
||||
### 2.2 interface.json schema
|
||||
@@ -225,8 +225,8 @@ asset site behind CloudFront + WAF). It is declared by a
|
||||
|------|---------|
|
||||
| `composition.json` | The composition tree: children, wires, outputs, optional features. |
|
||||
| `README.md` | Plain-language documentation following `README-TEMPLATE.md` (see §7). |
|
||||
| `examples/simple.yaml` | A minimal contract that uses the module with required inputs only. |
|
||||
| `examples/complex.yaml` | A contract that exercises optional inputs and feature flags. |
|
||||
| `examples/simple.yml` | A minimal contract that uses the module with required inputs only. |
|
||||
| `examples/complex.yml` | A contract that exercises optional inputs and feature flags. |
|
||||
|
||||
Directory layout:
|
||||
|
||||
@@ -235,8 +235,8 @@ modules/l2/<name>/
|
||||
composition.json
|
||||
README.md
|
||||
examples/
|
||||
simple.yaml
|
||||
complex.yaml
|
||||
simple.yml
|
||||
complex.yml
|
||||
```
|
||||
|
||||
There is no `instance.json` for an L2 module — the L2 is deployed by
|
||||
@@ -433,8 +433,8 @@ Every module README MUST follow the structure of
|
||||
8. `## Compliance extension points` — resources or behaviors that could
|
||||
be added for the future compliance milestone (GDPR, SOX, SOC2,
|
||||
DORA). Not implemented yet; listed so the redesign can plan for them.
|
||||
9. `## Examples` — links to `examples/simple.yaml` and
|
||||
`examples/complex.yaml` with a one-line description of each.
|
||||
9. `## Examples` — links to `examples/simple.yml` and
|
||||
`examples/complex.yml` with a one-line description of each.
|
||||
10. `## Versioning` — the module's semver policy: interface MAJOR,
|
||||
behavior MINOR, lifecycle PATCH. MAJOR bumps require a new
|
||||
`registry.json` entry (immutable publication); old entries enter a
|
||||
@@ -514,12 +514,12 @@ must be checked before the module is registered and published.
|
||||
|
||||
- [ ] All required files present:
|
||||
- L1: `interface.json`, `instance.json`, `README.md`,
|
||||
`examples/simple.yaml`, `examples/complex.yaml`.
|
||||
- L2: `composition.json`, `README.md`, `examples/simple.yaml`,
|
||||
`examples/complex.yaml` (no `instance.json`).
|
||||
`examples/simple.yml`, `examples/complex.yml`.
|
||||
- L2: `composition.json`, `README.md`, `examples/simple.yml`,
|
||||
`examples/complex.yml` (no `instance.json`).
|
||||
- [ ] `interface.json` (L1) / `composition.json` (L2) validates against
|
||||
`schemas/stack.schema.json`.
|
||||
- [ ] `examples/simple.yaml` and `examples/complex.yaml` validate
|
||||
- [ ] `examples/simple.yml` and `examples/complex.yml` validate
|
||||
against `schemas/contract.schema.json`.
|
||||
- [ ] Module registered in `modules/registry.json` at its semver with a
|
||||
full ISO 8601 `published_at` and `deprecated: false`.
|
||||
|
||||
@@ -73,37 +73,42 @@ pipeline validates them against `schemas/contract.schema.json`.
|
||||
|
||||
A minimal deployment:
|
||||
|
||||
[`examples/simple.yaml`](examples/simple.yaml)
|
||||
[`examples/simple.yml`](examples/simple.yml)
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: alb
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-alb
|
||||
subnets: subnet-aaa,subnet-bbb
|
||||
security_group: sg-xxx
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
region: us-east-1
|
||||
id: alb
|
||||
infrastructure:
|
||||
alb:
|
||||
inputs:
|
||||
name: my-alb
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
region: us-east-1
|
||||
security_group: sg-xxx
|
||||
subnets: subnet-aaa,subnet-bbb
|
||||
version: 1.0.0
|
||||
name: alb-loadbalancer
|
||||
```
|
||||
|
||||
### Complex
|
||||
|
||||
A production deployment with optional inputs:
|
||||
|
||||
[`examples/complex.yaml`](examples/complex.yaml)
|
||||
[`examples/complex.yml`](examples/complex.yml)
|
||||
```yaml
|
||||
# Complex ALB with HTTPS + ACM cert (requires a consumer-supplied domain)
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: alb
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-alb
|
||||
subnets: subnet-aaa,subnet-bbb
|
||||
security_group: sg-xxx
|
||||
port: 443
|
||||
protocol: HTTPS
|
||||
region: us-east-1
|
||||
id: alb
|
||||
infrastructure:
|
||||
alb:
|
||||
inputs:
|
||||
name: my-production-alb
|
||||
port: 443
|
||||
protocol: HTTPS
|
||||
region: us-east-1
|
||||
security_group: sg-xxx
|
||||
subnets: subnet-aaa,subnet-bbb
|
||||
version: 1.0.0
|
||||
name: alb-loadbalancer
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# Complex ALB with HTTPS + ACM cert (requires a consumer-supplied domain)
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: alb
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-alb
|
||||
subnets: subnet-aaa,subnet-bbb
|
||||
security_group: sg-xxx
|
||||
port: 443
|
||||
protocol: HTTPS
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,14 @@
|
||||
# Complex ALB with HTTPS + ACM cert (requires a consumer-supplied domain)
|
||||
environment: dev
|
||||
id: alb
|
||||
infrastructure:
|
||||
alb:
|
||||
inputs:
|
||||
name: my-production-alb
|
||||
port: 443
|
||||
protocol: HTTPS
|
||||
region: us-east-1
|
||||
security_group: sg-xxx
|
||||
subnets: subnet-aaa,subnet-bbb
|
||||
version: 1.0.0
|
||||
name: alb-loadbalancer
|
||||
@@ -1,10 +0,0 @@
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: alb
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-alb
|
||||
subnets: subnet-aaa,subnet-bbb
|
||||
security_group: sg-xxx
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,13 @@
|
||||
environment: dev
|
||||
id: alb
|
||||
infrastructure:
|
||||
alb:
|
||||
inputs:
|
||||
name: my-alb
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
region: us-east-1
|
||||
security_group: sg-xxx
|
||||
subnets: subnet-aaa,subnet-bbb
|
||||
version: 1.0.0
|
||||
name: alb-loadbalancer
|
||||
@@ -80,35 +80,39 @@ pipeline validates them against `schemas/contract.schema.json`.
|
||||
|
||||
A minimal deployment:
|
||||
|
||||
[`examples/simple.yaml`](examples/simple.yaml)
|
||||
[`examples/simple.yml`](examples/simple.yml)
|
||||
```yaml
|
||||
# Simple CloudFront distribution (S3 origin, no WAF)
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: cloudfront
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_regional_domain_name: my-bucket.s3.us-east-1.amazonaws.com
|
||||
region: us-east-1
|
||||
id: cdn
|
||||
infrastructure:
|
||||
cloudfront:
|
||||
inputs:
|
||||
bucket_regional_domain_name: my-bucket.s3.us-east-1.amazonaws.com
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: cloudfront
|
||||
```
|
||||
|
||||
### Complex
|
||||
|
||||
A production deployment with optional inputs:
|
||||
|
||||
[`examples/complex.yaml`](examples/complex.yaml)
|
||||
[`examples/complex.yml`](examples/complex.yml)
|
||||
```yaml
|
||||
# Complex CloudFront with WAF + custom TTL + viewer protocol redirect
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: cloudfront
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_regional_domain_name: my-bucket.s3.us-east-1.amazonaws.com
|
||||
price_class: PriceClass_100
|
||||
viewer_protocol_policy: redirect-to-https
|
||||
default_ttl: 3600
|
||||
max_ttl: 86400
|
||||
waf_web_acl_arn: arn:aws:wafv2:us-east-1:000000000000:webacl/my-waf
|
||||
region: us-east-1
|
||||
id: cdn
|
||||
infrastructure:
|
||||
cloudfront:
|
||||
inputs:
|
||||
bucket_regional_domain_name: my-bucket.s3.us-east-1.amazonaws.com
|
||||
default_ttl: 3600
|
||||
max_ttl: 86400
|
||||
price_class: PriceClass_100
|
||||
region: us-east-1
|
||||
viewer_protocol_policy: redirect-to-https
|
||||
waf_web_acl_arn: arn:aws:wafv2:us-east-1:000000000000:webacl/my-waf
|
||||
version: 1.0.0
|
||||
name: cloudfront
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# Complex CloudFront with WAF + custom TTL + viewer protocol redirect
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: cloudfront
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_regional_domain_name: my-bucket.s3.us-east-1.amazonaws.com
|
||||
price_class: PriceClass_100
|
||||
viewer_protocol_policy: redirect-to-https
|
||||
default_ttl: 3600
|
||||
max_ttl: 86400
|
||||
waf_web_acl_arn: arn:aws:wafv2:us-east-1:000000000000:webacl/my-waf
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,15 @@
|
||||
# Complex CloudFront with WAF + custom TTL + viewer protocol redirect
|
||||
environment: dev
|
||||
id: cdn
|
||||
infrastructure:
|
||||
cloudfront:
|
||||
inputs:
|
||||
bucket_regional_domain_name: my-bucket.s3.us-east-1.amazonaws.com
|
||||
default_ttl: 3600
|
||||
max_ttl: 86400
|
||||
price_class: PriceClass_100
|
||||
region: us-east-1
|
||||
viewer_protocol_policy: redirect-to-https
|
||||
waf_web_acl_arn: arn:aws:wafv2:us-east-1:000000000000:webacl/my-waf
|
||||
version: 1.0.0
|
||||
name: cloudfront
|
||||
@@ -1,7 +0,0 @@
|
||||
# Simple CloudFront distribution (S3 origin, no WAF)
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: cloudfront
|
||||
environment: dev
|
||||
inputs:
|
||||
bucket_regional_domain_name: my-bucket.s3.us-east-1.amazonaws.com
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,10 @@
|
||||
# Simple CloudFront distribution (S3 origin, no WAF)
|
||||
environment: dev
|
||||
id: cdn
|
||||
infrastructure:
|
||||
cloudfront:
|
||||
inputs:
|
||||
bucket_regional_domain_name: my-bucket.s3.us-east-1.amazonaws.com
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: cloudfront
|
||||
@@ -60,29 +60,34 @@ pipeline validates them against `schemas/contract.schema.json`.
|
||||
|
||||
A minimal deployment:
|
||||
|
||||
[`examples/simple.yaml`](examples/simple.yaml)
|
||||
[`examples/simple.yml`](examples/simple.yml)
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecr
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-repo
|
||||
region: us-east-1
|
||||
id: ecr
|
||||
infrastructure:
|
||||
ecr:
|
||||
inputs:
|
||||
name: my-repo
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecr-repo
|
||||
```
|
||||
|
||||
### Complex
|
||||
|
||||
A production deployment with optional inputs:
|
||||
|
||||
[`examples/complex.yaml`](examples/complex.yaml)
|
||||
[`examples/complex.yml`](examples/complex.yml)
|
||||
```yaml
|
||||
# Complex ECR with lifecycle policy + image scanning
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecr
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-repo
|
||||
region: us-east-1
|
||||
id: ecr
|
||||
infrastructure:
|
||||
ecr:
|
||||
inputs:
|
||||
name: my-production-repo
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecr-repo
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Complex ECR with lifecycle policy + image scanning
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecr
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-repo
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,10 @@
|
||||
# Complex ECR with lifecycle policy + image scanning
|
||||
environment: dev
|
||||
id: ecr
|
||||
infrastructure:
|
||||
ecr:
|
||||
inputs:
|
||||
name: my-production-repo
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecr-repo
|
||||
@@ -1,6 +0,0 @@
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecr
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-repo
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,9 @@
|
||||
environment: dev
|
||||
id: ecr
|
||||
infrastructure:
|
||||
ecr:
|
||||
inputs:
|
||||
name: my-repo
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecr-repo
|
||||
@@ -58,29 +58,34 @@ pipeline validates them against `schemas/contract.schema.json`.
|
||||
|
||||
A minimal deployment:
|
||||
|
||||
[`examples/simple.yaml`](examples/simple.yaml)
|
||||
[`examples/simple.yml`](examples/simple.yml)
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecs-cluster
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-cluster
|
||||
region: us-east-1
|
||||
id: clus
|
||||
infrastructure:
|
||||
ecs-cluster:
|
||||
inputs:
|
||||
name: my-cluster
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecs-cluster
|
||||
```
|
||||
|
||||
### Complex
|
||||
|
||||
A production deployment with optional inputs:
|
||||
|
||||
[`examples/complex.yaml`](examples/complex.yaml)
|
||||
[`examples/complex.yml`](examples/complex.yml)
|
||||
```yaml
|
||||
# Complex ECS cluster with container insights
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecs-cluster
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-cluster
|
||||
region: us-east-1
|
||||
id: clus
|
||||
infrastructure:
|
||||
ecs-cluster:
|
||||
inputs:
|
||||
name: my-production-cluster
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecs-cluster
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Complex ECS cluster with container insights
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecs-cluster
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-cluster
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,10 @@
|
||||
# Complex ECS cluster with container insights
|
||||
environment: dev
|
||||
id: clus
|
||||
infrastructure:
|
||||
ecs-cluster:
|
||||
inputs:
|
||||
name: my-production-cluster
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecs cluster
|
||||
@@ -1,6 +0,0 @@
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecs-cluster
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-cluster
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,9 @@
|
||||
environment: dev
|
||||
id: clus
|
||||
infrastructure:
|
||||
ecs-cluster:
|
||||
inputs:
|
||||
name: my-cluster
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecs cluster
|
||||
@@ -80,36 +80,41 @@ pipeline validates them against `schemas/contract.schema.json`.
|
||||
|
||||
A minimal deployment:
|
||||
|
||||
[`examples/simple.yaml`](examples/simple.yaml)
|
||||
[`examples/simple.yml`](examples/simple.yml)
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecs-service
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-service
|
||||
region: us-east-1
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
id: svc
|
||||
infrastructure:
|
||||
ecs-service:
|
||||
inputs:
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
name: my-service
|
||||
port: 80
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecs-service
|
||||
```
|
||||
|
||||
### Complex
|
||||
|
||||
A production deployment with optional inputs:
|
||||
|
||||
[`examples/complex.yaml`](examples/complex.yaml)
|
||||
[`examples/complex.yml`](examples/complex.yml)
|
||||
```yaml
|
||||
# Complex ECS service with env vars + health check
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecs-service
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-service
|
||||
region: us-east-1
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 8080
|
||||
env:
|
||||
LOG_LEVEL: info
|
||||
ENVIRONMENT: production
|
||||
id: svc
|
||||
infrastructure:
|
||||
ecs-service:
|
||||
inputs:
|
||||
env:
|
||||
ENVIRONMENT: production
|
||||
LOG_LEVEL: info
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
name: my-production-service
|
||||
port: 8080
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecs-service
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# Complex ECS service with env vars + health check
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecs-service
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-service
|
||||
region: us-east-1
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 8080
|
||||
env:
|
||||
LOG_LEVEL: info
|
||||
ENVIRONMENT: production
|
||||
@@ -0,0 +1,15 @@
|
||||
# Complex ECS service with env vars + health check
|
||||
environment: dev
|
||||
id: svc
|
||||
infrastructure:
|
||||
ecs-service:
|
||||
inputs:
|
||||
env:
|
||||
ENVIRONMENT: production
|
||||
LOG_LEVEL: info
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
name: my-production-service
|
||||
port: 8080
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecs service
|
||||
@@ -1,8 +0,0 @@
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: ecs-service
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-service
|
||||
region: us-east-1
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
port: 80
|
||||
@@ -0,0 +1,11 @@
|
||||
environment: dev
|
||||
id: svc
|
||||
infrastructure:
|
||||
ecs-service:
|
||||
inputs:
|
||||
image: public.ecr.aws/docker/library/nginx:latest
|
||||
name: my-service
|
||||
port: 80
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: ecs service
|
||||
@@ -66,29 +66,34 @@ pipeline validates them against `schemas/contract.schema.json`.
|
||||
|
||||
A minimal deployment:
|
||||
|
||||
[`examples/simple.yaml`](examples/simple.yaml)
|
||||
[`examples/simple.yml`](examples/simple.yml)
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: iam-role
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-task-role
|
||||
region: us-east-1
|
||||
id: role
|
||||
infrastructure:
|
||||
iam-role:
|
||||
inputs:
|
||||
name: my-task-role
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: iam-role
|
||||
```
|
||||
|
||||
### Complex
|
||||
|
||||
A production deployment with optional inputs:
|
||||
|
||||
[`examples/complex.yaml`](examples/complex.yaml)
|
||||
[`examples/complex.yml`](examples/complex.yml)
|
||||
```yaml
|
||||
# Complex IAM role with managed policies
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: iam-role
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-task-role
|
||||
region: us-east-1
|
||||
id: role
|
||||
infrastructure:
|
||||
iam-role:
|
||||
inputs:
|
||||
name: my-production-task-role
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: iam-role
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Complex IAM role with managed policies
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: iam-role
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-production-task-role
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,10 @@
|
||||
# Complex IAM role with managed policies
|
||||
environment: dev
|
||||
id: role
|
||||
infrastructure:
|
||||
iam-role:
|
||||
inputs:
|
||||
name: my-production-task-role
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: iam role
|
||||
@@ -1,6 +0,0 @@
|
||||
uses: acdl/pipelines/deploy.yaml@v1.6
|
||||
module: iam-role
|
||||
environment: dev
|
||||
inputs:
|
||||
name: my-task-role
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,9 @@
|
||||
environment: dev
|
||||
id: role
|
||||
infrastructure:
|
||||
iam-role:
|
||||
inputs:
|
||||
name: my-task-role
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: iam role
|
||||
@@ -67,29 +67,35 @@ pipeline validates them against `schemas/contract.schema.json`.
|
||||
|
||||
A minimal deployment:
|
||||
|
||||
[`examples/simple.yaml`](examples/simple.yaml)
|
||||
[`examples/simple.yml`](examples/simple.yml)
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.8
|
||||
module: kms-key
|
||||
environment: dev
|
||||
inputs:
|
||||
description: "Simple CMK for testing"
|
||||
region: us-east-1
|
||||
id: kms
|
||||
infrastructure:
|
||||
kms-key:
|
||||
inputs:
|
||||
description: Simple CMK for testing
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: kms-key
|
||||
```
|
||||
|
||||
### Complex
|
||||
|
||||
A production deployment with optional inputs:
|
||||
|
||||
[`examples/complex.yaml`](examples/complex.yaml)
|
||||
[`examples/complex.yml`](examples/complex.yml)
|
||||
```yaml
|
||||
uses: acdl/pipelines/deploy.yaml@v1.8
|
||||
module: kms-key
|
||||
environment: dev
|
||||
inputs:
|
||||
description: "Production CMK with 90-day deletion window"
|
||||
region: us-east-1
|
||||
deletion_window_days: 90
|
||||
id: kms
|
||||
infrastructure:
|
||||
kms-key:
|
||||
inputs:
|
||||
deletion_window_days: 90
|
||||
description: Production CMK with 90-day deletion window
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: kms-key
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
uses: acdl/pipelines/deploy.yaml@v1.8
|
||||
module: kms-key
|
||||
environment: dev
|
||||
inputs:
|
||||
description: "Production CMK with 90-day deletion window"
|
||||
region: us-east-1
|
||||
deletion_window_days: 90
|
||||
@@ -0,0 +1,10 @@
|
||||
environment: dev
|
||||
id: kms
|
||||
infrastructure:
|
||||
kms-key:
|
||||
inputs:
|
||||
deletion_window_days: 90
|
||||
description: Production CMK with 90-day deletion window
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: kms key
|
||||
@@ -1,6 +0,0 @@
|
||||
uses: acdl/pipelines/deploy.yaml@v1.8
|
||||
module: kms-key
|
||||
environment: dev
|
||||
inputs:
|
||||
description: "Simple CMK for testing"
|
||||
region: us-east-1
|
||||
@@ -0,0 +1,9 @@
|
||||
environment: dev
|
||||
id: kms
|
||||
infrastructure:
|
||||
kms-key:
|
||||
inputs:
|
||||
description: Simple CMK for testing
|
||||
region: us-east-1
|
||||
version: 1.0.0
|
||||
name: kms key
|
||||