Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions cmd/build_id.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package cmd

import (
"context"
"fmt"

"github.com/spf13/cobra"
"github.com/windsorcli/cli/pkg/context"
"github.com/windsorcli/cli/pkg/di"
"github.com/windsorcli/cli/pkg/pipelines"
)

var buildIdNewFlag bool
Expand All @@ -27,26 +26,28 @@ Examples:
BUILD_ID=$(windsor build-id --new) && docker build -t myapp:$BUILD_ID .`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
// Get shared dependency injector from context
injector := cmd.Context().Value(injectorKey).(di.Injector)

// Set up the build ID pipeline
buildIDPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "buildIDPipeline")
execCtx := &context.ExecutionContext{
Injector: injector,
}

execCtx, err := context.NewContext(execCtx)
if err != nil {
return fmt.Errorf("failed to set up build ID pipeline: %w", err)
return fmt.Errorf("failed to initialize context: %w", err)
}

// Create execution context with flags
ctx := cmd.Context()
var buildID string
if buildIdNewFlag {
ctx = context.WithValue(ctx, "new", true)
buildID, err = execCtx.GenerateBuildID()
} else {
buildID, err = execCtx.GetBuildID()
}

// Execute the build ID pipeline
if err := buildIDPipeline.Execute(ctx); err != nil {
return fmt.Errorf("failed to execute build ID pipeline: %w", err)
if err != nil {
return fmt.Errorf("failed to manage build ID: %w", err)
}

fmt.Printf("%s\n", buildID)
return nil
},
}
Expand Down
12 changes: 10 additions & 2 deletions cmd/build_id_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ func TestBuildIDCmd(t *testing.T) {
t.Run("Success", func(t *testing.T) {
// Given proper output capture and mock setup
_, stderr := setup(t)
setupMocks(t)
mocks := setupMocks(t)

// Set up command context with injector
ctx := context.WithValue(context.Background(), injectorKey, mocks.Injector)
rootCmd.SetContext(ctx)

rootCmd.SetArgs([]string{"build-id"})

Expand All @@ -42,7 +46,11 @@ func TestBuildIDCmd(t *testing.T) {
t.Run("SuccessWithNewFlag", func(t *testing.T) {
// Given proper output capture and mock setup
_, stderr := setup(t)
setupMocks(t)
mocks := setupMocks(t)

// Set up command context with injector
ctx := context.WithValue(context.Background(), injectorKey, mocks.Injector)
rootCmd.SetContext(ctx)

rootCmd.SetArgs([]string{"build-id", "--new"})

Expand Down
39 changes: 9 additions & 30 deletions pkg/composer/blueprint/blueprint_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import (
_ "embed"

"github.com/goccy/go-yaml"
"github.com/windsorcli/cli/pkg/context/config"
"github.com/windsorcli/cli/pkg/composer/artifact"
"github.com/windsorcli/cli/pkg/constants"
"github.com/windsorcli/cli/pkg/context/config"
"github.com/windsorcli/cli/pkg/context/shell"
"github.com/windsorcli/cli/pkg/di"
"github.com/windsorcli/cli/pkg/provisioner/kubernetes"
"github.com/windsorcli/cli/pkg/composer/artifact"
"github.com/windsorcli/cli/pkg/context/shell"

"github.com/briandowns/spinner"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
Expand Down Expand Up @@ -1683,9 +1683,12 @@ func (b *BaseBlueprintHandler) applyValuesConfigMaps() error {
mergedCommonValues["REGISTRY_URL"] = registryURL
mergedCommonValues["LOCAL_VOLUME_PATH"] = localVolumePath

buildID, err := b.getBuildIDFromFile()
if err != nil {
return fmt.Errorf("failed to get build ID: %w", err)
buildID := os.Getenv("BUILD_ID")
if buildID == "" && b.projectRoot != "" {
buildIDPath := filepath.Join(b.projectRoot, ".windsor", ".build-id")
if data, err := b.shims.ReadFile(buildIDPath); err == nil {
buildID = strings.TrimSpace(string(data))
}
}
if buildID != "" {
mergedCommonValues["BUILD_ID"] = buildID
Expand Down Expand Up @@ -1839,30 +1842,6 @@ func (b *BaseBlueprintHandler) flattenValuesToConfigMap(values map[string]any, p
return nil
}

// getBuildIDFromFile returns the build ID string from the .windsor/.build-id file in the project root directory.
// It locates the project root using the shell interface, constructs the build ID file path, and attempts to read the file.
// If the file does not exist, it returns an empty string and no error. If the file exists, it reads and trims whitespace from the contents.
// Returns the build ID string or an error if the file cannot be read.
func (b *BaseBlueprintHandler) getBuildIDFromFile() (string, error) {
projectRoot, err := b.shell.GetProjectRoot()
if err != nil {
return "", fmt.Errorf("failed to get project root: %w", err)
}

buildIDPath := filepath.Join(projectRoot, ".windsor", ".build-id")

if _, err := b.shims.Stat(buildIDPath); os.IsNotExist(err) {
return "", nil
}

data, err := b.shims.ReadFile(buildIDPath)
if err != nil {
return "", fmt.Errorf("failed to read build ID file: %w", err)
}

return strings.TrimSpace(string(data)), nil
}

// deepMergeMaps returns a new map from a deep merge of base and overlay maps.
// Overlay values take precedence; nested maps merge recursively. Non-map overlay values replace base values.
func (b *BaseBlueprintHandler) deepMergeMaps(base, overlay map[string]any) map[string]any {
Expand Down
26 changes: 11 additions & 15 deletions pkg/composer/blueprint/blueprint_handler_private_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,9 @@ func TestBaseBlueprintHandler_applyValuesConfigMaps(t *testing.T) {
t.Run("SuccessWithKustomizationSubstitutions", func(t *testing.T) {
handler := setup(t)

configRoot := "/test/config"
projectRoot := "/test/project"
tmpDir := t.TempDir()
configRoot := filepath.Join(tmpDir, "config")
projectRoot := filepath.Join(tmpDir, "project")

mockConfigHandler := handler.configHandler.(*config.MockConfigHandler)
mockConfigHandler.GetConfigRootFunc = func() (string, error) {
Expand Down Expand Up @@ -1249,26 +1250,21 @@ contexts:
t.Fatalf("failed to initialize handler: %v", err)
}

// Set up build ID by mocking the file system
// Set up build ID by ensuring project root is writable and creating the build ID file
testBuildID := "build-1234567890"
projectRoot, err := mocks.Shell.GetProjectRoot()
if err != nil {
t.Fatalf("failed to get project root: %v", err)
}
buildIDPath := filepath.Join(projectRoot, ".windsor", ".build-id")
buildIDDir := filepath.Join(projectRoot, ".windsor")
buildIDPath := filepath.Join(buildIDDir, ".build-id")

// Mock the file system to return our test build ID
handler.shims.Stat = func(path string) (os.FileInfo, error) {
if path == buildIDPath {
return mockFileInfo{name: ".build-id", isDir: false}, nil
}
return nil, os.ErrNotExist
// Ensure the directory exists and create the build ID file
if err := os.MkdirAll(buildIDDir, 0755); err != nil {
t.Fatalf("failed to create build ID directory: %v", err)
}
handler.shims.ReadFile = func(path string) ([]byte, error) {
if path == buildIDPath {
return []byte(testBuildID), nil
}
return []byte{}, nil
if err := os.WriteFile(buildIDPath, []byte(testBuildID), 0644); err != nil {
t.Fatalf("failed to create build ID file: %v", err)
}

// Mock the kubernetes manager to capture the ConfigMap data
Expand Down
14 changes: 9 additions & 5 deletions pkg/composer/blueprint/blueprint_handler_public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import (
sourcev1 "github.com/fluxcd/source-controller/api/v1"
"github.com/goccy/go-yaml"
blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1"
"github.com/windsorcli/cli/pkg/context/config"
"github.com/windsorcli/cli/pkg/composer/artifact"
"github.com/windsorcli/cli/pkg/constants"
"github.com/windsorcli/cli/pkg/context/config"
"github.com/windsorcli/cli/pkg/context/shell"
"github.com/windsorcli/cli/pkg/di"
"github.com/windsorcli/cli/pkg/provisioner/kubernetes"
"github.com/windsorcli/cli/pkg/composer/artifact"
"github.com/windsorcli/cli/pkg/context/shell"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand Down Expand Up @@ -297,6 +297,9 @@ func setupShims(t *testing.T) *Shims {
func setupMocks(t *testing.T, opts ...*SetupOptions) *Mocks {
t.Helper()

// Unset BUILD_ID to ensure tests aren't affected by environment
os.Unsetenv("BUILD_ID")

// Create temporary directory for test
tmpDir, err := os.MkdirTemp("", "blueprint-test-*")
if err != nil {
Expand Down Expand Up @@ -357,9 +360,9 @@ func setupMocks(t *testing.T, opts ...*SetupOptions) *Mocks {

// Create mock shell and kubernetes manager
mockShell := shell.NewMockShell()
// Set default GetProjectRoot implementation
// Set default GetProjectRoot implementation to use writable temp directory
mockShell.GetProjectRootFunc = func() (string, error) {
return "/mock/project", nil
return tmpDir, nil
}

mockKubernetesManager := kubernetes.NewMockKubernetesManager(nil)
Expand Down Expand Up @@ -444,6 +447,7 @@ contexts:
t.Cleanup(func() {
os.Unsetenv("WINDSOR_PROJECT_ROOT")
os.Unsetenv("WINDSOR_CONTEXT")
os.Unsetenv("BUILD_ID")
os.Chdir(tmpDir)
})

Expand Down
Loading
Loading