The alphabetical-sort lint requires env keys ascending; the --no-verify commit that added CDN_TEST_STACK_DIR appended it after CDN_TEST_WEB_ROOT (STACK < WEB). Reorder it before WEB_ROOT. The keys are independent assignments, so sourcing order is unchanged at runtime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The external-flavor CLI test checked for an npm/ dir under the served web root, but that bind source survives the inter-round purge, so a prior internal-flavor round left it behind and every later external round failed a false positive. Check the CDN compose instance dir (directories.instance) instead: it is purged between rounds and never recreated by the front-proxy-only external deploy, so it is present iff the build stack is actually deployed -- the real invariant the test means to assert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the ansible container-run plus copy publish with a proper compose
build service deployed via the role's own stack. The one-shot writes the
npm mirror straight into the node-local served CDN root that openresty
reads, so there is no DiND bind-layer mismatch and no separate publish step.
- meta/volumes.yml: build mounts (served root rw, staged manifests ro,
build script as a docker config)
- templates/compose.yml.j2 + env.j2: one-shot build service (restart no,
swarm condition none), CDN_WEB_ROOT and ROLES_DIR from the mount targets
- meta/services.yml: placement manager, replicas 1, resources, deploy modes
- meta/networks.yml: local subnet (the role now ships a stack)
- meta/variants.yml: internal and external flavor variants
- meta/tests.yml + test.env.j2 + files/test/test.sh: flavor-branching e2e
- tasks: internal flavor deploys the stack via sys-stk-full; drop the
container-run and ansible-copy publish; remove the unused dev-git include
- vars: pin CDN_FLAVOR to the role id (scope-safe) and compute host paths
directly (avoid the volume-lookup double-template)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>