diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..de6244a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,72 @@ +# Patternizer - Agent Guidelines + +Patternizer is a Go CLI tool that bootstraps Git repositories containing Helm charts into ready-to-use Validated Patterns. Module path: `github.com/validatedpatterns/patternizer`. + +## Build, Lint, and Test + +Run `make dev-setup` once to install tooling (golangci-lint, ginkgo). + +Key make targets: + +- `make ci` - Full CI pipeline: lint, build, test. Run this before considering any change complete. +- `make build` - Build the binary. +- `make test` - Run all tests via Ginkgo. +- `make lint` - Run all linters (gofmt, go vet, golangci-lint). +- `make fmt` - Auto-format Go code. +- `make check` - Quick check: format, vet, build, unit tests. + +All Go source lives under `src/`. The Makefile handles `cd src` automatically. + +## Code Style + +- No unnecessary inline comments. Code should be self-documenting through clear naming and structure. +- No emojis, ASCII art, or non-standard characters anywhere in the codebase. +- Exported functions must have godoc comments (enforced by the revive linter). +- Run `make fmt` before committing. Formatting is checked by `make lint`. +- Follow the existing project layout: + - `src/cmd/` - Cobra CLI command definitions. + - `src/internal/` - Private packages (not importable externally). + - `src/internal/types/` - Shared data structures. + - `src/internal/embedded/` - Embedded resources via `go:embed`. + +## Go Best Practices + +- Wrap errors with context: `fmt.Errorf("description: %w", err)`. +- Break large functions into smaller, well-named helper functions. Each function should do one thing. +- Keep `main.go` minimal; delegate to `cmd.Execute()`. +- Use `internal/` to keep implementation details private. +- Use `go:embed` for bundled resources (see `internal/embedded/`). +- CLI commands use Cobra. Add new commands by registering them on `rootCmd` in `cmd/root.go`. + +## Testing + +- Tests must be added or updated when writing new code. +- The project uses Ginkgo v2 and Gomega (BDD style: `Describe`/`Context`/`It`). +- Test files live alongside source files as `*_test.go`. +- Each package with tests needs a `*_suite_test.go` file for Ginkgo bootstrap. +- Use `GinkgoT().TempDir()` for temporary directories in tests. +- Run `make test` to execute all tests (`ginkgo -v ./...`). + +## Versioning + +The project version is tracked in the shield badge on line 1 of `README.md` (e.g. `![Version: 2.0.0](...)`). Bump it once per session or PR when the CLI binary changes: + +- **Patch** (2.0.0 -> 2.0.1): Bug fixes, internal refactors, dependency updates. +- **Minor** (2.0.0 -> 2.1.0): New features, new commands, new flags, or changed behavior. +- **Major**: Ask the user for confirmation before bumping the major version. +- **No bump needed**: Documentation-only changes, CI config, or other changes that do not affect the compiled binary. + +Only bump the version once even if multiple code changes are made in the same session. + +## Documentation + +- Update `README.md` when code changes affect user-facing behavior, CLI usage, or flags. +- Keep the README focused on usage and contribution guidance. + +## Verification + +Run `make ci` before submitting any change. It runs the full pipeline: + +1. Lint (gofmt, go vet, golangci-lint with gocritic, misspell, and revive) +2. Build +3. Test (all Ginkgo suites) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/src/cmd/cmd_suite_test.go b/src/cmd/cmd_suite_test.go index d45d1d2..1e81090 100644 --- a/src/cmd/cmd_suite_test.go +++ b/src/cmd/cmd_suite_test.go @@ -124,9 +124,7 @@ func verifyClusterGroupValues(valuesFile string, expectedClusterGroupValues *typ } func createTestDir() string { - dir, err := os.MkdirTemp("", "patternizer-test") - Expect(err).NotTo(HaveOccurred()) - return dir + return GinkgoT().TempDir() } func runCLI(dir string, args ...string) *gexec.Session { diff --git a/src/cmd/init.go b/src/cmd/init.go index a8f8bcd..cdf06b6 100644 --- a/src/cmd/init.go +++ b/src/cmd/init.go @@ -13,25 +13,21 @@ import ( // runInit handles the initialization logic for the init command. func runInit(withSecrets bool) error { - // Get pattern name and repository root patternName, repoRoot, err := pattern.GetPatternNameAndRepoRoot() if err != nil { return fmt.Errorf("error getting pattern information: %w", err) } - // Find Helm charts in the repository chartPaths, err := helm.FindTopLevelCharts(repoRoot) if err != nil { return fmt.Errorf("error finding Helm charts: %w", err) } - // Process values-global.yaml actualPatternName, clusterGroupName, err := pattern.ProcessGlobalValues(patternName, repoRoot, withSecrets) if err != nil { return fmt.Errorf("error processing global values: %w", err) } - // Process cluster group values using the actual pattern name and cluster group name from the global values if err := pattern.ProcessClusterGroupValues(actualPatternName, clusterGroupName, repoRoot, chartPaths, withSecrets); err != nil { return fmt.Errorf("error processing cluster group values: %w", err) } diff --git a/src/cmd/init_test.go b/src/cmd/init_test.go index e32afd2..1f6d9fe 100644 --- a/src/cmd/init_test.go +++ b/src/cmd/init_test.go @@ -61,10 +61,6 @@ var _ = Describe("patternizer init", func() { _ = runCLI(tempDir, "init") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the common pattern scaffold files", func() { verifyScaffoldFilesCopied(tempDir) }) @@ -118,10 +114,6 @@ var _ = Describe("patternizer init", func() { _ = runCLI(tempDir, "init") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the common pattern scaffold files", func() { verifyScaffoldFilesCopied(tempDir) }) @@ -182,10 +174,6 @@ var _ = Describe("patternizer init", func() { _ = runCLI(tempDir, "init") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the common pattern scaffold files", func() { verifyScaffoldFilesCopied(tempDir) }) @@ -235,10 +223,6 @@ var _ = Describe("patternizer init", func() { _ = runCLI(tempDir, "init") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the common pattern scaffold files", func() { verifyScaffoldFilesCopied(tempDir) }) @@ -297,10 +281,6 @@ var _ = Describe("patternizer init", func() { _ = runCLI(tempDir, "init") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should respect the explicit singleArgoCD override", func() { globalValuesFile := filepath.Join(tempDir, "values-global.yaml") expectedGlobalValues := types.ValuesGlobal{ @@ -333,10 +313,6 @@ var _ = Describe("patternizer init --with-secrets", func() { _ = runCLI(tempDir, "init", "--with-secrets") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the common pattern scaffold files", func() { verifyScaffoldFilesCopied(tempDir) }) @@ -419,10 +395,6 @@ var _ = Describe("patternizer init --with-secrets", func() { _ = runCLI(tempDir, "init", "--with-secrets") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the common pattern scaffold files", func() { verifyScaffoldFilesCopied(tempDir) }) @@ -515,10 +487,6 @@ var _ = Describe("patternizer init --with-secrets", func() { _ = runCLI(tempDir, "init", "--with-secrets") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the common pattern scaffold files", func() { verifyScaffoldFilesCopied(tempDir) }) @@ -602,10 +570,6 @@ var _ = Describe("patternizer init --with-secrets", func() { _ = runCLI(tempDir, "init", "--with-secrets") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the secrets template file", func() { verifySecretTemplateCopied(tempDir) }) @@ -695,10 +659,6 @@ var _ = Describe("patternizer init --with-secrets", func() { _ = runCLI(tempDir, "init", "--with-secrets") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the common pattern scaffold files", func() { verifyScaffoldFilesCopied(tempDir) }) @@ -789,10 +749,6 @@ var _ = Describe("patternizer init --with-secrets", func() { _ = runCLI(tempDir, "init", "--with-secrets") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should not modify the secrets template file", func() { actual, err := os.ReadFile(filepath.Join(tempDir, "values-secret.yaml.template")) Expect(err).NotTo(HaveOccurred()) diff --git a/src/cmd/root.go b/src/cmd/root.go index 350cbd3..4464d0a 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -29,7 +29,6 @@ for a validated pattern, including values-global.yaml and values-. When --with-secrets is specified, it also copies the secrets template and configures the pattern.sh script for secrets usage.`, RunE: func(cmd *cobra.Command, args []string) error { - // Check if "help" is passed as an argument if len(args) > 0 && args[0] == "help" { return cmd.Help() } diff --git a/src/cmd/upgrade.go b/src/cmd/upgrade.go index 7a5821d..777c390 100644 --- a/src/cmd/upgrade.go +++ b/src/cmd/upgrade.go @@ -12,22 +12,18 @@ import ( // runUpgrade handles the upgrade logic for the upgrade command. func runUpgrade(replaceMakefile bool) error { - // Determine repository root _, repoRoot, err := pattern.GetPatternNameAndRepoRoot() if err != nil { return fmt.Errorf("error getting pattern information: %w", err) } - // Paths commonDirPath := filepath.Join(repoRoot, "common") patternShPath := filepath.Join(repoRoot, "pattern.sh") - // Remove common/ directory if it exists if err := fileutils.RemovePathIfExists(commonDirPath); err != nil { return fmt.Errorf("error removing common directory: %w", err) } - // Remove ./pattern.sh if it exists (symlink or file) if err := fileutils.RemovePathIfExists(patternShPath); err != nil { return fmt.Errorf("error removing pattern.sh: %w", err) } diff --git a/src/cmd/upgrade_test.go b/src/cmd/upgrade_test.go index c93f71e..0005f3c 100644 --- a/src/cmd/upgrade_test.go +++ b/src/cmd/upgrade_test.go @@ -51,10 +51,6 @@ var _ = Describe("patternizer upgrade", func() { _ = runCLI(tempDir, "upgrade") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should copy the common scaffold files (except Makefile)", func() { verifyPattenShCopied(tempDir) verifyMakefileCommonCopied(tempDir) @@ -92,10 +88,6 @@ var _ = Describe("patternizer upgrade", func() { _ = runCLI(tempDir, "upgrade") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should not update Makefiles that already include Makefile-common", func() { f, err := os.ReadFile(filepath.Join(tempDir, "Makefile")) Expect(err).NotTo(HaveOccurred()) @@ -115,10 +107,6 @@ var _ = Describe("patternizer upgrade --replace-makefile", func() { _ = runCLI(tempDir, "upgrade", "--replace-makefile") }) - AfterAll(func() { - os.RemoveAll(tempDir) - }) - It("should update the common scaffold files (including Makefile)", func() { verifyScaffoldFilesCopied(tempDir) }) diff --git a/src/internal/embedded/embedded.go b/src/internal/embedded/embedded.go index e6f6fab..15f4bf7 100644 --- a/src/internal/embedded/embedded.go +++ b/src/internal/embedded/embedded.go @@ -2,8 +2,12 @@ package embedded import "embed" +// Resources holds the embedded resources directory tree (pattern.sh, Makefile, etc.). +// //go:embed resources/* var Resources embed.FS +// Skills holds the embedded skills directory tree for IDE integrations. +// //go:embed all:skills var Skills embed.FS diff --git a/src/internal/fileutils/fileutils.go b/src/internal/fileutils/fileutils.go index 1379129..5fd63ec 100644 --- a/src/internal/fileutils/fileutils.go +++ b/src/internal/fileutils/fileutils.go @@ -16,7 +16,7 @@ import ( func CopyFile(src, dst string) error { sourceFileStat, err := os.Stat(src) if err != nil { - return err + return fmt.Errorf("stat source file %s: %w", src, err) } if !sourceFileStat.Mode().IsRegular() { @@ -25,25 +25,24 @@ func CopyFile(src, dst string) error { sourceFile, err := os.Open(src) if err != nil { - return err + return fmt.Errorf("open source file %s: %w", src, err) } defer sourceFile.Close() destinationFile, err := os.Create(dst) if err != nil { - return err + return fmt.Errorf("create destination file %s: %w", dst, err) } defer destinationFile.Close() _, err = io.Copy(destinationFile, sourceFile) if err != nil { - return err + return fmt.Errorf("copy to %s: %w", dst, err) } - // Preserve the file permissions from the source file err = os.Chmod(dst, sourceFileStat.Mode()) if err != nil { - return err + return fmt.Errorf("chmod %s: %w", dst, err) } return nil @@ -69,7 +68,7 @@ func WriteEmbeddedFile(fsys fs.FS, srcPath, dstPath string, mode os.FileMode) er return fmt.Errorf("reading embedded file %s: %w", srcPath, err) } if err := os.WriteFile(dstPath, data, mode); err != nil { - return err + return fmt.Errorf("write file %s: %w", dstPath, err) } return os.Chmod(dstPath, mode) } @@ -78,11 +77,11 @@ func WriteEmbeddedFile(fsys fs.FS, srcPath, dstPath string, mode os.FileMode) er func WriteEmbeddedDir(fsys fs.FS, srcDir, dstDir string) error { return fs.WalkDir(fsys, srcDir, func(path string, d fs.DirEntry, err error) error { if err != nil { - return err + return fmt.Errorf("walk embedded dir: %w", err) } relPath, err := filepath.Rel(srcDir, path) if err != nil { - return err + return fmt.Errorf("compute relative path for %s: %w", path, err) } target := filepath.Join(dstDir, relPath) @@ -99,19 +98,19 @@ func WriteEmbeddedDir(fsys fs.FS, srcDir, dstDir string) error { func CopyDir(src, dst string) error { srcInfo, err := os.Stat(src) if err != nil { - return err + return fmt.Errorf("stat source dir %s: %w", src, err) } if !srcInfo.IsDir() { return fmt.Errorf("%s is not a directory", src) } if err := os.MkdirAll(dst, 0o755); err != nil { - return err + return fmt.Errorf("create destination dir %s: %w", dst, err) } entries, err := os.ReadDir(src) if err != nil { - return err + return fmt.Errorf("read source dir %s: %w", src, err) } for _, entry := range entries { @@ -120,11 +119,11 @@ func CopyDir(src, dst string) error { if entry.IsDir() { if err := CopyDir(srcPath, dstPath); err != nil { - return err + return fmt.Errorf("copy subdir %s: %w", entry.Name(), err) } } else { if err := CopyFile(srcPath, dstPath); err != nil { - return err + return fmt.Errorf("copy file %s: %w", entry.Name(), err) } } } @@ -143,7 +142,7 @@ func RemovePathIfExists(targetPath string) error { if os.IsNotExist(err) { return nil } - return err + return fmt.Errorf("lstat %s: %w", targetPath, err) } if info.IsDir() { @@ -157,7 +156,7 @@ func RemovePathIfExists(targetPath string) error { func FileContainsIncludeMakefileCommon(makefilePath string) (bool, error) { data, err := os.ReadFile(makefilePath) if err != nil { - return false, err + return false, fmt.Errorf("read %s: %w", makefilePath, err) } contents := string(data) // We keep this simple to avoid regex: look for lines with 'include' and 'Makefile-common' @@ -177,7 +176,7 @@ func FileContainsIncludeMakefileCommon(makefilePath string) (bool, error) { func PrependLineToFile(filePath, line string) error { data, err := os.ReadFile(filePath) if err != nil { - return err + return fmt.Errorf("read %s: %w", filePath, err) } mode := os.FileMode(0o644) @@ -208,7 +207,6 @@ func WriteYAMLWithIndent(data interface{}, filePath string) error { return fmt.Errorf("failed to encode YAML to %s: %w", filePath, err) } - // Set file permissions to 0644 if err := os.Chmod(filePath, 0o644); err != nil { return fmt.Errorf("failed to set permissions on %s: %w", filePath, err) } diff --git a/src/internal/fileutils/skills.go b/src/internal/fileutils/skills.go index f1a3264..4cd3350 100644 --- a/src/internal/fileutils/skills.go +++ b/src/internal/fileutils/skills.go @@ -10,6 +10,7 @@ import ( var skillTargets = []string{".claude", ".cursor"} +// InstallSkills copies all embedded skill directories into the .claude and .cursor skill directories under the given repository root. func InstallSkills(repoRoot string) error { entries, err := fs.ReadDir(embedded.Skills, "skills") if err != nil { diff --git a/src/internal/helm/helm.go b/src/internal/helm/helm.go index b4b18f5..2c662ab 100644 --- a/src/internal/helm/helm.go +++ b/src/internal/helm/helm.go @@ -1,6 +1,7 @@ package helm import ( + "fmt" "io/fs" "os" "path/filepath" @@ -17,11 +18,9 @@ func IsHelmChart(path string) bool { _, valuesErr := os.Stat(valuesYamlPath) templatesInfo, templatesErr := os.Stat(templatesDirPath) - // If any of the essential files don't exist, it's not a chart. if os.IsNotExist(chartErr) || os.IsNotExist(valuesErr) || os.IsNotExist(templatesErr) { return false } - // The 'templates' path must be a directory. if !templatesInfo.IsDir() { return false } @@ -57,7 +56,7 @@ func FindTopLevelCharts(rootDir string) ([]string, error) { }) if err != nil { - return nil, err + return nil, fmt.Errorf("walk directory %s: %w", rootDir, err) } return charts, nil } diff --git a/src/internal/helm/helm_test.go b/src/internal/helm/helm_test.go index eefb552..30de35b 100644 --- a/src/internal/helm/helm_test.go +++ b/src/internal/helm/helm_test.go @@ -10,8 +10,7 @@ import ( // createTestChartStructure creates a comprehensive test directory structure for helm chart testing. func createTestChartStructure() string { - tempDir, err := os.MkdirTemp("", "helm-test-*") - Expect(err).NotTo(HaveOccurred()) + tempDir := GinkgoT().TempDir() // chart1 (valid top-level chart) chart1Dir := filepath.Join(tempDir, "chart1") @@ -69,7 +68,6 @@ func createTestChartStructure() string { var _ = Describe("FindTopLevelCharts", func() { It("should only return top-level charts and skip sub-charts and non-chart directories", func() { tempDir := createTestChartStructure() - defer os.RemoveAll(tempDir) charts, err := FindTopLevelCharts(tempDir) Expect(err).NotTo(HaveOccurred()) @@ -92,10 +90,6 @@ var _ = Describe("IsHelmChart", func() { tempDir = createTestChartStructure() }) - AfterEach(func() { - os.RemoveAll(tempDir) - }) - DescribeTable("should correctly identify helm charts", func(chartDir string, expected bool) { testDir := filepath.Join(tempDir, chartDir) diff --git a/src/internal/pattern/pattern.go b/src/internal/pattern/pattern.go index fe23493..2c8e40a 100644 --- a/src/internal/pattern/pattern.go +++ b/src/internal/pattern/pattern.go @@ -14,13 +14,11 @@ import ( // GetPatternNameAndRepoRoot returns the pattern name and repository root directory. // The pattern name is derived from the basename of the current working directory. func GetPatternNameAndRepoRoot() (patternName, repoRoot string, err error) { - // Get the current working directory repoRoot, err = os.Getwd() if err != nil { return "", "", fmt.Errorf("failed to get current directory: %w", err) } - // Use the basename as the pattern name patternName = filepath.Base(repoRoot) return patternName, repoRoot, nil } @@ -31,20 +29,17 @@ func ProcessGlobalValues(patternName, repoRoot string, withSecrets bool) (actual globalValuesPath := filepath.Join(repoRoot, "values-global.yaml") values := types.NewDefaultValuesGlobal() - // Try to read existing file yamlFile, err := os.ReadFile(globalValuesPath) if err != nil && !os.IsNotExist(err) { return "", "", fmt.Errorf("failed to read %s: %w", globalValuesPath, err) } if err == nil { - // File exists, unmarshal into our defaults (natural merging) if err = yaml.Unmarshal(yamlFile, values); err != nil { return "", "", fmt.Errorf("failed to unmarshal YAML from %s: %w", globalValuesPath, err) } } - // Set pattern name if not already set if values.Global.Pattern == "" { values.Global.Pattern = patternName } @@ -54,7 +49,6 @@ func ProcessGlobalValues(patternName, repoRoot string, withSecrets bool) (actual // If withSecrets is false, we want secretLoader to be disabled (disabled = true) values.Global.SecretLoader.Disabled = !withSecrets - // Write back the merged values with 2-space indentation if err = fileutils.WriteYAMLWithIndent(values, globalValuesPath); err != nil { return "", "", fmt.Errorf("failed to write to %s: %w", globalValuesPath, err) } @@ -67,24 +61,20 @@ func ProcessClusterGroupValues(patternName, clusterGroupName, repoRoot string, c clusterGroupValuesPath := filepath.Join(repoRoot, fmt.Sprintf("values-%s.yaml", clusterGroupName)) values := types.NewDefaultValuesClusterGroup(patternName, clusterGroupName, chartPaths, useSecrets) - // Try to read existing file yamlFile, err := os.ReadFile(clusterGroupValuesPath) if err != nil && !os.IsNotExist(err) { return fmt.Errorf("failed to read %s: %w", clusterGroupValuesPath, err) } if err == nil { - // File exists, unmarshal into a separate struct first var existingValues types.ValuesClusterGroup if err = yaml.Unmarshal(yamlFile, &existingValues); err != nil { return fmt.Errorf("failed to unmarshal YAML from %s: %w", clusterGroupValuesPath, err) } - // Merge existing values with new defaults intelligently mergeClusterGroupValues(values, &existingValues) } - // Write back the merged values with 2-space indentation if err = fileutils.WriteYAMLWithIndent(values, clusterGroupValuesPath); err != nil { return fmt.Errorf("failed to write to %s: %w", clusterGroupValuesPath, err) } @@ -94,15 +84,12 @@ func ProcessClusterGroupValues(patternName, clusterGroupName, repoRoot string, c // mergeClusterGroupValues intelligently merges existing values with new defaults func mergeClusterGroupValues(defaults, existing *types.ValuesClusterGroup) { - // Preserve existing applications and merge with new ones for key, app := range existing.ClusterGroup.Applications { defaults.ClusterGroup.Applications[key] = app } - // For namespaces: preserve existing ones and add secrets-related ones if needed existingNamespaceMap := make(map[string]bool) for _, ns := range existing.ClusterGroup.Namespaces { - // Add existing namespace to defaults if not already present found := false for _, defaultNs := range defaults.ClusterGroup.Namespaces { if ns.Equal(defaultNs) { @@ -113,25 +100,19 @@ func mergeClusterGroupValues(defaults, existing *types.ValuesClusterGroup) { if !found { defaults.ClusterGroup.Namespaces = append(defaults.ClusterGroup.Namespaces, ns) } - // Track what we have if nsStr, ok := ns.GetString(); ok { existingNamespaceMap[nsStr] = true } } - // For projects: preserve existing ones and add cluster group project if secrets are needed existingProjectMap := make(map[string]bool) for _, proj := range existing.ClusterGroup.Projects { existingProjectMap[proj] = true } - // Rebuild projects list preserving existing order but ensuring required projects are present mergedProjects := make([]string, 0) - - // Add existing projects first mergedProjects = append(mergedProjects, existing.ClusterGroup.Projects...) - // Add any missing required projects for _, proj := range defaults.ClusterGroup.Projects { if !existingProjectMap[proj] { mergedProjects = append(mergedProjects, proj) @@ -140,12 +121,10 @@ func mergeClusterGroupValues(defaults, existing *types.ValuesClusterGroup) { defaults.ClusterGroup.Projects = mergedProjects - // Merge subscriptions for key, sub := range existing.ClusterGroup.Subscriptions { defaults.ClusterGroup.Subscriptions[key] = sub } - // Merge other fields if existing.ClusterGroup.OtherFields != nil { for key, value := range existing.ClusterGroup.OtherFields { defaults.ClusterGroup.OtherFields[key] = value diff --git a/src/internal/pattern/pattern_test.go b/src/internal/pattern/pattern_test.go index dc5ab61..d21be66 100644 --- a/src/internal/pattern/pattern_test.go +++ b/src/internal/pattern/pattern_test.go @@ -35,9 +35,7 @@ var _ = Describe("ProcessGlobalValues", func() { ) BeforeEach(func() { - var err error - tempDir, err = os.MkdirTemp("", "pattern-test-*") - Expect(err).NotTo(HaveOccurred()) + tempDir = GinkgoT().TempDir() initialValues := map[string]interface{}{ "global": map[string]interface{}{ @@ -69,10 +67,6 @@ var _ = Describe("ProcessGlobalValues", func() { Expect(os.WriteFile(valuesPath, initialYaml, 0o644)).To(Succeed()) }) - AfterEach(func() { - os.RemoveAll(tempDir) - }) - It("should preserve all custom fields", func() { actualPatternName, clusterGroupName, err := ProcessGlobalValues("new-pattern", tempDir, false) Expect(err).NotTo(HaveOccurred()) @@ -119,13 +113,7 @@ var _ = Describe("ProcessGlobalValues", func() { var tempDir string BeforeEach(func() { - var err error - tempDir, err = os.MkdirTemp("", "pattern-test-*") - Expect(err).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - os.RemoveAll(tempDir) + tempDir = GinkgoT().TempDir() }) It("should create the file with defaults", func() { @@ -155,13 +143,7 @@ var _ = Describe("ProcessGlobalValues", func() { var tempDir string BeforeEach(func() { - var err error - tempDir, err = os.MkdirTemp("", "pattern-test-*") - Expect(err).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - os.RemoveAll(tempDir) + tempDir = GinkgoT().TempDir() }) It("should set SecretLoader.Disabled to false", func() { @@ -193,9 +175,7 @@ var _ = Describe("ProcessClusterGroupValues", func() { ) BeforeEach(func() { - var err error - tempDir, err = os.MkdirTemp("", "pattern-test-*") - Expect(err).NotTo(HaveOccurred()) + tempDir = GinkgoT().TempDir() initialValues := map[string]interface{}{ "clusterGroup": map[string]interface{}{ @@ -233,10 +213,6 @@ var _ = Describe("ProcessClusterGroupValues", func() { Expect(os.WriteFile(valuesPath, initialYaml, 0o644)).To(Succeed()) }) - AfterEach(func() { - os.RemoveAll(tempDir) - }) - It("should preserve custom fields", func() { chartPaths := []string{"charts/app1", "charts/app2"} Expect(ProcessClusterGroupValues("test-pattern", "prod", tempDir, chartPaths, false)).To(Succeed())