From 3a14b40c19372094e83140ee27928cc1921347db Mon Sep 17 00:00:00 2001 From: Ryan VanGundy Date: Sat, 6 Sep 2025 23:38:02 -0400 Subject: [PATCH] fix(init): Ensure oci blueprints self-reference When generating the `blueprint.yaml` file from an OCI artifact, it should reference the artifact in relevant sources. This fix ensures the injection step is performed. --- pkg/pipelines/init.go | 25 ++++++ pkg/pipelines/install.go | 15 ---- pkg/pipelines/install_test.go | 155 ---------------------------------- pkg/pipelines/pipeline.go | 12 ++- 4 files changed, 35 insertions(+), 172 deletions(-) diff --git a/pkg/pipelines/init.go b/pkg/pipelines/init.go index bac01cf43..4df614212 100644 --- a/pkg/pipelines/init.go +++ b/pkg/pipelines/init.go @@ -11,6 +11,7 @@ import ( "github.com/windsorcli/cli/pkg/artifact" "github.com/windsorcli/cli/pkg/blueprint" "github.com/windsorcli/cli/pkg/config" + "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/env" "github.com/windsorcli/cli/pkg/generators" @@ -302,6 +303,30 @@ func (p *InitPipeline) Execute(ctx context.Context) error { return nil } +// prepareTemplateData sets the fallbackBlueprintURL if the default blueprint URL is used. +// It calls the base pipeline's prepareTemplateData, checks for explicit blueprint context and local templates, +// and assigns the fallback URL for blueprint processing if necessary. +// Returns the prepared template data or an error. +func (p *InitPipeline) prepareTemplateData(ctx context.Context) (map[string][]byte, error) { + templateData, err := p.BasePipeline.prepareTemplateData(ctx) + if err != nil { + return nil, err + } + if ctx.Value("blueprint") == nil && p.artifactBuilder != nil { + blueprintHandler := p.withBlueprintHandler() + hasLocalTemplates := false + if blueprintHandler != nil { + if localTemplateData, err := blueprintHandler.GetLocalTemplateData(); err == nil && len(localTemplateData) > 0 { + hasLocalTemplates = true + } + } + if !hasLocalTemplates { + p.fallbackBlueprintURL = constants.GetEffectiveBlueprintURL() + } + } + return templateData, nil +} + // ============================================================================= // Private Methods // ============================================================================= diff --git a/pkg/pipelines/install.go b/pkg/pipelines/install.go index 260c17af0..1d4899fec 100644 --- a/pkg/pipelines/install.go +++ b/pkg/pipelines/install.go @@ -139,18 +139,3 @@ func (p *InstallPipeline) Execute(ctx context.Context) error { return nil } - -// processTemplateData renders and processes template data for the InstallPipeline. -// Unlike the base pipeline, this method does not handle blueprint data extraction -// as blueprint loading is handled separately in the Execute method. -func (p *InstallPipeline) processTemplateData(templateData map[string][]byte) (map[string]any, error) { - if p.templateRenderer == nil || len(templateData) == 0 { - return nil, nil - } - - renderedData := make(map[string]any) - if err := p.templateRenderer.Process(templateData, renderedData); err != nil { - return nil, fmt.Errorf("failed to process template data: %w", err) - } - return renderedData, nil -} diff --git a/pkg/pipelines/install_test.go b/pkg/pipelines/install_test.go index 5f69d8b6c..2887e2d65 100644 --- a/pkg/pipelines/install_test.go +++ b/pkg/pipelines/install_test.go @@ -745,158 +745,3 @@ func TestInstallPipeline_Execute(t *testing.T) { } }) } - -// ============================================================================= -// processTemplateData Tests -// ============================================================================= - -func TestInstallPipeline_processTemplateData(t *testing.T) { - setup := func(t *testing.T, opts ...*SetupOptions) (*InstallPipeline, *InstallMocks) { - t.Helper() - pipeline := NewInstallPipeline() - mocks := setupInstallMocks(t, opts...) - return pipeline, mocks - } - - t.Run("ProcessesTemplateDataSuccessfully", func(t *testing.T) { - // Given a pipeline with template renderer and template data - pipeline, _ := setup(t) - mockTemplateRenderer := &MockTemplate{ - ProcessFunc: func(templateData map[string][]byte, renderedData map[string]any) error { - renderedData["test"] = "processed" - return nil - }, - } - pipeline.templateRenderer = mockTemplateRenderer - - templateData := map[string][]byte{ - "test.jsonnet": []byte(`{"key": "value"}`), - } - - // When processTemplateData is called - result, err := pipeline.processTemplateData(templateData) - - // Then no error should be returned - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - // And template processing should be called - if !mockTemplateRenderer.ProcessCalled { - t.Error("Expected template processing to be called") - } - - // And result should contain processed data - if result["test"] != "processed" { - t.Errorf("Expected processed data, got %v", result) - } - }) - - t.Run("ReturnsErrorWhenTemplateProcessingFails", func(t *testing.T) { - // Given a pipeline with failing template renderer - pipeline, _ := setup(t) - mockTemplateRenderer := &MockTemplate{ - ProcessFunc: func(templateData map[string][]byte, renderedData map[string]any) error { - return fmt.Errorf("template processing failed") - }, - } - pipeline.templateRenderer = mockTemplateRenderer - - templateData := map[string][]byte{ - "test.jsonnet": []byte(`{"key": "value"}`), - } - - // When processTemplateData is called - result, err := pipeline.processTemplateData(templateData) - - // Then an error should be returned - if err == nil { - t.Fatal("Expected error, got nil") - } - if err.Error() != "failed to process template data: template processing failed" { - t.Errorf("Expected template processing error, got %q", err.Error()) - } - - // And result should be nil - if result != nil { - t.Errorf("Expected nil result, got %v", result) - } - }) - - t.Run("ReturnsNilWhenNoTemplateRenderer", func(t *testing.T) { - // Given a pipeline with no template renderer - pipeline, _ := setup(t) - pipeline.templateRenderer = nil - - templateData := map[string][]byte{ - "test.jsonnet": []byte(`{"key": "value"}`), - } - - // When processTemplateData is called - result, err := pipeline.processTemplateData(templateData) - - // Then no error should be returned - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - // And result should be nil - if result != nil { - t.Errorf("Expected nil result, got %v", result) - } - }) - - t.Run("ReturnsNilWhenNoTemplateData", func(t *testing.T) { - // Given a pipeline with template renderer but no template data - pipeline, _ := setup(t) - mockTemplateRenderer := &MockTemplate{} - pipeline.templateRenderer = mockTemplateRenderer - - templateData := make(map[string][]byte) - - // When processTemplateData is called - result, err := pipeline.processTemplateData(templateData) - - // Then no error should be returned - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - // And result should be nil - if result != nil { - t.Errorf("Expected nil result, got %v", result) - } - - // And template processing should not be called - if mockTemplateRenderer.ProcessCalled { - t.Error("Expected template processing to not be called") - } - }) - - t.Run("ReturnsNilWhenEmptyTemplateData", func(t *testing.T) { - // Given a pipeline with template renderer but empty template data - pipeline, _ := setup(t) - mockTemplateRenderer := &MockTemplate{} - pipeline.templateRenderer = mockTemplateRenderer - - templateData := map[string][]byte{} - - // When processTemplateData is called - result, err := pipeline.processTemplateData(templateData) - - // Then no error should be returned - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - // And result should be nil - if result != nil { - t.Errorf("Expected nil result, got %v", result) - } - - // And template processing should not be called - if mockTemplateRenderer.ProcessCalled { - t.Error("Expected template processing to not be called") - } - }) -} diff --git a/pkg/pipelines/pipeline.go b/pkg/pipelines/pipeline.go index c11219695..0b3521df5 100644 --- a/pkg/pipelines/pipeline.go +++ b/pkg/pipelines/pipeline.go @@ -866,8 +866,9 @@ func (p *BasePipeline) processTemplateData(templateData map[string][]byte) (map[ return renderedData, nil } -// loadBlueprintFromTemplate loads blueprint data from rendered template data. If the "blueprint" key exists -// in renderedData and is a map, attempts to parse OCI artifact info from the context's "blueprint" value. +// loadBlueprintFromTemplate loads blueprint data from rendered template data. +// If the "blueprint" key exists in renderedData and is a map, attempts to parse OCI artifact info +// from the context's "blueprint" value or falls back to the default blueprint URL if artifactBuilder is set. // Delegates loading to blueprintHandler.LoadData with the parsed blueprint map and optional OCI info. func (p *BasePipeline) loadBlueprintFromTemplate(ctx context.Context, renderedData map[string]any) error { if blueprintData, exists := renderedData["blueprint"]; exists { @@ -893,6 +894,13 @@ func (p *BasePipeline) loadBlueprintFromTemplate(ctx context.Context, renderedDa return err } } + } else if p.artifactBuilder != nil { + effectiveBlueprintURL := constants.GetEffectiveBlueprintURL() + var err error + ociInfo, err = bundler.ParseOCIReference(effectiveBlueprintURL) + if err != nil { + return fmt.Errorf("failed to parse default blueprint reference: %w", err) + } } blueprintHandler := p.withBlueprintHandler()