feat: JAR retention policy with nightly cleanup job
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m23s
CI / docker (push) Successful in 1m9s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 40s

Per-environment "keep last N versions" setting (default 5, null for
unlimited). Nightly scheduled job at 03:00 deletes old versions from
both database and disk, skipping any version that is currently deployed.

Full stack:
- V6 migration: adds jar_retention_count column to environments
- Environment record, repository, service, admin controller endpoint
- JarRetentionJob: @Scheduled nightly, iterates environments and apps
- UI: retention policy editor on admin Environments page with
  toggle between limited/unlimited and version count input
- AppVersionRepository.delete() for version cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-08 19:06:28 +02:00
parent 863a992cc4
commit 7e47f1628d
11 changed files with 252 additions and 3 deletions

View File

@@ -9,4 +9,5 @@ public interface AppVersionRepository {
Optional<AppVersion> findById(UUID id);
int findMaxVersion(UUID appId);
UUID create(UUID appId, int version, String jarPath, String jarChecksum, String jarFilename, Long jarSizeBytes);
void delete(UUID id);
}

View File

@@ -11,5 +11,6 @@ public record Environment(
boolean production,
boolean enabled,
Map<String, Object> defaultContainerConfig,
Integer jarRetentionCount,
Instant createdAt
) {}

View File

@@ -12,5 +12,6 @@ public interface EnvironmentRepository {
UUID create(String slug, String displayName, boolean production);
void update(UUID id, String displayName, boolean production, boolean enabled);
void updateDefaultContainerConfig(UUID id, Map<String, Object> defaultContainerConfig);
void updateJarRetentionCount(UUID id, Integer jarRetentionCount);
void delete(UUID id);
}

View File

@@ -38,6 +38,14 @@ public class EnvironmentService {
repo.updateDefaultContainerConfig(id, defaultContainerConfig);
}
public void updateJarRetentionCount(UUID id, Integer jarRetentionCount) {
getById(id); // verify exists
if (jarRetentionCount != null && jarRetentionCount < 1) {
throw new IllegalArgumentException("Retention count must be at least 1 or null for unlimited");
}
repo.updateJarRetentionCount(id, jarRetentionCount);
}
public void delete(UUID id) {
Environment env = getById(id);
if ("default".equals(env.slug())) {