The package rename moved every file the older PR-docs link to, leaving 60 dead links. Only the link targets are rewritten, never the visible text and never a statement: those documents record what was true when they were written, GitTally in the prose included. A snapshot may be outdated; it should still be navigable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5.3 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
The Problem
Retention is count-based only: artifacts.retentionPerBranch keeps the newest N builds per branch, no matter how old they are.
The legacy script also supported an age limit (GITTALLY_ARTIFACT_BUILD_RETENTION_PER_BRANCH=3d), which the rewrite dropped — the migration doc declared the age suffix unsupported.
Without an age limit there is no way to bound how long stale build logs and reports stay on disk, e.g. on branches that build rarely.
Non-Goals
- No either/or single config value like legacy (
3or3d); count and age are separate keys that can be combined. - No age pruning of a branch's newest build — a dormant branch keeps its last status and artifacts.
- No change to the existing pruning of branches deleted from origin.
- No size-based (bytes) retention.
The Scenarios
Feature: age-based build retention combinable with count-based retention
Background
artifacts.retentionMaxAgetakes the shared duration format (s/m/h/dsuffix, likewatcher.newBranchMaxAge); empty (the default) means no age limit.- Pruning runs in the watcher poll cycle; artifact directories follow the surviving build results.
Scenario#000.01: Builds older than retentionMaxAge are pruned
So that stale logs and reports do not stay on disk indefinitely.
- Given
artifacts.retentionMaxAge: 30d- and a branch with builds within the retention count, some older than 30 days
- When the watcher prunes results and artifacts
- Then the builds older than 30 days are dropped together with their artifacts
- and the younger builds are kept.
Verified by
Scenario#000.02: Count and age combine as independent caps
So that one config can bound disk usage by count and staleness by age at the same time — which legacy could not.
- Given
artifacts.retentionPerBranch: 2andartifacts.retentionMaxAge: 30d - When the watcher prunes
- Then a build is kept only while it is among the branch's newest 2 builds and younger than 30 days
- and a build violating either limit is dropped.
Verified by
Scenario#000.03: A branch's newest build is never age-pruned
So that a dormant branch keeps its last build status visible, matching the legacy age-retention behavior.
- Given a branch whose newest build is older than
retentionMaxAge - When the watcher prunes
- Then that newest build is kept regardless of its age.
Verified by
Scenario#000.04: keepLatestGreen shields the latest green build from the age limit
So that the permanent /branches/<branch-key>/… artifact links stay valid while newer builds fail, even under an age limit.
- Given
artifacts.keepLatestGreen: true(the default)- and a branch whose latest green build is older than
retentionMaxAge, followed by newer failed builds
- and a branch whose latest green build is older than
- When the watcher prunes
- Then the latest green build and its artifacts are kept.
Verified by
Scenario#000.05: The default keeps existing behavior unchanged
So that existing installations are unaffected by the new key.
- Given
artifacts.retentionMaxAgeis unset or empty - When the watcher prunes
- Then only the count-based retention applies, exactly as before this PR.
Verified by
- FileBuildResultRepositoryTest (pre-existing count-only prune tests)
The Solution
- New config key
artifacts.retentionMaxAge, parsed with the existingDurationParser; all three config sync points are updated (GitTallyConfig, theinittemplates,docs/configuration.md). BuildResultRepository.prunetakes an optionalretentionCutoff: Instant?; entries started before the cutoff are dropped, except each branch's newest entry and — withkeepLatestGreen— the newest green entry.- The
Watchercomputes the cutoff from its injectedClockon every poll cycle, so the repository stays clock-free and deterministic in tests, and config changes apply without restart like the other retention keys. - Artifact directories need no separate handling: the artifact store already prunes by the surviving build results.
Open Questions
- An invalid
retentionMaxAgevalue (e.g.30x) surfaces as a poll-cycle error in the log and the watcher state, not as a startup failure — consistent with howwatcher.newBranchMaxAgeis handled.
Additional Changes
docs/migration-from-legacy.mdnow maps a legacy age value ofGITTALLY_ARTIFACT_BUILD_RETENTION_PER_BRANCHtoartifacts.retentionMaxAgeinstead of declaring the age suffix unsupported.