You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Resolves#36160 · Epic #35693
### Proposed Changes
* Add a small Python promotion engine at `cicd/evergreen-tracks/`
(managed with `uv`) that advances three **floating Docker tags** —
`latest` / `standard` / `trailing` — across the linear GA CalVer stream
by release **age** (newest GA, ~14d, ~28d; thresholds configurable).
* State is **registry-only** via marker tags: `<version>_tainted`
(forward-only block — a bad release can't propagate to more conservative
tracks) and `<track>_hold` (sticky manual freeze). No separate
datastore; the audit trail is the Actions run logs.
* Tags are re-pointed **by digest** with `docker buildx imagetools
create` (no layer re-push). Age is read from the **CalVer date in the
version**, not build/publish date, so emergency backports of older
releases can't be swept into a future promotion.
* Two workflows: `cicd_evergreen-tracks-promote.yml` (daily cron +
dispatch) and `cicd_evergreen-tracks-admin.yml` (manual taint/hold).
* Document Release Tracks (usage + the "why") in the root `README.md`.
### Checklist
- [x] Tests — 60 unit tests (pure planner, calver, markers, registry
parser, CLI); run with `cd cicd/evergreen-tracks && uv run pytest`.
- [ ] Translations — N/A (CI tooling, no UI strings).
- [x] Security Implications Contemplated — see notes below.
### Additional Info
**Tag control:** `latest` is moved on-demand by the release pipeline
(`promote-latest` job in `cicd_6-release.yml`, `--tracks latest`) the
moment a GA's images publish, for `dotcms/dotcms` and
`dotcms/dotcms-dev` — the old `deploy-docker latest: true` path is
unwired (`latest: false`). The daily cron ages `standard`/`trailing`
forward and **always applies** (no separate enable gate). To pause
promotion, disable the scheduled workflow or `hold` the track; to block
a bad release, `taint` it. All registry mutations (release-driven
latest, cron, admin) serialize under one concurrency group
`evergreen-tracks-registry`.
**Credential scope (important):** promotion needs only **write**, but
`untaint` and `release-hold` call the Hub delete API — the
`DOCKER_USERNAME`/`DOCKER_TOKEN` used here **must have
Read/Write/Delete** scope, or those two admin actions fail. (Verified
both ways: write-only 403s on delete; RWD succeeds.)
**Validation:** the full lifecycle (promote-by-age, taint→skip,
hold→freeze, release-hold→resume, untaint→restore, teardown) was
exercised end-to-end against the `dotcms/dotcms-test` sandbox and
verified by digest. The live smoke can't run in `core-workflow-test` CI
(it intentionally carries no live Docker secrets), so it should run here
in `core` CI / on dispatch.
**Security notes:** free-text workflow inputs are passed via `env:` (not
interpolated into `run:`) to avoid expression injection; no tokens are
logged; least-privilege `permissions: contents: read` on the promote
workflow.
**Out of scope (per epic):** LTS-line tracks, Java-variant track tags,
the Cloud control-plane UI, and update *cadence* changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Steve Freudenthaler <stevefreudenthaler@36:c1:bd:05:e8:20.home>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
0 commit comments