diff --git a/internal/controller/rollout_controller.go b/internal/controller/rollout_controller.go index d592a9f..e4bb095 100644 --- a/internal/controller/rollout_controller.go +++ b/internal/controller/rollout_controller.go @@ -218,7 +218,8 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } // Gating logic: gates already evaluated at the beginning, check if deployment should be blocked - if !r.hasManualDeployment(&rollout) { + // Skip gate blocking on first deploy (empty history) so a rollout always reaches its initial version. + if !r.hasManualDeployment(&rollout) && len(rollout.Status.History) > 0 { if !gatesPassing { return ctrl.Result{}, nil // Status already updated at the beginning } @@ -227,9 +228,15 @@ func (r *RolloutReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } } + // On first deploy, fall back to raw release candidates if gates filtered them all out. + if len(rollout.Status.History) == 0 && len(gatedReleaseCandidates) == 0 { + gatedReleaseCandidates = releaseCandidates + } + // Evaluate health checks - block deployment if health checks are not healthy // For manual deployments (WantedVersion or force-deploy), skip this check - if !r.hasManualDeployment(&rollout) { + // Also skip when no version has been deployed yet (first deploy) — nothing is running so health checks will be unhealthy by definition + if !r.hasManualDeployment(&rollout) && len(rollout.Status.History) > 0 { healthChecksHealthy, healthCheckMessage, err := r.evaluateHealthChecks(ctx, req.Namespace, &rollout) if err != nil { log.Error(err, "Failed to evaluate health checks")