From 12ef097c493456b01d0aff1d363e823302e7d36f Mon Sep 17 00:00:00 2001 From: Drew Minnear Date: Mon, 3 Nov 2025 11:00:15 -0500 Subject: [PATCH 1/4] add latest fixes from common to pattern.sh --- resources/pattern.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/pattern.sh b/resources/pattern.sh index 54ed86f..d6daa15 100755 --- a/resources/pattern.sh +++ b/resources/pattern.sh @@ -62,7 +62,7 @@ fi # Detect if we use podman machine. If we do not then we bind mount local host ssl folders # if we are using podman machine then we do not bind mount anything (for now!) -REMOTE_PODMAN=$(podman system connection list -q | wc -l) +REMOTE_PODMAN=$(podman system connection list | tail -n +2 | wc -l) if [ $REMOTE_PODMAN -eq 0 ]; then # If we are not using podman machine we check the hosts folders # We check /etc/pki/tls because on ubuntu /etc/pki/fwupd sometimes # exists but not /etc/pki/tls and we do not want to bind mount in such a case @@ -107,10 +107,11 @@ podman run -it --rm --pull=newer \ -e UUID_FILE \ -e VALUES_SECRET \ ${PKI_HOST_MOUNT_ARGS} \ + -v "$(pwd -P)":"$(pwd -P)" \ -v "${HOME}":"${HOME}" \ -v "${HOME}":/pattern-home \ ${PODMAN_ARGS} \ ${EXTRA_ARGS} \ - -w "$(pwd)" \ + -w "$(pwd -P)" \ "$PATTERN_UTILITY_CONTAINER" \ $@ From 45a68af118bef4482e8dc311307a8900ee3b43f3 Mon Sep 17 00:00:00 2001 From: Drew Minnear Date: Mon, 3 Nov 2025 13:30:22 -0500 Subject: [PATCH 2/4] fix new patterns always defaulting to repo pattern name --- Containerfile | 3 - README.md | 16 ++--- src/internal/pattern/pattern.go | 30 ---------- src/internal/pattern/pattern_test.go | 88 ---------------------------- 4 files changed, 8 insertions(+), 129 deletions(-) diff --git a/Containerfile b/Containerfile index 4f34a50..f50acf6 100644 --- a/Containerfile +++ b/Containerfile @@ -24,9 +24,6 @@ WORKDIR ${PATTERNIZER_RESOURCES_DIR} COPY resources/* . -ARG PATTERN_REPO_ROOT=/repo -WORKDIR ${PATTERN_REPO_ROOT} - ENV PATTERNIZER_RESOURCES_DIR=${PATTERNIZER_RESOURCES_DIR} ENTRYPOINT ["patternizer"] diff --git a/README.md b/README.md index c2f6c2a..65749cb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Patternizer -![Version: 0.2.0](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) +![Version: 0.2.1](https://img.shields.io/badge/Version-0.2.0-informational?style=flat-square) [![Quay Repository](https://img.shields.io/badge/Quay.io-patternizer-blue?logo=quay)](https://quay.io/repository/validatedpatterns/patternizer) [![CI Pipeline](https://github.com/validatedpatterns/patternizer/actions/workflows/build-push.yaml/badge.svg?branch=main)](https://github.com/validatedpatterns/patternizer/actions/workflows/build-push.yaml) @@ -50,7 +50,7 @@ Navigate to your repository's root directory and run the initialization command: ```bash # In the root of your pattern-repo -podman run -v "$PWD:/repo:z" quay.io/validatedpatterns/patternizer init +podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init ``` This single command will generate all the necessary files to turn your repository into a Validated Pattern. @@ -68,7 +68,7 @@ This single command will generate all the necessary files to turn your repositor 2. **Initialize the pattern using Patternizer:** ```bash - podman run -v "$PWD:/repo:z" quay.io/validatedpatterns/patternizer init + podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init ``` 3. **Review, commit, and push the generated files:** @@ -90,18 +90,18 @@ This single command will generate all the necessary files to turn your repositor ### Container Usage (Recommended) -Using the prebuilt container is the easiest way to run Patternizer, as it requires no local installation. The `-v "$PWD:/repo:z"` flag mounts your current directory into the container's `/repo` workspace. +Using the prebuilt container is the easiest way to run Patternizer, as it requires no local installation. The `-v "$PWD:$PWD:z" -w "$PWD"` flag mounts your current directory into the container's `/repo` workspace. #### **Initialize without secrets:** ```bash -podman run -v "$PWD:/repo:z" quay.io/validatedpatterns/patternizer init +podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init ``` #### **Initialize with secrets support:** ```bash -podman run -v "$PWD:/repo:z" quay.io/validatedpatterns/patternizer init --with-secrets +podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init --with-secrets ``` #### **Upgrade an existing pattern repository:** @@ -110,10 +110,10 @@ Use this to migrate or refresh an existing pattern repo to the latest common str ```bash # Refresh common assets, keep your Makefile unless it lacks the include -podman run -v "$PWD:/repo:z" quay.io/validatedpatterns/patternizer upgrade +podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer upgrade # Replace your Makefile with the default from Patternizer -podman run -v "$PWD:/repo:z" quay.io/validatedpatterns/patternizer upgrade --replace-makefile +podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer upgrade --replace-makefile ``` What upgrade does: diff --git a/src/internal/pattern/pattern.go b/src/internal/pattern/pattern.go index 6a3e1f2..698489d 100644 --- a/src/internal/pattern/pattern.go +++ b/src/internal/pattern/pattern.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "path/filepath" - "strings" "gopkg.in/yaml.v3" @@ -26,35 +25,6 @@ func GetPatternNameAndRepoRoot() (patternName, repoRoot string, err error) { return patternName, repoRoot, nil } -// extractPatternNameFromURL extracts the pattern name from a Git repository URL. -// Returns an error if the URL format is not recognized. -func extractPatternNameFromURL(url string) (string, error) { - // Handle SSH URLs: git@github.com:user/repo.git - if strings.HasPrefix(url, "git@") { - parts := strings.Split(url, ":") - if len(parts) >= 2 { - repoPath := parts[1] - repoName := filepath.Base(repoPath) - return strings.TrimSuffix(repoName, ".git"), nil - } - return "", fmt.Errorf("invalid SSH URL format") - } - - // Handle HTTPS URLs: https://github.com/user/repo.git - if strings.HasPrefix(url, "https://") { - repoName := filepath.Base(url) - return strings.TrimSuffix(repoName, ".git"), nil - } - - // Handle HTTP URLs: http://github.com/user/repo.git - if strings.HasPrefix(url, "http://") { - repoName := filepath.Base(url) - return strings.TrimSuffix(repoName, ".git"), nil - } - - return "", fmt.Errorf("unsupported URL format: expected git@host:user/repo.git, https://host/user/repo.git, or http://host/user/repo.git") -} - // 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, withSecrets bool) (actualPatternName, clusterGroupName string, err error) { diff --git a/src/internal/pattern/pattern_test.go b/src/internal/pattern/pattern_test.go index 7f36f9b..6f50db2 100644 --- a/src/internal/pattern/pattern_test.go +++ b/src/internal/pattern/pattern_test.go @@ -10,94 +10,6 @@ import ( "github.com/dminnear-rh/patternizer/internal/types" ) -// TestExtractPatternNameFromURL tests URL parsing for different Git URL formats. -func TestExtractPatternNameFromURL(t *testing.T) { - tests := []struct { - name string - url string - expected string - expectError bool - }{ - { - name: "SSH URL with .git suffix", - url: "git@github.com:user/my-pattern.git", - expected: "my-pattern", - }, - { - name: "SSH URL without .git suffix", - url: "git@github.com:user/my-pattern", - expected: "my-pattern", - }, - { - name: "HTTPS URL with .git suffix", - url: "https://github.com/user/my-pattern.git", - expected: "my-pattern", - }, - { - name: "HTTPS URL without .git suffix", - url: "https://github.com/user/my-pattern", - expected: "my-pattern", - }, - { - name: "HTTP URL with .git suffix", - url: "http://github.com/user/my-pattern.git", - expected: "my-pattern", - }, - { - name: "HTTP URL without .git suffix", - url: "http://github.com/user/my-pattern", - expected: "my-pattern", - }, - { - name: "GitLab SSH URL", - url: "git@gitlab.com:group/subgroup/my-pattern.git", - expected: "my-pattern", - }, - { - name: "GitLab HTTPS URL", - url: "https://gitlab.com/group/subgroup/my-pattern.git", - expected: "my-pattern", - }, - { - name: "Invalid SSH URL format", - url: "git@github.com", - expectError: true, - }, - { - name: "Unsupported protocol", - url: "ftp://github.com/user/repo.git", - expectError: true, - }, - { - name: "Empty URL", - url: "", - expectError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := extractPatternNameFromURL(tt.url) - - if tt.expectError { - if err == nil { - t.Errorf("Expected error for URL '%s', but got none", tt.url) - } - return - } - - if err != nil { - t.Errorf("Unexpected error for URL '%s': %v", tt.url, err) - return - } - - if result != tt.expected { - t.Errorf("extractPatternNameFromURL('%s') = '%s', expected '%s'", tt.url, result, tt.expected) - } - }) - } -} - // TestProcessGlobalValuesPreservesFields tests that ProcessGlobalValues preserves existing user fields. func TestProcessGlobalValuesPreservesFields(t *testing.T) { tempDir, err := os.MkdirTemp("", "pattern-test-*") From ae8a72b48daa2047efa4c8d23de2469ed5111661 Mon Sep 17 00:00:00 2001 From: Drew Minnear Date: Mon, 3 Nov 2025 14:00:28 -0500 Subject: [PATCH 3/4] write yaml with two spaces instead of 4 --- src/internal/fileutils/fileutils.go | 29 +++++++++ src/internal/fileutils/fileutils_test.go | 77 ++++++++++++++++++++++++ src/internal/pattern/pattern.go | 17 ++---- 3 files changed, 111 insertions(+), 12 deletions(-) diff --git a/src/internal/fileutils/fileutils.go b/src/internal/fileutils/fileutils.go index d35815f..56fb174 100644 --- a/src/internal/fileutils/fileutils.go +++ b/src/internal/fileutils/fileutils.go @@ -6,6 +6,8 @@ import ( "os" "path/filepath" "strings" + + "gopkg.in/yaml.v3" ) // CopyFile copies a file from src to dst. If dst already exists, it will be overwritten. @@ -130,3 +132,30 @@ func PrependLineToFile(filePath, line string) error { newContents := []byte(line + "\n" + string(data)) return os.WriteFile(filePath, newContents, mode) } + +// WriteYAMLWithIndent marshals the given data structure to YAML and writes it to a file +// with 2-space indentation. This ensures consistency with prettier formatting. +func WriteYAMLWithIndent(data interface{}, filePath string) error { + file, err := os.Create(filePath) + if err != nil { + return fmt.Errorf("failed to create file %s: %w", filePath, err) + } + defer file.Close() + + encoder := yaml.NewEncoder(file) + defer encoder.Close() + + // Set indentation to 2 spaces instead of the default 4 + encoder.SetIndent(2) + + if err := encoder.Encode(data); err != nil { + 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) + } + + return nil +} diff --git a/src/internal/fileutils/fileutils_test.go b/src/internal/fileutils/fileutils_test.go index fe8937a..08da417 100644 --- a/src/internal/fileutils/fileutils_test.go +++ b/src/internal/fileutils/fileutils_test.go @@ -6,6 +6,8 @@ import ( "runtime" "strings" "testing" + + "gopkg.in/yaml.v3" ) func writeFileWithMode(t *testing.T, path, content string, mode os.FileMode) { @@ -221,3 +223,78 @@ func TestPrependLineToFile_PrependsAndPreservesMode(t *testing.T) { t.Fatalf("mode not preserved: got %v want %v", info.Mode().Perm(), mode.Perm()) } } + +func TestWriteYAMLWithIndent_Uses2SpaceIndentation(t *testing.T) { + dir := t.TempDir() + p := filepath.Join(dir, "test.yaml") + + // Create a nested structure to verify indentation + type NestedStruct struct { + Field1 string `yaml:"field1"` + Field2 int `yaml:"field2"` + } + type TestStruct struct { + Name string `yaml:"name"` + Nested NestedStruct `yaml:"nested"` + Items []string `yaml:"items"` + } + + data := TestStruct{ + Name: "test", + Nested: NestedStruct{ + Field1: "value1", + Field2: 42, + }, + Items: []string{"item1", "item2", "item3"}, + } + + // Write the YAML with our function + if err := WriteYAMLWithIndent(data, p); err != nil { + t.Fatalf("WriteYAMLWithIndent failed: %v", err) + } + + // Read the file and verify indentation + content, err := os.ReadFile(p) + if err != nil { + t.Fatalf("read failed: %v", err) + } + + // Verify 2-space indentation by checking the content + // The nested fields should be indented with 2 spaces, not 4 + contentStr := string(content) + if !strings.Contains(contentStr, " field1: value1") { + t.Fatalf("expected 2-space indentation for nested.field1, got:\n%s", contentStr) + } + if !strings.Contains(contentStr, " field2: 42") { + t.Fatalf("expected 2-space indentation for nested.field2, got:\n%s", contentStr) + } + // Verify it's not 4-space indentation + if strings.Contains(contentStr, " field1") || strings.Contains(contentStr, " field2") { + t.Fatalf("unexpected 4-space indentation found:\n%s", contentStr) + } + + // Verify the file can be unmarshaled back correctly + var decoded TestStruct + if err := yaml.Unmarshal(content, &decoded); err != nil { + t.Fatalf("failed to unmarshal written YAML: %v", err) + } + if decoded.Name != data.Name { + t.Fatalf("name mismatch: got %q want %q", decoded.Name, data.Name) + } + if decoded.Nested.Field1 != data.Nested.Field1 { + t.Fatalf("nested.field1 mismatch: got %q want %q", decoded.Nested.Field1, data.Nested.Field1) + } + if decoded.Nested.Field2 != data.Nested.Field2 { + t.Fatalf("nested.field2 mismatch: got %d want %d", decoded.Nested.Field2, data.Nested.Field2) + } + + // Verify file permissions + info, err := os.Stat(p) + if err != nil { + t.Fatalf("stat failed: %v", err) + } + expectedMode := os.FileMode(0o644) + if info.Mode().Perm() != expectedMode.Perm() { + t.Fatalf("mode mismatch: got %v want %v", info.Mode().Perm(), expectedMode.Perm()) + } +} diff --git a/src/internal/pattern/pattern.go b/src/internal/pattern/pattern.go index 698489d..0e5a965 100644 --- a/src/internal/pattern/pattern.go +++ b/src/internal/pattern/pattern.go @@ -7,6 +7,7 @@ import ( "gopkg.in/yaml.v3" + "github.com/dminnear-rh/patternizer/internal/fileutils" "github.com/dminnear-rh/patternizer/internal/types" ) @@ -54,12 +55,8 @@ 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 - finalYamlBytes, err := yaml.Marshal(values) - if err != nil { - return "", "", fmt.Errorf("failed to marshal global values: %w", err) - } - if err = os.WriteFile(globalValuesPath, finalYamlBytes, 0o644); err != nil { + // 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) } @@ -88,12 +85,8 @@ func ProcessClusterGroupValues(patternName, clusterGroupName, repoRoot string, c mergeClusterGroupValues(values, &existingValues) } - // Write back the merged values - finalYamlBytes, err := yaml.Marshal(values) - if err != nil { - return fmt.Errorf("failed to marshal cluster group values: %w", err) - } - if err = os.WriteFile(clusterGroupValuesPath, finalYamlBytes, 0o644); err != nil { + // 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) } From b2f0c465799a53fe6f115068134d04140ed3e2dc Mon Sep 17 00:00:00 2001 From: Drew Minnear Date: Mon, 3 Nov 2025 14:25:30 -0500 Subject: [PATCH 4/4] remove projects from default values --- src/internal/types/clustergroup.go | 13 ++----------- src/main_test.go | 5 ----- test/expected_values_custom_cluster_overwrite.yaml | 7 ------- test/expected_values_prod.yaml | 4 ---- test/expected_values_prod_with_secrets.yaml | 7 ------- test/expected_values_renamed_cluster_group.yaml | 7 ------- 6 files changed, 2 insertions(+), 41 deletions(-) diff --git a/src/internal/types/clustergroup.go b/src/internal/types/clustergroup.go index 19681d0..3fad2e7 100644 --- a/src/internal/types/clustergroup.go +++ b/src/internal/types/clustergroup.go @@ -66,7 +66,7 @@ func NewNamespaceEntry(namespace string) NamespaceEntry { type Application struct { Name string `yaml:"name"` Namespace string `yaml:"namespace"` - Project string `yaml:"project"` + Project string `yaml:"project,omitempty"` Path string `yaml:"path,omitempty"` Chart string `yaml:"chart,omitempty"` ChartVersion string `yaml:"chartVersion,omitempty"` @@ -87,7 +87,7 @@ type ClusterGroup struct { Name string `yaml:"name"` IsHubCluster bool `yaml:"isHubCluster,omitempty"` Namespaces []NamespaceEntry `yaml:"namespaces"` - Projects []string `yaml:"projects"` + Projects []string `yaml:"projects,omitempty"` Subscriptions map[string]Subscription `yaml:"subscriptions"` Applications map[string]Application `yaml:"applications"` OtherFields map[string]interface{} `yaml:",inline"` @@ -103,26 +103,19 @@ type ValuesClusterGroup struct { // It conditionally includes secrets-related resources based on the useSecrets flag. func NewDefaultValuesClusterGroup(patternName, clusterGroupName string, chartPaths []string, useSecrets bool) *ValuesClusterGroup { namespaces := []NamespaceEntry{NewNamespaceEntry(patternName)} - projects := []string{patternName} applications := make(map[string]Application) - if useSecrets { - projects = append(projects, clusterGroupName) - } - if useSecrets { namespaces = append(namespaces, NewNamespaceEntry("vault"), NewNamespaceEntry("golang-external-secrets")) applications["vault"] = Application{ Name: "vault", Namespace: "vault", - Project: clusterGroupName, Chart: "hashicorp-vault", ChartVersion: "0.1.*", } applications["golang-external-secrets"] = Application{ Name: "golang-external-secrets", Namespace: "golang-external-secrets", - Project: clusterGroupName, Chart: "golang-external-secrets", ChartVersion: "0.1.*", } @@ -133,7 +126,6 @@ func NewDefaultValuesClusterGroup(patternName, clusterGroupName string, chartPat app := Application{ Name: chartName, Namespace: patternName, - Project: patternName, Path: path, } applications[chartName] = app @@ -143,7 +135,6 @@ func NewDefaultValuesClusterGroup(patternName, clusterGroupName string, chartPat ClusterGroup: ClusterGroup{ Name: clusterGroupName, Namespaces: namespaces, - Projects: projects, Subscriptions: make(map[string]Subscription), Applications: applications, OtherFields: make(map[string]interface{}), diff --git a/src/main_test.go b/src/main_test.go index b48bda1..205e5ac 100644 --- a/src/main_test.go +++ b/src/main_test.go @@ -59,11 +59,6 @@ func TestNewDefaultValuesClusterGroup(t *testing.T) { t.Errorf("Expected %d namespaces, got %d", len(expectedNamespaces), len(values.ClusterGroup.Namespaces)) } - expectedProjects := []string{"test-pattern"} - if len(values.ClusterGroup.Projects) != len(expectedProjects) { - t.Errorf("Expected %d projects, got %d", len(expectedProjects), len(values.ClusterGroup.Projects)) - } - // Test with secrets valuesWithSecrets := types.NewDefaultValuesClusterGroup("test-pattern", "test-group", []string{"charts/app1"}, true) diff --git a/test/expected_values_custom_cluster_overwrite.yaml b/test/expected_values_custom_cluster_overwrite.yaml index 1189dde..cf4d73a 100644 --- a/test/expected_values_custom_cluster_overwrite.yaml +++ b/test/expected_values_custom_cluster_overwrite.yaml @@ -6,9 +6,6 @@ clusterGroup: - custom-pattern-name - vault - golang-external-secrets - projects: - - custom-pattern-name - - custom-cluster subscriptions: {} applications: custom-user-app: @@ -20,23 +17,19 @@ clusterGroup: golang-external-secrets: name: golang-external-secrets namespace: golang-external-secrets - project: custom-cluster chart: golang-external-secrets chartVersion: 0.1.* simple: name: simple namespace: custom-pattern-name - project: custom-pattern-name path: charts/simple trivial: name: trivial namespace: custom-pattern-name - project: custom-pattern-name path: charts/trivial vault: name: vault namespace: vault - project: custom-cluster chart: hashicorp-vault chartVersion: 0.1.* customClusterTopLevel: user-cluster-top-level diff --git a/test/expected_values_prod.yaml b/test/expected_values_prod.yaml index a53f5f3..19000ad 100644 --- a/test/expected_values_prod.yaml +++ b/test/expected_values_prod.yaml @@ -2,17 +2,13 @@ clusterGroup: name: prod namespaces: - trivial-pattern - projects: - - trivial-pattern subscriptions: {} applications: simple: name: simple namespace: trivial-pattern - project: trivial-pattern path: charts/simple trivial: name: trivial namespace: trivial-pattern - project: trivial-pattern path: charts/trivial diff --git a/test/expected_values_prod_with_secrets.yaml b/test/expected_values_prod_with_secrets.yaml index ecbe17c..c98df81 100644 --- a/test/expected_values_prod_with_secrets.yaml +++ b/test/expected_values_prod_with_secrets.yaml @@ -4,30 +4,23 @@ clusterGroup: - trivial-pattern - vault - golang-external-secrets - projects: - - trivial-pattern - - prod subscriptions: {} applications: golang-external-secrets: name: golang-external-secrets namespace: golang-external-secrets - project: prod chart: golang-external-secrets chartVersion: 0.1.* simple: name: simple namespace: trivial-pattern - project: trivial-pattern path: charts/simple trivial: name: trivial namespace: trivial-pattern - project: trivial-pattern path: charts/trivial vault: name: vault namespace: vault - project: prod chart: hashicorp-vault chartVersion: 0.1.* diff --git a/test/expected_values_renamed_cluster_group.yaml b/test/expected_values_renamed_cluster_group.yaml index b9376a0..307ce06 100644 --- a/test/expected_values_renamed_cluster_group.yaml +++ b/test/expected_values_renamed_cluster_group.yaml @@ -4,30 +4,23 @@ clusterGroup: - renamed-pattern - vault - golang-external-secrets - projects: - - renamed-pattern - - renamed-cluster-group subscriptions: {} applications: golang-external-secrets: name: golang-external-secrets namespace: golang-external-secrets - project: renamed-cluster-group chart: golang-external-secrets chartVersion: 0.1.* simple: name: simple namespace: renamed-pattern - project: renamed-pattern path: charts/simple trivial: name: trivial namespace: renamed-pattern - project: renamed-pattern path: charts/trivial vault: name: vault namespace: vault - project: renamed-cluster-group chart: hashicorp-vault chartVersion: 0.1.*