From 3d55244e1f2947d5d24984bfbe5c7e7f69d2d3bd Mon Sep 17 00:00:00 2001 From: Drew Minnear Date: Wed, 23 Jul 2025 09:48:47 -0400 Subject: [PATCH 1/4] add cursor rule to use "make ci" for validating source code changes --- src/.cursor/rules/make-ci-verification.mdc | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/.cursor/rules/make-ci-verification.mdc diff --git a/src/.cursor/rules/make-ci-verification.mdc b/src/.cursor/rules/make-ci-verification.mdc new file mode 100644 index 0000000..61e8938 --- /dev/null +++ b/src/.cursor/rules/make-ci-verification.mdc @@ -0,0 +1,8 @@ +--- +description: lint/build/test any changes to source code +alwaysApply: true +--- + +- Any changs to the the Go CLI source code need to be vetted with `make ci` which will lint, build, and test the CLI + +@Makefile From ef6314a47631a5ed680d239c06d7373959fe91db Mon Sep 17 00:00:00 2001 From: Drew Minnear Date: Wed, 23 Jul 2025 11:53:50 -0400 Subject: [PATCH 2/4] replace static USE_SECRETS with dynamic global.secretLoader.disabled configuration **Core Changes:** - **New architecture**: Replaced hardcoded `USE_SECRETS` modifications with dynamic configuration reading from `values-global.yaml` - **Single source of truth**: Added `global.secretLoader.disabled` field that controls secrets behavior (true = disabled, false = enabled) - **Makefile restructure**: Now generates simple include-based `Makefile` + always copies `Makefile-pattern` with dynamic logic - **Runtime configuration**: `Makefile-pattern` uses `yq` to read `global.secretLoader.disabled` and set `USE_SECRETS` accordingly **Implementation Details:** - Updated `ProcessGlobalValues()` to accept `withSecrets` parameter and set `global.secretLoader.disabled` field - Modified Makefile generation to create include-based structure instead of copying/modifying single file - Removed static file modifications from `pattern.sh` and `HandleSecretsSetup()` - Updated all unit and integration tests to validate new `global.secretLoader.disabled` behavior - Added Cursor acknowledgment to README and updated documentation **Benefits:** - Cleaner architecture with configuration-driven behavior - Users can customize Makefiles while retaining common functionality - Eliminates need for static file modifications - Single configuration field controls all secrets-related behavior The change maintains full backward compatibility while providing a more robust and maintainable architecture. --- Makefile-pattern | 12 +++- README.md | 36 ++++++----- pattern.sh | 3 - src/.cursor/rules/make-ci-verification.mdc | 34 ++++++++++- src/cmd/init.go | 25 ++++---- src/internal/fileutils/fileutils.go | 21 +++++-- src/internal/pattern/pattern.go | 7 ++- src/internal/pattern/pattern_test.go | 58 ++++++++++++++++-- src/internal/types/global.go | 17 +++++- test/expected_values_global.yaml | 2 + test/expected_values_global_custom.yaml | 2 + test/expected_values_global_with_secrets.yaml | 9 +++ test/integration_test.sh | 59 +++++++++---------- 13 files changed, 207 insertions(+), 78 deletions(-) create mode 100644 test/expected_values_global_with_secrets.yaml diff --git a/Makefile-pattern b/Makefile-pattern index f9483bf..fdb2d95 100644 --- a/Makefile-pattern +++ b/Makefile-pattern @@ -69,8 +69,16 @@ help: ## This help message @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^(\s|[a-zA-Z_0-9-])+:.*?##/ { printf " \033[36m%-35s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) .PHONY: install -USE_SECRETS ?= false -install: operator-deploy ## Install the pattern (USE_SECRETS=true to load secrets) +# Dynamically read secretLoader.disabled from values-global.yaml to determine USE_SECRETS +# If secretLoader.disabled is true, USE_SECRETS should be false (secrets disabled) +# If secretLoader.disabled is false, USE_SECRETS should be true (secrets enabled) +USE_SECRETS := $(shell if [ -f values-global.yaml ]; then \ + DISABLED=$$(yq -r '.global.secretLoader.disabled // true' values-global.yaml 2>/dev/null || echo "true"); \ + if [ "$$DISABLED" = "false" ]; then echo "true"; else echo "false"; fi; \ +else \ + echo "false"; \ +fi) +install: operator-deploy ## Install the pattern (USE_SECRETS determined by global.secretLoader.disabled in values-global.yaml) @if [ "$(USE_SECRETS)" != "false" ]; then \ $(MAKE) post-install; \ fi diff --git a/README.md b/README.md index 8877215..c87a183 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Quay Repository](https://img.shields.io/badge/Quay.io-patternizer-blue?logo=quay)](https://quay.io/repository/dminnear/patternizer) [![CI Pipeline](https://github.com/dminnear-rh/patternizer/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/dminnear-rh/patternizer/actions/workflows/ci.yaml) +> **Note:** This tool was developed with assistance from [Cursor](https://cursor.sh), an AI-powered code editor. + **Patternizer** is a CLI tool and container utility designed to bootstrap Validated Pattern repositories. It automatically generates the necessary `values-global.yaml` and `values-.yaml` files by inspecting Git repositories, discovering Helm charts, and applying sensible defaults. The tool provides both a standalone CLI and containerized execution for maximum flexibility and consistency across environments. @@ -61,17 +63,18 @@ patternizer init help The `patternizer init` command generates: -- `values-global.yaml` - Global pattern configuration +- `values-global.yaml` - Global pattern configuration with `global.secretLoader.disabled: true` - `values-.yaml` - Cluster group-specific configuration - `pattern.sh` - Utility script for pattern operations -- `Makefile` - Self-contained Makefile with inlined scripts for pattern management +- `Makefile` - Simple include-based Makefile that includes `Makefile-pattern` +- `Makefile-pattern` - Contains all pattern targets and dynamically reads secrets config from `values-global.yaml` When using `--with-secrets`: - `values-secret.yaml.template` - Template for secrets configuration -- Modified `pattern.sh` with `USE_SECRETS=true` as default -- Modified `Makefile` with `USE_SECRETS=true` as default +- `values-global.yaml` with `global.secretLoader.disabled: false` (enables secrets) +- Additional applications (vault, golang-external-secrets) in cluster group values -Both `pattern.sh` and `Makefile` provide equivalent functionality for pattern installation and management, with the Makefile offering a more traditional build tool approach. +The secrets loading behavior is controlled entirely by the `global.secretLoader.disabled` field in `values-global.yaml`. --- @@ -109,7 +112,7 @@ podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer init --with-secret 3. **Review generated files:** ```bash - ls -la values-*.yaml pattern.sh + ls -la values-*.yaml pattern.sh Makefile* ``` 4. **Install the pattern:** @@ -217,6 +220,10 @@ The project includes comprehensive unit tests across multiple packages: - Adds new applications while maintaining existing ones - Maintains custom fields within applications and subscriptions - `TestProcessGlobalValuesWithNewFile()` - New file creation with proper defaults +- `TestProcessGlobalValuesWithSecrets()` - Validates secrets configuration: + - Tests `ProcessGlobalValues` with `withSecrets=true` + - Verifies `global.secretLoader.disabled: false` is set correctly + - Ensures secrets-enabled configuration is properly generated ### Integration Tests @@ -225,29 +232,30 @@ The integration test suite (`test/integration_test.sh`) validates the complete C **Test 1: Basic Initialization (Without Secrets)** - Clones the [trivial-pattern](https://github.com/dminnear-rh/trivial-pattern) repository - Runs `patternizer init` and validates generated files -- Verifies `values-global.yaml` and `values-prod.yaml` content using YAML normalization -- Ensures `pattern.sh` is created with `USE_SECRETS=false` and executable permissions -- Validates `Makefile` is created with `USE_SECRETS=false` and contains proper functionality +- Verifies `values-global.yaml` contains `global.secretLoader.disabled: true` +- Validates `values-prod.yaml` content using YAML normalization +- Ensures `pattern.sh` is created and executable +- Validates `Makefile` (include-based) and `Makefile-pattern` are created **Test 2: Initialization with Secrets** - Tests `patternizer init --with-secrets` functionality +- Verifies `values-global.yaml` contains `global.secretLoader.disabled: false` - Validates secrets-specific applications (vault, golang-external-secrets) are added - Verifies additional namespaces and `values-secret.yaml.template` are created -- Ensures `pattern.sh` is configured with `USE_SECRETS=true` -- Validates `Makefile` is configured with `USE_SECRETS=true` and contains secrets support +- Ensures `pattern.sh` and both Makefile files are properly generated **Test 3: Custom Pattern and Cluster Group Names** - Tests field preservation and intelligent merging of existing configurations - Pre-populates custom `values-global.yaml` with renamed pattern and cluster group - Verifies custom names are preserved while adding missing default configurations - Validates custom cluster group file generation (e.g., `values-renamed-cluster-group.yaml`) -- Ensures both `pattern.sh` and `Makefile` are configured correctly for the custom setup +- Ensures `global.secretLoader.disabled: false` is set correctly with `--with-secrets` **Test 4: Sequential Execution** - Tests running `patternizer init` followed by `patternizer init --with-secrets` - Validates that the second command properly upgrades the configuration -- Ensures final state matches direct `--with-secrets` execution -- Verifies both `pattern.sh` and `Makefile` are updated correctly in the sequential workflow +- Ensures `global.secretLoader.disabled` transitions from `true` to `false` +- Verifies final state matches direct `--with-secrets` execution Run integration tests locally: ```bash diff --git a/pattern.sh b/pattern.sh index be6b002..ff6b2bb 100755 --- a/pattern.sh +++ b/pattern.sh @@ -78,8 +78,6 @@ else PKI_HOST_MOUNT_ARGS="" fi -: "${USE_SECRETS:=false}" - # Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory # $HOME is mounted as itself for any files that are referenced with absolute paths # $HOME is mounted to /root because the UID in the container is 0 and that's where SSH looks for credentials @@ -105,7 +103,6 @@ podman run -it --rm --pull=newer \ -e K8S_AUTH_USERNAME \ -e K8S_AUTH_PASSWORD \ -e K8S_AUTH_TOKEN \ - -e USE_SECRETS="$USE_SECRETS" \ ${PKI_HOST_MOUNT_ARGS} \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ diff --git a/src/.cursor/rules/make-ci-verification.mdc b/src/.cursor/rules/make-ci-verification.mdc index 61e8938..076e24b 100644 --- a/src/.cursor/rules/make-ci-verification.mdc +++ b/src/.cursor/rules/make-ci-verification.mdc @@ -3,6 +3,38 @@ description: lint/build/test any changes to source code alwaysApply: true --- -- Any changs to the the Go CLI source code need to be vetted with `make ci` which will lint, build, and test the CLI +## Go Source Code Testing Rule + +**ALWAYS use `make ci` for testing Go code changes - NEVER run manual go test commands** + +### When to Apply This Rule: +- Any changes to Go source code in the `src/` directory +- When you want to verify that changes work correctly +- Before proposing code changes to the user +- When debugging build/test issues + +### What to Use: +- ✅ **CORRECT**: `make ci` - This runs the complete CI pipeline locally (lint, build, test) +- ❌ **WRONG**: `cd src && go test ./... -v` - Manual go test commands +- ❌ **WRONG**: `go build`, `go test`, etc. - Direct go commands +- ❌ **WRONG**: `make test-unit` or other individual targets when you want full verification + +### Why This Rule Exists: +- `make ci` ensures consistency with the actual CI pipeline +- It runs linting, formatting checks, building, AND testing in the correct order +- It catches issues that manual testing might miss +- It's the same command developers use locally + +### The Command to Use: +```bash +make ci +``` + +This will: +1. Run all linting checks (`make lint`) +2. Build the binary (`make build`) +3. Run unit tests (`make test-unit`) +4. Run integration tests (`make test-integration`) +5. Generate coverage reports @Makefile diff --git a/src/cmd/init.go b/src/cmd/init.go index 8328db9..60bac3f 100644 --- a/src/cmd/init.go +++ b/src/cmd/init.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "os" "path/filepath" "github.com/dminnear-rh/patternizer/internal/fileutils" @@ -24,7 +25,7 @@ func runInit(withSecrets bool) error { } // Process values-global.yaml - actualPatternName, clusterGroupName, err := pattern.ProcessGlobalValues(patternName, repoRoot) + actualPatternName, clusterGroupName, err := pattern.ProcessGlobalValues(patternName, repoRoot, withSecrets) if err != nil { return fmt.Errorf("error processing global values: %w", err) } @@ -46,21 +47,19 @@ func runInit(withSecrets bool) error { return fmt.Errorf("error copying pattern.sh: %w", err) } - // Set USE_SECRETS in pattern.sh based on the flag - if err := fileutils.ModifyPatternShScript(patternShDst, withSecrets); err != nil { - return fmt.Errorf("error modifying pattern.sh: %w", err) + // Always copy Makefile-pattern to the pattern repo + makefilePatternSrc := filepath.Join(resourcesDir, "Makefile-pattern") + makefilePatternDst := filepath.Join(repoRoot, "Makefile-pattern") + if err := fileutils.CopyFile(makefilePatternSrc, makefilePatternDst); err != nil { + return fmt.Errorf("error copying Makefile-pattern: %w", err) } - // Copy and modify Makefile - makefileSrc := filepath.Join(resourcesDir, "Makefile-pattern") + // Create a simple Makefile that includes Makefile-pattern (only if it doesn't exist) makefileDst := filepath.Join(repoRoot, "Makefile") - if err := fileutils.CopyFile(makefileSrc, makefileDst); err != nil { - return fmt.Errorf("error copying Makefile: %w", err) - } - - // Set USE_SECRETS in Makefile based on the flag - if err := fileutils.ModifyMakefileScript(makefileDst, withSecrets); err != nil { - return fmt.Errorf("error modifying Makefile: %w", err) + if _, err := os.Stat(makefileDst); os.IsNotExist(err) { + if err := fileutils.CreateIncludeMakefile(makefileDst); err != nil { + return fmt.Errorf("error creating Makefile: %w", err) + } } // Handle secrets setup if requested diff --git a/src/internal/fileutils/fileutils.go b/src/internal/fileutils/fileutils.go index cdaddea..4793d7d 100644 --- a/src/internal/fileutils/fileutils.go +++ b/src/internal/fileutils/fileutils.go @@ -144,8 +144,7 @@ func ModifyMakefileScript(makefilePath string, useSecrets bool) error { return nil } -// HandleSecretsSetup handles the setup for secrets usage by copying the secrets template -// and modifying the pattern.sh script. +// HandleSecretsSetup handles the setup for secrets usage by copying the secrets template. func HandleSecretsSetup(resourcesDir, repoRoot string) (err error) { // Copy the values-secret.yaml.template file to the pattern root secretsTemplateSrc := filepath.Join(resourcesDir, "values-secret.yaml.template") @@ -155,10 +154,20 @@ func HandleSecretsSetup(resourcesDir, repoRoot string) (err error) { return fmt.Errorf("error copying secrets template: %w", err) } - // Modify the pattern.sh script to set USE_SECRETS=true - patternShPath := filepath.Join(repoRoot, "pattern.sh") - if err = ModifyPatternShScript(patternShPath, true); err != nil { - return fmt.Errorf("error modifying pattern.sh for secrets: %w", err) + return nil +} + +// CreateIncludeMakefile creates a simple Makefile that includes Makefile-pattern. +func CreateIncludeMakefile(makefilePath string) error { + content := `# Generated by patternizer +# This Makefile includes the common pattern targets from Makefile-pattern +# You can add custom targets above or below the include line + +include Makefile-pattern +` + + if err := os.WriteFile(makefilePath, []byte(content), 0o644); err != nil { + return fmt.Errorf("failed to create Makefile: %w", err) } return nil diff --git a/src/internal/pattern/pattern.go b/src/internal/pattern/pattern.go index 0ff2c13..b1711b6 100644 --- a/src/internal/pattern/pattern.go +++ b/src/internal/pattern/pattern.go @@ -73,7 +73,7 @@ func extractPatternNameFromURL(url string) (string, error) { // ProcessGlobalValues processes the global values YAML file. // It returns the pattern name and cluster group name that should be used (from the file if they exist, or the detected/default names). -func ProcessGlobalValues(patternName, repoRoot string) (actualPatternName, clusterGroupName string, err error) { +func ProcessGlobalValues(patternName, repoRoot string, withSecrets bool) (actualPatternName, clusterGroupName string, err error) { globalValuesPath := filepath.Join(repoRoot, "values-global.yaml") values := types.NewDefaultValuesGlobal() @@ -95,6 +95,11 @@ func ProcessGlobalValues(patternName, repoRoot string) (actualPatternName, clust values.Global.Pattern = patternName } + // Set secretLoader.disabled based on withSecrets flag + // If withSecrets is true, we want secretLoader to be enabled (disabled = false) + // If withSecrets is false, we want secretLoader to be disabled (disabled = true) + values.Global.SecretLoader.Disabled = !withSecrets + // Write back the merged values finalYamlBytes, err := yaml.Marshal(values) if err != nil { diff --git a/src/internal/pattern/pattern_test.go b/src/internal/pattern/pattern_test.go index 09aa6a0..7f36f9b 100644 --- a/src/internal/pattern/pattern_test.go +++ b/src/internal/pattern/pattern_test.go @@ -140,8 +140,8 @@ func TestProcessGlobalValuesPreservesFields(t *testing.T) { t.Fatalf("Failed to write initial values file: %v", err) } - // Process the values - actualPatternName, clusterGroupName, err := ProcessGlobalValues("new-pattern", tempDir) + // Process the values (test without secrets) + actualPatternName, clusterGroupName, err := ProcessGlobalValues("new-pattern", tempDir, false) if err != nil { t.Fatalf("ProcessGlobalValues failed: %v", err) } @@ -343,8 +343,8 @@ func TestProcessGlobalValuesWithNewFile(t *testing.T) { } defer os.RemoveAll(tempDir) - // Process values without existing file - actualPatternName, clusterGroupName, err := ProcessGlobalValues("test-pattern", tempDir) + // Process values without existing file (test without secrets) + actualPatternName, clusterGroupName, err := ProcessGlobalValues("test-pattern", tempDir, false) if err != nil { t.Fatalf("ProcessGlobalValues failed: %v", err) } @@ -386,6 +386,56 @@ func TestProcessGlobalValuesWithNewFile(t *testing.T) { if values.Main.MultiSourceConfig.ClusterGroupChartVersion != "0.9.*" { t.Errorf("ClusterGroupChartVersion = %s, expected '0.9.*'", values.Main.MultiSourceConfig.ClusterGroupChartVersion) } + if !values.Global.SecretLoader.Disabled { + t.Error("SecretLoader.Disabled should be true when withSecrets=false") + } +} + +// TestProcessGlobalValuesWithSecrets tests ProcessGlobalValues with withSecrets=true. +func TestProcessGlobalValuesWithSecrets(t *testing.T) { + tempDir, err := os.MkdirTemp("", "pattern-test-*") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + // Process values without existing file (test with secrets) + actualPatternName, clusterGroupName, err := ProcessGlobalValues("test-pattern", tempDir, true) + if err != nil { + t.Fatalf("ProcessGlobalValues failed: %v", err) + } + + // Verify return values + if actualPatternName != "test-pattern" { + t.Errorf("Expected pattern name 'test-pattern', got '%s'", actualPatternName) + } + if clusterGroupName != "prod" { // Default cluster group name + t.Errorf("Expected cluster group name 'prod', got '%s'", clusterGroupName) + } + + // Verify file was created with defaults + valuesPath := filepath.Join(tempDir, "values-global.yaml") + if _, err := os.Stat(valuesPath); os.IsNotExist(err) { + t.Fatal("values-global.yaml was not created") + } + + // Read and verify content + data, err := os.ReadFile(valuesPath) + if err != nil { + t.Fatalf("Failed to read created file: %v", err) + } + + var values types.ValuesGlobal + if err := yaml.Unmarshal(data, &values); err != nil { + t.Fatalf("Failed to unmarshal created file: %v", err) + } + + if values.Global.Pattern != "test-pattern" { + t.Errorf("Global pattern = %s, expected 'test-pattern'", values.Global.Pattern) + } + if values.Global.SecretLoader.Disabled { + t.Error("SecretLoader.Disabled should be false when withSecrets=true") + } } // getNestedValue is a helper function to get nested values from a map using a path. diff --git a/src/internal/types/global.go b/src/internal/types/global.go index 93e168d..7d5f076 100644 --- a/src/internal/types/global.go +++ b/src/internal/types/global.go @@ -1,9 +1,16 @@ package types +// SecretLoader represents the 'secretLoader' section under 'global'. +type SecretLoader struct { + Disabled bool `yaml:"disabled"` + OtherFields map[string]interface{} `yaml:",inline"` +} + // Global represents the 'global' section of the YAML file. type Global struct { - Pattern string `yaml:"pattern"` - OtherFields map[string]interface{} `yaml:",inline"` + Pattern string `yaml:"pattern"` + SecretLoader SecretLoader `yaml:"secretLoader"` + OtherFields map[string]interface{} `yaml:",inline"` } // Main represents the 'main' section of the YAML file. @@ -30,7 +37,11 @@ type ValuesGlobal struct { // NewDefaultValuesGlobal creates a ValuesGlobal struct with all the default values. func NewDefaultValuesGlobal() *ValuesGlobal { return &ValuesGlobal{ - Global: Global{}, + Global: Global{ + SecretLoader: SecretLoader{ + Disabled: true, + }, + }, Main: Main{ ClusterGroupName: "prod", MultiSourceConfig: MultiSourceConfig{ diff --git a/test/expected_values_global.yaml b/test/expected_values_global.yaml index e97874e..75f5a1a 100644 --- a/test/expected_values_global.yaml +++ b/test/expected_values_global.yaml @@ -1,5 +1,7 @@ global: pattern: trivial-pattern + secretLoader: + disabled: true main: clusterGroupName: prod multiSourceConfig: diff --git a/test/expected_values_global_custom.yaml b/test/expected_values_global_custom.yaml index 5a8b638..90cadba 100644 --- a/test/expected_values_global_custom.yaml +++ b/test/expected_values_global_custom.yaml @@ -1,5 +1,7 @@ global: pattern: renamed-pattern + secretLoader: + disabled: false main: clusterGroupName: renamed-cluster-group multiSourceConfig: diff --git a/test/expected_values_global_with_secrets.yaml b/test/expected_values_global_with_secrets.yaml new file mode 100644 index 0000000..76167bc --- /dev/null +++ b/test/expected_values_global_with_secrets.yaml @@ -0,0 +1,9 @@ +global: + pattern: trivial-pattern + secretLoader: + disabled: false +main: + clusterGroupName: prod + multiSourceConfig: + enabled: true + clusterGroupChartVersion: 0.9.* diff --git a/test/integration_test.sh b/test/integration_test.sh index e84d285..97b2634 100755 --- a/test/integration_test.sh +++ b/test/integration_test.sh @@ -36,6 +36,7 @@ REPO_ROOT=$(pwd) # Set absolute paths to expected files EXPECTED_VALUES_GLOBAL="$REPO_ROOT/test/expected_values_global.yaml" +EXPECTED_VALUES_GLOBAL_WITH_SECRETS="$REPO_ROOT/test/expected_values_global_with_secrets.yaml" EXPECTED_VALUES_PROD="$REPO_ROOT/test/expected_values_prod.yaml" EXPECTED_VALUES_PROD_WITH_SECRETS="$REPO_ROOT/test/expected_values_prod_with_secrets.yaml" EXPECTED_VALUES_GLOBAL_CUSTOM="$REPO_ROOT/test/expected_values_global_custom.yaml" @@ -162,10 +163,7 @@ compare_yaml "$EXPECTED_VALUES_GLOBAL" "values-global.yaml" "values-global.yaml # Test 1.2: Check values-prod.yaml compare_yaml "$EXPECTED_VALUES_PROD" "values-prod.yaml" "values-prod.yaml content" -# Test 1.3: Check pattern.sh exists and has USE_SECRETS=false -check_file_content "pattern.sh" 'USE_SECRETS:=false' "pattern.sh contains USE_SECRETS=false" - -# Test 1.4: Verify pattern.sh is executable +# Test 1.3: Verify pattern.sh is executable if [ -x "pattern.sh" ]; then echo -e "${GREEN}PASS: pattern.sh is executable${NC}" else @@ -173,9 +171,11 @@ else exit 1 fi -# Test 1.5: Check Makefile exists and has USE_SECRETS=false +# Test 1.4: Check Makefile exists (simple include-based Makefile) check_file_exists "Makefile" "Makefile exists (init without secrets)" -check_file_content "Makefile" 'USE_SECRETS ?= false' "Makefile contains USE_SECRETS=false" + +# Test 1.5: Check Makefile-pattern exists (contains the actual targets) +check_file_exists "Makefile-pattern" "Makefile-pattern exists (init without secrets)" echo -e "${GREEN}=== Test 1: Basic initialization PASSED ===${NC}" @@ -194,16 +194,13 @@ PATTERNIZER_RESOURCES_DIR="$REPO_ROOT" "$PATTERNIZER_BINARY" init --with-secrets echo -e "${YELLOW}Running verification tests for secrets...${NC}" -# Test 2.1: Check values-global.yaml (should be same as without secrets) -compare_yaml "$EXPECTED_VALUES_GLOBAL" "values-global.yaml" "values-global.yaml content (with secrets)" +# Test 2.1: Check values-global.yaml (secretLoader.disabled should be false with secrets) +compare_yaml "$EXPECTED_VALUES_GLOBAL_WITH_SECRETS" "values-global.yaml" "values-global.yaml content (with secrets)" # Test 2.2: Check values-prod.yaml with secrets applications compare_yaml "$EXPECTED_VALUES_PROD_WITH_SECRETS" "values-prod.yaml" "values-prod.yaml content (with secrets)" -# Test 2.3: Check pattern.sh exists and has USE_SECRETS=true -check_file_content "pattern.sh" 'USE_SECRETS:=true' "pattern.sh contains USE_SECRETS=true" - -# Test 2.4: Verify pattern.sh is executable +# Test 2.3: Verify pattern.sh is executable if [ -x "pattern.sh" ]; then echo -e "${GREEN}PASS: pattern.sh is executable (with secrets)${NC}" else @@ -211,12 +208,14 @@ else exit 1 fi -# Test 2.5: Check values-secret.yaml.template exists +# Test 2.4: Check values-secret.yaml.template exists check_file_exists "values-secret.yaml.template" "values-secret.yaml.template file exists" -# Test 2.6: Check Makefile exists and has USE_SECRETS=true +# Test 2.5: Check Makefile exists (simple include-based Makefile) check_file_exists "Makefile" "Makefile exists (init with secrets)" -check_file_content "Makefile" 'USE_SECRETS ?= true' "Makefile contains USE_SECRETS=true" + +# Test 2.6: Check Makefile-pattern exists (contains the actual targets) +check_file_exists "Makefile-pattern" "Makefile-pattern exists (init with secrets)" echo -e "${GREEN}=== Test 2: Initialization with secrets PASSED ===${NC}" @@ -244,10 +243,7 @@ compare_yaml "$EXPECTED_VALUES_GLOBAL_CUSTOM" "values-global.yaml" "values-globa # Test 3.2: Check custom cluster group file is created with correct content compare_yaml "$EXPECTED_VALUES_RENAMED_CLUSTER_GROUP" "values-renamed-cluster-group.yaml" "values-renamed-cluster-group.yaml content" -# Test 3.3: Check pattern.sh exists and has USE_SECRETS=true -check_file_content "pattern.sh" 'USE_SECRETS:=true' "pattern.sh contains USE_SECRETS=true (custom names)" - -# Test 3.4: Verify pattern.sh is executable +# Test 3.3: Verify pattern.sh is executable if [ -x "pattern.sh" ]; then echo -e "${GREEN}PASS: pattern.sh is executable (custom names)${NC}" else @@ -255,12 +251,14 @@ else exit 1 fi -# Test 3.5: Check values-secret.yaml.template exists +# Test 3.4: Check values-secret.yaml.template exists check_file_exists "values-secret.yaml.template" "values-secret.yaml.template file exists (custom names)" -# Test 3.6: Check Makefile exists and has USE_SECRETS=true +# Test 3.5: Check Makefile exists (simple include-based Makefile) check_file_exists "Makefile" "Makefile exists (custom names with secrets)" -check_file_content "Makefile" 'USE_SECRETS ?= true' "Makefile contains USE_SECRETS=true (custom names)" + +# Test 3.6: Check Makefile-pattern exists (contains the actual targets) +check_file_exists "Makefile-pattern" "Makefile-pattern exists (custom names with secrets)" echo -e "${GREEN}=== Test 3: Custom pattern and cluster group names (with secrets) PASSED ===${NC}" @@ -289,16 +287,13 @@ PATTERNIZER_RESOURCES_DIR="$REPO_ROOT" "$PATTERNIZER_BINARY" init --with-secrets echo -e "${YELLOW}Running verification tests for sequential execution...${NC}" -# Test 4.1: Check values-global.yaml (should be same as basic case) -compare_yaml "$EXPECTED_VALUES_GLOBAL" "values-global.yaml" "values-global.yaml content (sequential)" +# Test 4.1: Check values-global.yaml (should have secretLoader.disabled=false after --with-secrets) +compare_yaml "$EXPECTED_VALUES_GLOBAL_WITH_SECRETS" "values-global.yaml" "values-global.yaml content (sequential)" # Test 4.2: Check values-prod.yaml matches the --with-secrets output compare_yaml "$EXPECTED_VALUES_PROD_WITH_SECRETS" "values-prod.yaml" "values-prod.yaml content (sequential, should match --with-secrets)" -# Test 4.3: Check pattern.sh exists and has USE_SECRETS=true -check_file_content "pattern.sh" 'USE_SECRETS:=true' "pattern.sh contains USE_SECRETS=true (sequential)" - -# Test 4.4: Verify pattern.sh is executable +# Test 4.3: Verify pattern.sh is executable if [ -x "pattern.sh" ]; then echo -e "${GREEN}PASS: pattern.sh is executable (sequential)${NC}" else @@ -306,12 +301,14 @@ else exit 1 fi -# Test 4.5: Check values-secret.yaml.template exists +# Test 4.4: Check values-secret.yaml.template exists check_file_exists "values-secret.yaml.template" "values-secret.yaml.template file exists (sequential)" -# Test 4.6: Check Makefile exists and has USE_SECRETS=true +# Test 4.5: Check Makefile exists (simple include-based Makefile) check_file_exists "Makefile" "Makefile exists (sequential execution)" -check_file_content "Makefile" 'USE_SECRETS ?= true' "Makefile contains USE_SECRETS=true (sequential)" + +# Test 4.6: Check Makefile-pattern exists (contains the actual targets) +check_file_exists "Makefile-pattern" "Makefile-pattern exists (sequential execution)" echo -e "${GREEN}=== Test 4: Sequential execution PASSED ===${NC}" From 5f92bee2610c8486838e76d30402fe52375a3133 Mon Sep 17 00:00:00 2001 From: Drew Minnear Date: Wed, 23 Jul 2025 12:15:59 -0400 Subject: [PATCH 3/4] remove unused functions and update USE_SECRETS to LOAD_SECRETS in Makefile --- Makefile-pattern | 12 ++-- src/internal/fileutils/fileutils.go | 99 ----------------------------- 2 files changed, 6 insertions(+), 105 deletions(-) diff --git a/Makefile-pattern b/Makefile-pattern index fdb2d95..2fb2f62 100644 --- a/Makefile-pattern +++ b/Makefile-pattern @@ -69,17 +69,17 @@ help: ## This help message @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^(\s|[a-zA-Z_0-9-])+:.*?##/ { printf " \033[36m%-35s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) .PHONY: install -# Dynamically read secretLoader.disabled from values-global.yaml to determine USE_SECRETS -# If secretLoader.disabled is true, USE_SECRETS should be false (secrets disabled) -# If secretLoader.disabled is false, USE_SECRETS should be true (secrets enabled) -USE_SECRETS := $(shell if [ -f values-global.yaml ]; then \ +# Dynamically read secretLoader.disabled from values-global.yaml to determine LOAD_SECRETS +# If secretLoader.disabled is true, LOAD_SECRETS should be false (secrets disabled) +# If secretLoader.disabled is false, LOAD_SECRETS should be true (secrets enabled) +LOAD_SECRETS := $(shell if [ -f values-global.yaml ]; then \ DISABLED=$$(yq -r '.global.secretLoader.disabled // true' values-global.yaml 2>/dev/null || echo "true"); \ if [ "$$DISABLED" = "false" ]; then echo "true"; else echo "false"; fi; \ else \ echo "false"; \ fi) -install: operator-deploy ## Install the pattern (USE_SECRETS determined by global.secretLoader.disabled in values-global.yaml) - @if [ "$(USE_SECRETS)" != "false" ]; then \ +install: operator-deploy ## Install the pattern (LOAD_SECRETS determined by global.secretLoader.disabled in values-global.yaml) + @if [ "$(LOAD_SECRETS)" != "false" ]; then \ $(MAKE) post-install; \ fi @echo "Installed" diff --git a/src/internal/fileutils/fileutils.go b/src/internal/fileutils/fileutils.go index 4793d7d..ba51940 100644 --- a/src/internal/fileutils/fileutils.go +++ b/src/internal/fileutils/fileutils.go @@ -1,13 +1,10 @@ package fileutils import ( - "bufio" "fmt" "io" "os" "path/filepath" - "regexp" - "strings" ) // CopyFile copies a file from src to dst. If dst already exists, it will be overwritten. @@ -48,102 +45,6 @@ func CopyFile(src, dst string) error { return nil } -// ModifyPatternShScript modifies the pattern.sh script to set USE_SECRETS to the desired value. -func ModifyPatternShScript(patternShPath string, useSecrets bool) error { - file, err := os.Open(patternShPath) - if err != nil { - return err - } - - var lines []string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - lines = append(lines, scanner.Text()) - } - file.Close() - - if err := scanner.Err(); err != nil { - return err - } - - // Regex to match the USE_SECRETS line - regex := regexp.MustCompile(`^\s*:\s*"\$\{USE_SECRETS:=(.+)\}"`) - - for i, line := range lines { - if matches := regex.FindStringSubmatch(line); matches != nil { - if useSecrets { - lines[i] = strings.Replace(line, matches[1], "true", 1) - } else { - lines[i] = strings.Replace(line, matches[1], "false", 1) - } - break - } - } - - output, err := os.Create(patternShPath) - if err != nil { - return err - } - defer output.Close() - - for _, line := range lines { - _, err := output.WriteString(line + "\n") - if err != nil { - return err - } - } - - return nil -} - -// ModifyMakefileScript modifies the Makefile to set USE_SECRETS to the desired value. -func ModifyMakefileScript(makefilePath string, useSecrets bool) error { - file, err := os.Open(makefilePath) - if err != nil { - return err - } - - var lines []string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - lines = append(lines, scanner.Text()) - } - file.Close() - - if err := scanner.Err(); err != nil { - return err - } - - // Regex to match the USE_SECRETS line in Makefile format - regex := regexp.MustCompile(`^USE_SECRETS\s*\?=\s*(.+)$`) - - for i, line := range lines { - if matches := regex.FindStringSubmatch(line); matches != nil { - if useSecrets { - lines[i] = "USE_SECRETS ?= true" - } else { - lines[i] = "USE_SECRETS ?= false" - } - break - } - } - - output, err := os.Create(makefilePath) - if err != nil { - return err - } - defer output.Close() - - for _, line := range lines { - _, err := output.WriteString(line + "\n") - if err != nil { - return err - } - } - - return nil -} - // HandleSecretsSetup handles the setup for secrets usage by copying the secrets template. func HandleSecretsSetup(resourcesDir, repoRoot string) (err error) { // Copy the values-secret.yaml.template file to the pattern root From e09ec4532a411c91ff27fc96fc91a36421a0a9c9 Mon Sep 17 00:00:00 2001 From: Drew Minnear Date: Wed, 23 Jul 2025 15:18:14 -0400 Subject: [PATCH 4/4] fix load secrets logic --- Makefile-pattern | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Makefile-pattern b/Makefile-pattern index 2fb2f62..9c41d4a 100644 --- a/Makefile-pattern +++ b/Makefile-pattern @@ -73,14 +73,24 @@ help: ## This help message # If secretLoader.disabled is true, LOAD_SECRETS should be false (secrets disabled) # If secretLoader.disabled is false, LOAD_SECRETS should be true (secrets enabled) LOAD_SECRETS := $(shell if [ -f values-global.yaml ]; then \ - DISABLED=$$(yq -r '.global.secretLoader.disabled // true' values-global.yaml 2>/dev/null || echo "true"); \ + YQ_OUTPUT=$$(yq -r '.global.secretLoader.disabled' values-global.yaml 2>/dev/null); \ + YQ_EXIT=$$?; \ + if [ $$YQ_EXIT -eq 0 ] && [ "$$YQ_OUTPUT" != "null" ]; then \ + DISABLED="$$YQ_OUTPUT"; \ + else \ + DISABLED="true"; \ + fi; \ if [ "$$DISABLED" = "false" ]; then echo "true"; else echo "false"; fi; \ else \ echo "false"; \ fi) install: operator-deploy ## Install the pattern (LOAD_SECRETS determined by global.secretLoader.disabled in values-global.yaml) + @echo "Secrets configuration: global.secretLoader.disabled=$$(yq -r '.global.secretLoader.disabled // true' values-global.yaml 2>/dev/null || echo 'true'), LOAD_SECRETS=$(LOAD_SECRETS)" @if [ "$(LOAD_SECRETS)" != "false" ]; then \ - $(MAKE) post-install; \ + echo "Loading secrets..."; \ + $(MAKE) load-secrets; \ + else \ + echo "Skipping secrets loading (disabled)"; \ fi @echo "Installed" @@ -163,11 +173,6 @@ validate-cluster: echo "OK";\ fi -.PHONY: post-install -post-install: - make load-secrets - @echo "Done" - .PHONY: load-secrets load-secrets: ## Load secrets into the configured backend @PATTERN_NAME=$(NAME); \