diff --git a/internal/controller/helmchart_controller.go b/internal/controller/helmchart_controller.go index 9197e11e7..4f1d1a02e 100644 --- a/internal/controller/helmchart_controller.go +++ b/internal/controller/helmchart_controller.go @@ -456,8 +456,15 @@ func (r *HelmChartReconciler) reconcileSource(ctx context.Context, sp *patch.Ser // Assert source has an artifact if s.GetArtifact() == nil || !r.Storage.ArtifactExist(*s.GetArtifact()) { - // Set the condition to indicate that the source has no artifact for all types except OCI HelmRepository - if helmRepo, ok := s.(*sourcev1.HelmRepository); !ok || helmRepo.Spec.Type != sourcev1.HelmRepositoryTypeOCI { + // OCI HelmRepository sources do not produce artifacts themselves, but a + // failed source reconcile should still block chart builds to avoid + // surfacing a later generic build error on the HelmChart. + if helmRepo, ok := s.(*sourcev1.HelmRepository); ok && helmRepo.Spec.Type == sourcev1.HelmRepositoryTypeOCI { + if ready := conditions.Get(helmRepo, meta.ReadyCondition); ready != nil && ready.Status == metav1.ConditionFalse { + conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, ready.Reason, "%s", ready.Message) + return sreconcile.ResultRequeue, nil + } + } else { conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, "NoSourceArtifact", "no artifact available for %s source '%s'", obj.Spec.SourceRef.Kind, obj.Spec.SourceRef.Name) r.eventLogf(ctx, obj, eventv1.EventTypeTrace, "NoSourceArtifact", diff --git a/internal/controller/helmchart_controller_test.go b/internal/controller/helmchart_controller_test.go index 23188e968..4f2d10f2b 100644 --- a/internal/controller/helmchart_controller_test.go +++ b/internal/controller/helmchart_controller_test.go @@ -779,6 +779,43 @@ func TestHelmChartReconciler_reconcileSource(t *testing.T) { })) }, }, + { + name: "ResultRequeue when OCI HelmRepository source is not ready", + source: &sourcev1.HelmRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "helmrepository", + Namespace: "default", + }, + Spec: sourcev1.HelmRepositorySpec{ + Type: sourcev1.HelmRepositoryTypeOCI, + URL: "oci://example.com/charts", + }, + Status: sourcev1.HelmRepositoryStatus{ + Conditions: []metav1.Condition{ + *conditions.FalseCondition(meta.ReadyCondition, sourcev1.AuthenticationFailedReason, "failed to login to OCI registry"), + }, + }, + }, + beforeFunc: func(obj *sourcev1.HelmChart) { + obj.Spec.Chart = "helmchart" + obj.Spec.SourceRef = sourcev1.LocalHelmChartSourceReference{ + Name: "helmrepository", + Kind: sourcev1.HelmRepositoryKind, + } + conditions.MarkReconciling(obj, meta.ProgressingReason, "foo") + conditions.MarkUnknown(obj, meta.ReadyCondition, meta.ProgressingReason, "foo") + }, + want: sreconcile.ResultRequeue, + assertFunc: func(g *WithT, build chart.Build, obj sourcev1.HelmChart) { + g.Expect(build.Complete()).To(BeFalse()) + + g.Expect(obj.Status.Conditions).To(conditions.MatchConditions([]metav1.Condition{ + *conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to login to OCI registry"), + *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"), + *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "foo"), + })) + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {