fix(deploy): include DEGRADED deploys as restorable checkpoints

Snapshot is written by DeploymentExecutor before the RUNNING/DEGRADED
split, so DEGRADED rows already carry a deployed_config_snapshot. Treat
them as checkpoints — partial-healthy deploys still produced a working
config worth restoring. Aligns repo query with UI filter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-23 00:34:25 +02:00
parent 837e5d46f5
commit 4d4c59efe3
2 changed files with 11 additions and 3 deletions

View File

@@ -140,10 +140,12 @@ public class PostgresDeploymentRepository implements DeploymentRepository {
}
public Optional<Deployment> findLatestSuccessfulByAppAndEnv(UUID appId, UUID envId) {
// DEGRADED deploys also carry a snapshot (executor writes before the RUNNING/DEGRADED
// split), and represent a config that reached COMPLETE stage — restorable for the user.
var results = jdbc.query(
"SELECT " + SELECT_COLS + " FROM deployments "
+ "WHERE app_id = ? AND environment_id = ? "
+ "AND status = 'RUNNING' AND deployed_config_snapshot IS NOT NULL "
+ "AND status IN ('RUNNING', 'DEGRADED') AND deployed_config_snapshot IS NOT NULL "
+ "ORDER BY deployed_at DESC NULLS LAST LIMIT 1",
(rs, rowNum) -> mapRow(rs), appId, envId);
return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0));

View File

@@ -15,9 +15,15 @@ export function Checkpoints({ deployments, versions, currentDeploymentId, onRest
const [open, setOpen] = useState(false);
const versionMap = new Map(versions.map((v) => [v.id, v]));
// Only successful deployments (RUNNING with a deployedAt). Exclude the currently-running one.
// Deployments that reached COMPLETE stage and captured a snapshot (RUNNING or DEGRADED).
// Exclude the currently-running one.
const checkpoints = deployments
.filter((d) => d.deployedAt && d.status === 'RUNNING' && d.id !== currentDeploymentId)
.filter(
(d) =>
d.deployedAt &&
(d.status === 'RUNNING' || d.status === 'DEGRADED') &&
d.id !== currentDeploymentId,
)
.sort((a, b) => (b.deployedAt ?? '').localeCompare(a.deployedAt ?? ''));
return (