Add count-based release retention (keep newest N per channel)
Summary
- Add an
Itemsunit toRetentionPolicyUnitso a lifecycle can keep a limited number of releases per channel instead of a time window. Purely additive (ordinal 4) — existing units keep their values. - When
Itemsis active,RetentionPolicyEnforcerkeeps the newest N releases per channel and cascade-deletes the rest: each pruned release takes its deployments + server-task data (task, activity log, task log, interruptions, checkpoints) + completions +ReleaseSelectedPackagerows with it, then its process/variable snapshots are ref-count-GC'd (a snapshot still referenced by a surviving release is kept). - Currently-deployed releases and releases with an in-progress (non-terminal) deployment are always preserved, even beyond N.
- The existing time-based path is unchanged; its guarded release/package delete is now factored into a shared helper used by both paths.
Safety / non-breaking
- Opt-in: the unit only takes effect when a lifecycle disables keep-forever. The default (
ReleaseRetentionKeepForever = true) prunes nothing. - A non-positive keep count is treated as keep-forever (no-op), never "delete everything".
- Pruned deployments are captured at read time and deleted by id; the release/package delete re-checks for referencing deployments at delete time. A release that gains a deployment concurrently is skipped rather than orphaned, and an in-flight deploy is never killed.
- Children are deleted before the release/deployment anchor, so a mid-sequence crash leaves the anchor for the next run to retry idempotently.
- No schema change — the unit is stored as the existing integer enum column.
Test plan
-
Unit ( RetentionPolicyEnforcerTests):GetReleasesExceedingCountmatrix — keep-N-per-channel, per-channel independence, preserved-beyond-window, fewer-than-N, CreatedDate tie broken by id, empty, all-preserved, keep-zero contract; plus a pin thatItemsmakes per-environment deployment pruning a no-op (cleanup defers to the release cascade). -
Integration ( ReleaseRetentionCountTests, real Postgres): oldest-beyond-N pruned with snapshot GC; full deployment cascade (deployment + task data + completion + packages + snapshot); currently-deployed preserved beyond N; in-progress deployment preserved beyond N; per-channel keep-N; shared snapshot kept by ref-count; keep-forever prunes nothing; keep-count-zero prunes nothing. -
Full unit suite green (5752/5752). -
All retention integration tests green (19/19: time-based + task-cleanup + count-based). -
Full solution builds with 0 errors.