a90a7562b9
---ci---
phase: 21
milestone: v1.6
status: verify
lessons:
- P0 fix: docs/_config.yml had conflicting theme + remote_theme (would
break the GitHub Pages build). Removed the conflicting theme: key,
kept remote_theme: minimal-mistakes.
- P2 fix: removed unused json + os imports from core/environment_check.py.
---/ci---
Multi-persona review of v1.6 phase 21 (docs restructure + core rename +
environments scaffold).
P0 (blocking) — AUTO-FIXED:
- M1: docs/_config.yml declared both and
. Jekyll rejects using
both; the Pages build would fail. Fixed: removed the line,
kept (minimal-mistakes, which provides the
layout the defaults reference).
P2 (nits) — AUTO-FIXED:
- M2: core/environment_check.py imported + but never used
them. Removed.
P1 (important) — FLAGGED FOR POST-HOC REVIEW (do not block ship):
- C1 (pre-existing, from v1.5 review C2): .github/workflows/deploy.yml
checks out the platform repo at , but no floating tag
exists (only v1.4.0 / v1.4.1). Operator must create a floating v1.4
tag or change the ref to v1.4.1 (or v1.6.0 now that it exists). The
consumer guide + sample contract also reference @v1.4.
- C2: docs/_config.yml key is not a standard minimal-mistakes
navigation config (that theme reads _data/navigation.yml). The
key is harmless metadata but won't render a real nav. Recommend adding
docs/_data/navigation.yml for the theme, or switching to a theme that
reads from _config.yml. Non-blocking for the docs content.
- S1 (pre-existing, from v1.5 review S1): the static-key override in
deploy.yml sets ACDL_AWS_ACCESS_KEY_ID/ACDL_AWS_SECRET_ACCESS_KEY as env
vars on the configure-aws-credentials step, but that action reads AWS_*
or its own access-key/secret-key inputs, not ACDL_AWS_*. The override
is not actually wired. Phase 21 did not touch this step.
Verified: byte-identical workflows (CI + deploy); dev.json valid JSON;
all core Python compiles; path-traversal on --env is safe (no file match
-> onboarding prompt, exit 1); all docs internal links resolve; 166
tests pass; run_ci.sh green. The run_platform.sh env-check ordering is
correct (default contract is assigned before the env check runs).
99 lines
3.3 KiB
Python
99 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Environment onboarding check.
|
|
|
|
Reads a contract's `environment` field and looks up the matching
|
|
`core/environments/<name>.json`. If no matching file exists, prints a
|
|
friendly onboarding prompt and exits non-zero, halting the pipeline before
|
|
any work is done.
|
|
|
|
Usage:
|
|
python3 core/environment_check.py <contract.yaml>
|
|
python3 core/environment_check.py --env dev
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
try:
|
|
import yaml
|
|
except ImportError:
|
|
sys.stderr.write("PyYAML is required (pip install pyyaml)\n")
|
|
sys.exit(2)
|
|
|
|
|
|
def _environments_dir(root=None):
|
|
if root is None:
|
|
root = Path(__file__).resolve().parent.parent
|
|
return Path(root) / "core" / "environments"
|
|
|
|
|
|
def _contract_environment(contract_path):
|
|
with open(contract_path) as f:
|
|
contract = yaml.safe_load(f)
|
|
return contract.get("environment")
|
|
|
|
|
|
def _onboarding_message(env_name):
|
|
return (
|
|
"=== ACDL Environment Onboarding ===\n"
|
|
f"No environment named '{env_name}' is bound to this repository.\n\n"
|
|
"ACDL environments are platform-managed. The platform provisions on\n"
|
|
"your behalf:\n"
|
|
" - an AWS account (or a scoped partition of one)\n"
|
|
" - a network (VPC + subnets)\n"
|
|
" - a state backend (an S3 bucket + DynamoDB lock table)\n"
|
|
" - an IAM role surfaced to your repo via attribute-based\n"
|
|
" authorization (ABAC)\n\n"
|
|
"You do not provide an AWS account, VPC, subnet, or state bucket.\n\n"
|
|
"To request an environment:\n"
|
|
" 1. Contact the platform team with your repo name + the\n"
|
|
" environment name you need (e.g. 'dev').\n"
|
|
" 2. The platform team provisions the account/network/state/role\n"
|
|
" and binds the environment to your repo.\n"
|
|
" 3. Your next pipeline run will proceed normally.\n\n"
|
|
"Expected turnaround: contact the platform team for current SLA.\n"
|
|
"===================================\n"
|
|
)
|
|
|
|
|
|
def check(contract_path=None, env_name=None, root=None):
|
|
"""Return (ok: bool, message: str).
|
|
|
|
If env_name is None it is read from the contract at contract_path.
|
|
ok is True when an environment definition exists; False otherwise.
|
|
On False, message is the friendly onboarding prompt.
|
|
"""
|
|
if env_name is None:
|
|
if contract_path is None:
|
|
return (False, "no contract or environment name supplied")
|
|
env_name = _contract_environment(contract_path)
|
|
if env_name is None:
|
|
return (False, "contract has no 'environment' field")
|
|
|
|
env_file = _environments_dir(root) / f"{env_name}.json"
|
|
if env_file.is_file():
|
|
return (True, f"environment '{env_name}' is bound ({env_file})")
|
|
return (False, _onboarding_message(env_name))
|
|
|
|
|
|
def main(argv):
|
|
contract_path = None
|
|
env_name = None
|
|
for arg in argv[1:]:
|
|
if arg.startswith("--env="):
|
|
env_name = arg.split("=", 1)[1]
|
|
elif arg.startswith("--"):
|
|
sys.stderr.write(f"unknown flag: {arg}\n")
|
|
return 2
|
|
else:
|
|
contract_path = arg
|
|
|
|
ok, message = check(contract_path=contract_path, env_name=env_name)
|
|
if ok:
|
|
print(message)
|
|
return 0
|
|
sys.stdout.write(message)
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv)) |