Skip to content

Commit 458f960

Browse files
authored
Update secret loading logic (#7)
* add cursor rule to use "make ci" for validating source code changes * 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. * remove unused functions and update USE_SECRETS to LOAD_SECRETS in Makefile * fix load secrets logic
1 parent 431a582 commit 458f960

13 files changed

Lines changed: 226 additions & 183 deletions

Makefile-pattern

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,28 @@ help: ## This help message
6969
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\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)
7070

7171
.PHONY: install
72-
USE_SECRETS ?= false
73-
install: operator-deploy ## Install the pattern (USE_SECRETS=true to load secrets)
74-
@if [ "$(USE_SECRETS)" != "false" ]; then \
75-
$(MAKE) post-install; \
72+
# Dynamically read secretLoader.disabled from values-global.yaml to determine LOAD_SECRETS
73+
# If secretLoader.disabled is true, LOAD_SECRETS should be false (secrets disabled)
74+
# If secretLoader.disabled is false, LOAD_SECRETS should be true (secrets enabled)
75+
LOAD_SECRETS := $(shell if [ -f values-global.yaml ]; then \
76+
YQ_OUTPUT=$$(yq -r '.global.secretLoader.disabled' values-global.yaml 2>/dev/null); \
77+
YQ_EXIT=$$?; \
78+
if [ $$YQ_EXIT -eq 0 ] && [ "$$YQ_OUTPUT" != "null" ]; then \
79+
DISABLED="$$YQ_OUTPUT"; \
80+
else \
81+
DISABLED="true"; \
82+
fi; \
83+
if [ "$$DISABLED" = "false" ]; then echo "true"; else echo "false"; fi; \
84+
else \
85+
echo "false"; \
86+
fi)
87+
install: operator-deploy ## Install the pattern (LOAD_SECRETS determined by global.secretLoader.disabled in values-global.yaml)
88+
@echo "Secrets configuration: global.secretLoader.disabled=$$(yq -r '.global.secretLoader.disabled // true' values-global.yaml 2>/dev/null || echo 'true'), LOAD_SECRETS=$(LOAD_SECRETS)"
89+
@if [ "$(LOAD_SECRETS)" != "false" ]; then \
90+
echo "Loading secrets..."; \
91+
$(MAKE) load-secrets; \
92+
else \
93+
echo "Skipping secrets loading (disabled)"; \
7694
fi
7795
@echo "Installed"
7896

@@ -155,11 +173,6 @@ validate-cluster:
155173
echo "OK";\
156174
fi
157175

158-
.PHONY: post-install
159-
post-install:
160-
make load-secrets
161-
@echo "Done"
162-
163176
.PHONY: load-secrets
164177
load-secrets: ## Load secrets into the configured backend
165178
@PATTERN_NAME=$(NAME); \

README.md

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
[![Quay Repository](https://img.shields.io/badge/Quay.io-patternizer-blue?logo=quay)](https://quay.io/repository/dminnear/patternizer)
44
[![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)
55

6+
> **Note:** This tool was developed with assistance from [Cursor](https://cursor.sh), an AI-powered code editor.
7+
68
**Patternizer** is a CLI tool and container utility designed to bootstrap Validated Pattern repositories. It automatically generates the necessary `values-global.yaml` and `values-<cluster_group>.yaml` files by inspecting Git repositories, discovering Helm charts, and applying sensible defaults.
79

810
The tool provides both a standalone CLI and containerized execution for maximum flexibility and consistency across environments.
@@ -61,17 +63,18 @@ patternizer init help
6163

6264
The `patternizer init` command generates:
6365

64-
- `values-global.yaml` - Global pattern configuration
66+
- `values-global.yaml` - Global pattern configuration with `global.secretLoader.disabled: true`
6567
- `values-<cluster_group>.yaml` - Cluster group-specific configuration
6668
- `pattern.sh` - Utility script for pattern operations
67-
- `Makefile` - Self-contained Makefile with inlined scripts for pattern management
69+
- `Makefile` - Simple include-based Makefile that includes `Makefile-pattern`
70+
- `Makefile-pattern` - Contains all pattern targets and dynamically reads secrets config from `values-global.yaml`
6871

6972
When using `--with-secrets`:
7073
- `values-secret.yaml.template` - Template for secrets configuration
71-
- Modified `pattern.sh` with `USE_SECRETS=true` as default
72-
- Modified `Makefile` with `USE_SECRETS=true` as default
74+
- `values-global.yaml` with `global.secretLoader.disabled: false` (enables secrets)
75+
- Additional applications (vault, golang-external-secrets) in cluster group values
7376

74-
Both `pattern.sh` and `Makefile` provide equivalent functionality for pattern installation and management, with the Makefile offering a more traditional build tool approach.
77+
The secrets loading behavior is controlled entirely by the `global.secretLoader.disabled` field in `values-global.yaml`.
7578

7679
---
7780

@@ -109,7 +112,7 @@ podman run --rm -it -v .:/repo:z quay.io/dminnear/patternizer init --with-secret
109112

110113
3. **Review generated files:**
111114
```bash
112-
ls -la values-*.yaml pattern.sh
115+
ls -la values-*.yaml pattern.sh Makefile*
113116
```
114117

115118
4. **Install the pattern:**
@@ -217,6 +220,10 @@ The project includes comprehensive unit tests across multiple packages:
217220
- Adds new applications while maintaining existing ones
218221
- Maintains custom fields within applications and subscriptions
219222
- `TestProcessGlobalValuesWithNewFile()` - New file creation with proper defaults
223+
- `TestProcessGlobalValuesWithSecrets()` - Validates secrets configuration:
224+
- Tests `ProcessGlobalValues` with `withSecrets=true`
225+
- Verifies `global.secretLoader.disabled: false` is set correctly
226+
- Ensures secrets-enabled configuration is properly generated
220227

221228
### Integration Tests
222229

@@ -225,29 +232,30 @@ The integration test suite (`test/integration_test.sh`) validates the complete C
225232
**Test 1: Basic Initialization (Without Secrets)**
226233
- Clones the [trivial-pattern](https://github.com/dminnear-rh/trivial-pattern) repository
227234
- Runs `patternizer init` and validates generated files
228-
- Verifies `values-global.yaml` and `values-prod.yaml` content using YAML normalization
229-
- Ensures `pattern.sh` is created with `USE_SECRETS=false` and executable permissions
230-
- Validates `Makefile` is created with `USE_SECRETS=false` and contains proper functionality
235+
- Verifies `values-global.yaml` contains `global.secretLoader.disabled: true`
236+
- Validates `values-prod.yaml` content using YAML normalization
237+
- Ensures `pattern.sh` is created and executable
238+
- Validates `Makefile` (include-based) and `Makefile-pattern` are created
231239

232240
**Test 2: Initialization with Secrets**
233241
- Tests `patternizer init --with-secrets` functionality
242+
- Verifies `values-global.yaml` contains `global.secretLoader.disabled: false`
234243
- Validates secrets-specific applications (vault, golang-external-secrets) are added
235244
- Verifies additional namespaces and `values-secret.yaml.template` are created
236-
- Ensures `pattern.sh` is configured with `USE_SECRETS=true`
237-
- Validates `Makefile` is configured with `USE_SECRETS=true` and contains secrets support
245+
- Ensures `pattern.sh` and both Makefile files are properly generated
238246

239247
**Test 3: Custom Pattern and Cluster Group Names**
240248
- Tests field preservation and intelligent merging of existing configurations
241249
- Pre-populates custom `values-global.yaml` with renamed pattern and cluster group
242250
- Verifies custom names are preserved while adding missing default configurations
243251
- Validates custom cluster group file generation (e.g., `values-renamed-cluster-group.yaml`)
244-
- Ensures both `pattern.sh` and `Makefile` are configured correctly for the custom setup
252+
- Ensures `global.secretLoader.disabled: false` is set correctly with `--with-secrets`
245253

246254
**Test 4: Sequential Execution**
247255
- Tests running `patternizer init` followed by `patternizer init --with-secrets`
248256
- Validates that the second command properly upgrades the configuration
249-
- Ensures final state matches direct `--with-secrets` execution
250-
- Verifies both `pattern.sh` and `Makefile` are updated correctly in the sequential workflow
257+
- Ensures `global.secretLoader.disabled` transitions from `true` to `false`
258+
- Verifies final state matches direct `--with-secrets` execution
251259

252260
Run integration tests locally:
253261
```bash

pattern.sh

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ else
7878
PKI_HOST_MOUNT_ARGS=""
7979
fi
8080

81-
: "${USE_SECRETS:=false}"
82-
8381
# Copy Kubeconfig from current environment. The utilities will pick up ~/.kube/config if set so it's not mandatory
8482
# $HOME is mounted as itself for any files that are referenced with absolute paths
8583
# $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 \
105103
-e K8S_AUTH_USERNAME \
106104
-e K8S_AUTH_PASSWORD \
107105
-e K8S_AUTH_TOKEN \
108-
-e USE_SECRETS="$USE_SECRETS" \
109106
${PKI_HOST_MOUNT_ARGS} \
110107
-v "${HOME}":"${HOME}" \
111108
-v "${HOME}":/pattern-home \
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
description: lint/build/test any changes to source code
3+
alwaysApply: true
4+
---
5+
6+
## Go Source Code Testing Rule
7+
8+
**ALWAYS use `make ci` for testing Go code changes - NEVER run manual go test commands**
9+
10+
### When to Apply This Rule:
11+
- Any changes to Go source code in the `src/` directory
12+
- When you want to verify that changes work correctly
13+
- Before proposing code changes to the user
14+
- When debugging build/test issues
15+
16+
### What to Use:
17+
- ✅ **CORRECT**: `make ci` - This runs the complete CI pipeline locally (lint, build, test)
18+
- ❌ **WRONG**: `cd src && go test ./... -v` - Manual go test commands
19+
- ❌ **WRONG**: `go build`, `go test`, etc. - Direct go commands
20+
- ❌ **WRONG**: `make test-unit` or other individual targets when you want full verification
21+
22+
### Why This Rule Exists:
23+
- `make ci` ensures consistency with the actual CI pipeline
24+
- It runs linting, formatting checks, building, AND testing in the correct order
25+
- It catches issues that manual testing might miss
26+
- It's the same command developers use locally
27+
28+
### The Command to Use:
29+
```bash
30+
make ci
31+
```
32+
33+
This will:
34+
1. Run all linting checks (`make lint`)
35+
2. Build the binary (`make build`)
36+
3. Run unit tests (`make test-unit`)
37+
4. Run integration tests (`make test-integration`)
38+
5. Generate coverage reports
39+
40+
@Makefile

src/cmd/init.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"fmt"
5+
"os"
56
"path/filepath"
67

78
"github.com/dminnear-rh/patternizer/internal/fileutils"
@@ -24,7 +25,7 @@ func runInit(withSecrets bool) error {
2425
}
2526

2627
// Process values-global.yaml
27-
actualPatternName, clusterGroupName, err := pattern.ProcessGlobalValues(patternName, repoRoot)
28+
actualPatternName, clusterGroupName, err := pattern.ProcessGlobalValues(patternName, repoRoot, withSecrets)
2829
if err != nil {
2930
return fmt.Errorf("error processing global values: %w", err)
3031
}
@@ -46,21 +47,19 @@ func runInit(withSecrets bool) error {
4647
return fmt.Errorf("error copying pattern.sh: %w", err)
4748
}
4849

49-
// Set USE_SECRETS in pattern.sh based on the flag
50-
if err := fileutils.ModifyPatternShScript(patternShDst, withSecrets); err != nil {
51-
return fmt.Errorf("error modifying pattern.sh: %w", err)
50+
// Always copy Makefile-pattern to the pattern repo
51+
makefilePatternSrc := filepath.Join(resourcesDir, "Makefile-pattern")
52+
makefilePatternDst := filepath.Join(repoRoot, "Makefile-pattern")
53+
if err := fileutils.CopyFile(makefilePatternSrc, makefilePatternDst); err != nil {
54+
return fmt.Errorf("error copying Makefile-pattern: %w", err)
5255
}
5356

54-
// Copy and modify Makefile
55-
makefileSrc := filepath.Join(resourcesDir, "Makefile-pattern")
57+
// Create a simple Makefile that includes Makefile-pattern (only if it doesn't exist)
5658
makefileDst := filepath.Join(repoRoot, "Makefile")
57-
if err := fileutils.CopyFile(makefileSrc, makefileDst); err != nil {
58-
return fmt.Errorf("error copying Makefile: %w", err)
59-
}
60-
61-
// Set USE_SECRETS in Makefile based on the flag
62-
if err := fileutils.ModifyMakefileScript(makefileDst, withSecrets); err != nil {
63-
return fmt.Errorf("error modifying Makefile: %w", err)
59+
if _, err := os.Stat(makefileDst); os.IsNotExist(err) {
60+
if err := fileutils.CreateIncludeMakefile(makefileDst); err != nil {
61+
return fmt.Errorf("error creating Makefile: %w", err)
62+
}
6463
}
6564

6665
// Handle secrets setup if requested

src/internal/fileutils/fileutils.go

Lines changed: 15 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package fileutils
22

33
import (
4-
"bufio"
54
"fmt"
65
"io"
76
"os"
87
"path/filepath"
9-
"regexp"
10-
"strings"
118
)
129

1310
// CopyFile copies a file from src to dst. If dst already exists, it will be overwritten.
@@ -48,104 +45,7 @@ func CopyFile(src, dst string) error {
4845
return nil
4946
}
5047

51-
// ModifyPatternShScript modifies the pattern.sh script to set USE_SECRETS to the desired value.
52-
func ModifyPatternShScript(patternShPath string, useSecrets bool) error {
53-
file, err := os.Open(patternShPath)
54-
if err != nil {
55-
return err
56-
}
57-
58-
var lines []string
59-
scanner := bufio.NewScanner(file)
60-
for scanner.Scan() {
61-
lines = append(lines, scanner.Text())
62-
}
63-
file.Close()
64-
65-
if err := scanner.Err(); err != nil {
66-
return err
67-
}
68-
69-
// Regex to match the USE_SECRETS line
70-
regex := regexp.MustCompile(`^\s*:\s*"\$\{USE_SECRETS:=(.+)\}"`)
71-
72-
for i, line := range lines {
73-
if matches := regex.FindStringSubmatch(line); matches != nil {
74-
if useSecrets {
75-
lines[i] = strings.Replace(line, matches[1], "true", 1)
76-
} else {
77-
lines[i] = strings.Replace(line, matches[1], "false", 1)
78-
}
79-
break
80-
}
81-
}
82-
83-
output, err := os.Create(patternShPath)
84-
if err != nil {
85-
return err
86-
}
87-
defer output.Close()
88-
89-
for _, line := range lines {
90-
_, err := output.WriteString(line + "\n")
91-
if err != nil {
92-
return err
93-
}
94-
}
95-
96-
return nil
97-
}
98-
99-
// ModifyMakefileScript modifies the Makefile to set USE_SECRETS to the desired value.
100-
func ModifyMakefileScript(makefilePath string, useSecrets bool) error {
101-
file, err := os.Open(makefilePath)
102-
if err != nil {
103-
return err
104-
}
105-
106-
var lines []string
107-
scanner := bufio.NewScanner(file)
108-
for scanner.Scan() {
109-
lines = append(lines, scanner.Text())
110-
}
111-
file.Close()
112-
113-
if err := scanner.Err(); err != nil {
114-
return err
115-
}
116-
117-
// Regex to match the USE_SECRETS line in Makefile format
118-
regex := regexp.MustCompile(`^USE_SECRETS\s*\?=\s*(.+)$`)
119-
120-
for i, line := range lines {
121-
if matches := regex.FindStringSubmatch(line); matches != nil {
122-
if useSecrets {
123-
lines[i] = "USE_SECRETS ?= true"
124-
} else {
125-
lines[i] = "USE_SECRETS ?= false"
126-
}
127-
break
128-
}
129-
}
130-
131-
output, err := os.Create(makefilePath)
132-
if err != nil {
133-
return err
134-
}
135-
defer output.Close()
136-
137-
for _, line := range lines {
138-
_, err := output.WriteString(line + "\n")
139-
if err != nil {
140-
return err
141-
}
142-
}
143-
144-
return nil
145-
}
146-
147-
// HandleSecretsSetup handles the setup for secrets usage by copying the secrets template
148-
// and modifying the pattern.sh script.
48+
// HandleSecretsSetup handles the setup for secrets usage by copying the secrets template.
14949
func HandleSecretsSetup(resourcesDir, repoRoot string) (err error) {
15050
// Copy the values-secret.yaml.template file to the pattern root
15151
secretsTemplateSrc := filepath.Join(resourcesDir, "values-secret.yaml.template")
@@ -155,10 +55,20 @@ func HandleSecretsSetup(resourcesDir, repoRoot string) (err error) {
15555
return fmt.Errorf("error copying secrets template: %w", err)
15656
}
15757

158-
// Modify the pattern.sh script to set USE_SECRETS=true
159-
patternShPath := filepath.Join(repoRoot, "pattern.sh")
160-
if err = ModifyPatternShScript(patternShPath, true); err != nil {
161-
return fmt.Errorf("error modifying pattern.sh for secrets: %w", err)
58+
return nil
59+
}
60+
61+
// CreateIncludeMakefile creates a simple Makefile that includes Makefile-pattern.
62+
func CreateIncludeMakefile(makefilePath string) error {
63+
content := `# Generated by patternizer
64+
# This Makefile includes the common pattern targets from Makefile-pattern
65+
# You can add custom targets above or below the include line
66+
67+
include Makefile-pattern
68+
`
69+
70+
if err := os.WriteFile(makefilePath, []byte(content), 0o644); err != nil {
71+
return fmt.Errorf("failed to create Makefile: %w", err)
16272
}
16373

16474
return nil

0 commit comments

Comments
 (0)