fix(deploy): Checkpoints — preserve STOPPED history, fix filter + placement
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 2m4s
CI / docker (push) Successful in 1m15s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 41s

- Backend: rename deleteTerminalByAppAndEnvironment → deleteFailedByAppAndEnvironment.
  STOPPED rows were being wiped on every redeploy, so Checkpoints was always empty.
  Now only FAILED rows are pruned; STOPPED deployments are retained as restorable
  checkpoints (they still carry deployed_config_snapshot from their RUNNING window).
- UI filter: any deployment with a snapshot is a checkpoint (was RUNNING|DEGRADED only,
  which excluded the main case — the previous blue/green deployment now in STOPPED).
- UI placement: Checkpoints disclosure now renders inside IdentitySection, matching
  the design spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-23 10:26:46 +02:00
parent 007597715a
commit c6aef5ab35
11 changed files with 49 additions and 30 deletions

View File

@@ -126,8 +126,8 @@ public class PostgresDeploymentRepository implements DeploymentRepository {
}
@Override
public void deleteTerminalByAppAndEnvironment(UUID appId, UUID environmentId) {
jdbc.update("DELETE FROM deployments WHERE app_id = ? AND environment_id = ? AND status IN ('STOPPED', 'FAILED')",
public void deleteFailedByAppAndEnvironment(UUID appId, UUID environmentId) {
jdbc.update("DELETE FROM deployments WHERE app_id = ? AND environment_id = ? AND status = 'FAILED'",
appId, environmentId);
}

View File

@@ -87,6 +87,27 @@ class PostgresDeploymentRepositoryIT extends AbstractPostgresIT {
assertThat(loaded.deployedConfigSnapshot()).isNull();
}
@Test
void deleteFailedByAppAndEnvironment_keepsStoppedAndActive() {
// given: one STOPPED (checkpoint), one FAILED, one RUNNING
UUID stoppedId = repository.create(appId, appVersionId, envId, "stopped");
repository.updateStatus(stoppedId, com.cameleer.server.core.runtime.DeploymentStatus.STOPPED, null, null);
UUID failedId = repository.create(appId, appVersionId, envId, "failed");
repository.updateStatus(failedId, com.cameleer.server.core.runtime.DeploymentStatus.FAILED, null, "boom");
UUID runningId = repository.create(appId, appVersionId, envId, "running");
repository.updateStatus(runningId, com.cameleer.server.core.runtime.DeploymentStatus.RUNNING, "c1", null);
// when
repository.deleteFailedByAppAndEnvironment(appId, envId);
// then: STOPPED and RUNNING survive; FAILED is gone
assertThat(repository.findById(stoppedId)).isPresent();
assertThat(repository.findById(runningId)).isPresent();
assertThat(repository.findById(failedId)).isEmpty();
}
@Test
void deployedConfigSnapshot_canBeClearedToNull() {
UUID jarVersionId = UUID.randomUUID();