diff --git a/pkg/cmd/template.go b/pkg/cmd/template.go index 5645ab33d..09d138a0c 100644 --- a/pkg/cmd/template.go +++ b/pkg/cmd/template.go @@ -17,7 +17,6 @@ limitations under the License. package cmd import ( - "bytes" "errors" "fmt" "io" @@ -118,8 +117,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { // We ignore a potential error here because, when the --debug flag was specified, // we always want to print the YAML, even if it is not valid. The error is still returned afterwards. if rel != nil { - var manifests bytes.Buffer - fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest)) + manifests := []byte(rel.Manifest) if !client.DisableHooks { fileWritten := make(map[string]bool) for _, m := range rel.Hooks { @@ -127,7 +125,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { continue } if client.OutputDir == "" { - fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest) + manifests = fmt.Appendf(manifests, "---\n# Source: %s\n%s\n", m.Path, strings.TrimSuffix(m.Manifest, "\n")) } else { newDir := client.OutputDir if client.UseReleaseName { @@ -151,7 +149,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { if len(showFiles) > 0 { // This is necessary to ensure consistent manifest ordering when using --show-only // with globs or directory names. - splitManifests := releaseutil.SplitManifests(manifests.String()) + splitManifests := releaseutil.SplitManifests(string(manifests)) manifestsKeys := make([]string, 0, len(splitManifests)) for k := range splitManifests { manifestsKeys = append(manifestsKeys, k) @@ -195,10 +193,10 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { } } for _, m := range manifestsToRender { - fmt.Fprintf(out, "---\n%s\n", m) + fmt.Fprintf(out, "---\n%s\n", strings.TrimSuffix(m, "\n")) } } else { - fmt.Fprintf(out, "%s", manifests.String()) + out.Write(manifests) } } @@ -253,7 +251,7 @@ func writeToFile(outputDir, name, data string, appendData bool) error { defer f.Close() - _, err = fmt.Fprintf(f, "---\n# Source: %s\n%s\n", name, data) + _, err = fmt.Fprintf(f, "---\n# Source: %s\n%s\n", name, strings.TrimSuffix(data, "\n")) if err != nil { return err } diff --git a/pkg/cmd/template_test.go b/pkg/cmd/template_test.go index d508a0505..2bc047237 100644 --- a/pkg/cmd/template_test.go +++ b/pkg/cmd/template_test.go @@ -20,12 +20,17 @@ import ( "fmt" "path/filepath" "testing" + + "github.com/stretchr/testify/require" + + "helm.sh/helm/v4/internal/test" ) var chartPath = "testdata/testcharts/subchart" func TestTemplateCmd(t *testing.T) { deletevalchart := "testdata/testcharts/issue-9027" + newlinesChart := "testdata/testcharts/trailing-newlines" tests := []cmdTestCase{ { @@ -172,10 +177,66 @@ func TestTemplateCmd(t *testing.T) { cmd: fmt.Sprintf("template '%s' -f %s/extra_values.yaml", chartPath, chartPath), golden: "output/template-subchart-cm-set-file.txt", }, + { + name: "trailing newlines", + cmd: fmt.Sprintf("template '%s'", newlinesChart), + golden: "output/template-newlines.txt", + }, + { + name: "trailing newlines without hooks", + cmd: fmt.Sprintf("template '%s' --no-hooks", newlinesChart), + golden: "output/template-newlines-no-hooks.txt", + }, + { + name: "trailing newlines with reversed show-only order", + cmd: fmt.Sprintf("template '%s' --show-only templates/hooks.yaml --show-only templates/00-no-newline-hook.yaml --show-only templates/regular.yaml --show-only templates/00-no-newline.yaml", newlinesChart), + golden: "output/template-newlines-reversed.txt", + }, + { + name: "trailing newlines with show-only regular manifests", + cmd: fmt.Sprintf("template '%s' --show-only templates/regular.yaml", newlinesChart), + golden: "output/template-newlines-regular.txt", + }, + { + name: "trailing newlines with show-only hooks", + cmd: fmt.Sprintf("template '%s' --show-only templates/hooks.yaml", newlinesChart), + golden: "output/template-newlines-hooks.txt", + }, + { + name: "missing final newline with show-only", + cmd: fmt.Sprintf("template '%s' --show-only templates/00-no-newline.yaml", newlinesChart), + golden: "output/template-newlines-no-newline.txt", + }, + { + name: "missing final newline with show-only hook", + cmd: fmt.Sprintf("template '%s' --show-only templates/00-no-newline-hook.yaml", newlinesChart), + golden: "output/template-newlines-no-newline-hook.txt", + }, } runTestCmd(t, tests) } +func TestTemplateCmdOutputDir(t *testing.T) { + t.Cleanup(resetEnv()) + outputDir := t.TempDir() + _, _, err := executeActionCommand(fmt.Sprintf("template testdata/testcharts/trailing-newlines --output-dir %q", outputDir)) + require.NoError(t, err) + + for _, tc := range []struct { + file string + golden string + }{ + {"regular.yaml", "output/template-newlines-regular.txt"}, + {"hooks.yaml", "output/template-newlines-hooks.txt"}, + {"00-no-newline.yaml", "output/template-newlines-no-newline.txt"}, + {"00-no-newline-hook.yaml", "output/template-newlines-no-newline-hook.txt"}, + } { + t.Run(tc.file, func(t *testing.T) { + test.AssertGoldenFile(t, filepath.Join(outputDir, "trailing-newlines", "templates", tc.file), tc.golden) + }) + } +} + func TestTemplateVersionCompletion(t *testing.T) { repoFile := "testdata/helmhome/helm/repositories.yaml" repoCache := "testdata/helmhome/helm/repository" diff --git a/pkg/cmd/testdata/output/object-order.txt b/pkg/cmd/testdata/output/object-order.txt index 8ca93377c..985022951 100644 --- a/pkg/cmd/testdata/output/object-order.txt +++ b/pkg/cmd/testdata/output/object-order.txt @@ -202,4 +202,3 @@ spec: - Egress - Ingress - diff --git a/pkg/cmd/testdata/output/template-name-template.txt b/pkg/cmd/testdata/output/template-name-template.txt index 485b2f59c..9406048dd 100644 --- a/pkg/cmd/testdata/output/template-name-template.txt +++ b/pkg/cmd/testdata/output/template-name-template.txt @@ -93,7 +93,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -113,4 +112,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/output/template-newlines-hooks.txt b/pkg/cmd/testdata/output/template-newlines-hooks.txt new file mode 100644 index 000000000..3062af92b --- /dev/null +++ b/pkg/cmd/testdata/output/template-newlines-hooks.txt @@ -0,0 +1,25 @@ +--- +# Source: trailing-newlines/templates/hooks.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-one-newline + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/hooks.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-two-newlines + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 + diff --git a/pkg/cmd/testdata/output/template-newlines-no-hooks.txt b/pkg/cmd/testdata/output/template-newlines-no-hooks.txt new file mode 100644 index 000000000..ade45d45c --- /dev/null +++ b/pkg/cmd/testdata/output/template-newlines-no-hooks.txt @@ -0,0 +1,31 @@ +--- +# Source: trailing-newlines/templates/00-no-newline.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-no-newline +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/regular.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-one-newline +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/regular.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-two-newlines +data: + cfg: |+ + line1 + line2 + diff --git a/pkg/cmd/testdata/output/template-newlines-no-newline-hook.txt b/pkg/cmd/testdata/output/template-newlines-no-newline-hook.txt new file mode 100644 index 000000000..6ad15b529 --- /dev/null +++ b/pkg/cmd/testdata/output/template-newlines-no-newline-hook.txt @@ -0,0 +1,12 @@ +--- +# Source: trailing-newlines/templates/00-no-newline-hook.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-no-newline + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 diff --git a/pkg/cmd/testdata/output/template-newlines-no-newline.txt b/pkg/cmd/testdata/output/template-newlines-no-newline.txt new file mode 100644 index 000000000..8b233153f --- /dev/null +++ b/pkg/cmd/testdata/output/template-newlines-no-newline.txt @@ -0,0 +1,10 @@ +--- +# Source: trailing-newlines/templates/00-no-newline.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-no-newline +data: + cfg: |+ + line1 + line2 diff --git a/pkg/cmd/testdata/output/template-newlines-regular.txt b/pkg/cmd/testdata/output/template-newlines-regular.txt new file mode 100644 index 000000000..e014ef387 --- /dev/null +++ b/pkg/cmd/testdata/output/template-newlines-regular.txt @@ -0,0 +1,21 @@ +--- +# Source: trailing-newlines/templates/regular.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-one-newline +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/regular.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-two-newlines +data: + cfg: |+ + line1 + line2 + diff --git a/pkg/cmd/testdata/output/template-newlines-reversed.txt b/pkg/cmd/testdata/output/template-newlines-reversed.txt new file mode 100644 index 000000000..7cc169cb7 --- /dev/null +++ b/pkg/cmd/testdata/output/template-newlines-reversed.txt @@ -0,0 +1,68 @@ +--- +# Source: trailing-newlines/templates/hooks.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-one-newline + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/hooks.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-two-newlines + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 + +--- +# Source: trailing-newlines/templates/00-no-newline-hook.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-no-newline + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/regular.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-one-newline +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/regular.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-two-newlines +data: + cfg: |+ + line1 + line2 + +--- +# Source: trailing-newlines/templates/00-no-newline.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-no-newline +data: + cfg: |+ + line1 + line2 diff --git a/pkg/cmd/testdata/output/template-newlines.txt b/pkg/cmd/testdata/output/template-newlines.txt new file mode 100644 index 000000000..a9d434dd8 --- /dev/null +++ b/pkg/cmd/testdata/output/template-newlines.txt @@ -0,0 +1,68 @@ +--- +# Source: trailing-newlines/templates/00-no-newline.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-no-newline +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/regular.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-one-newline +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/regular.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-two-newlines +data: + cfg: |+ + line1 + line2 + +--- +# Source: trailing-newlines/templates/00-no-newline-hook.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-no-newline + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/hooks.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-one-newline + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 +--- +# Source: trailing-newlines/templates/hooks.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-two-newlines + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 + diff --git a/pkg/cmd/testdata/output/template-set.txt b/pkg/cmd/testdata/output/template-set.txt index cc14c2b7e..4040991cf 100644 --- a/pkg/cmd/testdata/output/template-set.txt +++ b/pkg/cmd/testdata/output/template-set.txt @@ -93,7 +93,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -113,4 +112,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/output/template-show-only-glob.txt b/pkg/cmd/testdata/output/template-show-only-glob.txt index 28bc465b2..b2d2b1c2d 100644 --- a/pkg/cmd/testdata/output/template-show-only-glob.txt +++ b/pkg/cmd/testdata/output/template-show-only-glob.txt @@ -8,7 +8,6 @@ rules: - apiGroups: [""] resources: ["pods"] verbs: ["get","list","watch"] - --- # Source: subchart/templates/subdir/rolebinding.yaml apiVersion: rbac.authorization.k8s.io/v1 @@ -23,4 +22,3 @@ subjects: - kind: ServiceAccount name: subchart-sa namespace: default - diff --git a/pkg/cmd/testdata/output/template-show-only-multiple.txt b/pkg/cmd/testdata/output/template-show-only-multiple.txt index 97f995286..1aac3081a 100644 --- a/pkg/cmd/testdata/output/template-show-only-multiple.txt +++ b/pkg/cmd/testdata/output/template-show-only-multiple.txt @@ -19,7 +19,6 @@ spec: name: nginx selector: app.kubernetes.io/name: subchart - --- # Source: subchart/charts/subcharta/templates/service.yaml apiVersion: v1 @@ -37,4 +36,3 @@ spec: name: apache selector: app.kubernetes.io/name: subcharta - diff --git a/pkg/cmd/testdata/output/template-show-only-one.txt b/pkg/cmd/testdata/output/template-show-only-one.txt index 27a56c03c..9cc34f515 100644 --- a/pkg/cmd/testdata/output/template-show-only-one.txt +++ b/pkg/cmd/testdata/output/template-show-only-one.txt @@ -19,4 +19,3 @@ spec: name: nginx selector: app.kubernetes.io/name: subchart - diff --git a/pkg/cmd/testdata/output/template-subchart-cm-set-file.txt b/pkg/cmd/testdata/output/template-subchart-cm-set-file.txt index 67fda94c7..56844e292 100644 --- a/pkg/cmd/testdata/output/template-subchart-cm-set-file.txt +++ b/pkg/cmd/testdata/output/template-subchart-cm-set-file.txt @@ -101,7 +101,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -121,4 +120,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/output/template-subchart-cm-set.txt b/pkg/cmd/testdata/output/template-subchart-cm-set.txt index a280e4466..e52f7c234 100644 --- a/pkg/cmd/testdata/output/template-subchart-cm-set.txt +++ b/pkg/cmd/testdata/output/template-subchart-cm-set.txt @@ -101,7 +101,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -121,4 +120,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/output/template-subchart-cm.txt b/pkg/cmd/testdata/output/template-subchart-cm.txt index 5338e8e0e..9cc9e2296 100644 --- a/pkg/cmd/testdata/output/template-subchart-cm.txt +++ b/pkg/cmd/testdata/output/template-subchart-cm.txt @@ -101,7 +101,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -121,4 +120,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/output/template-values-files.txt b/pkg/cmd/testdata/output/template-values-files.txt index cc14c2b7e..4040991cf 100644 --- a/pkg/cmd/testdata/output/template-values-files.txt +++ b/pkg/cmd/testdata/output/template-values-files.txt @@ -93,7 +93,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -113,4 +112,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/output/template-with-api-version.txt b/pkg/cmd/testdata/output/template-with-api-version.txt index 81346ff91..8b6074cdb 100644 --- a/pkg/cmd/testdata/output/template-with-api-version.txt +++ b/pkg/cmd/testdata/output/template-with-api-version.txt @@ -95,7 +95,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -115,4 +114,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/output/template-with-crds.txt b/pkg/cmd/testdata/output/template-with-crds.txt index b17088b76..a0c78ea12 100644 --- a/pkg/cmd/testdata/output/template-with-crds.txt +++ b/pkg/cmd/testdata/output/template-with-crds.txt @@ -109,7 +109,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -129,4 +128,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/output/template-with-kube-version.txt b/pkg/cmd/testdata/output/template-with-kube-version.txt index e088a9085..9d326f328 100644 --- a/pkg/cmd/testdata/output/template-with-kube-version.txt +++ b/pkg/cmd/testdata/output/template-with-kube-version.txt @@ -93,7 +93,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -113,4 +112,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/output/template.txt b/pkg/cmd/testdata/output/template.txt index c3f33b919..58c480b47 100644 --- a/pkg/cmd/testdata/output/template.txt +++ b/pkg/cmd/testdata/output/template.txt @@ -93,7 +93,6 @@ metadata: "helm.sh/hook": test data: message: Hello World - --- # Source: subchart/templates/tests/test-nothing.yaml apiVersion: v1 @@ -113,4 +112,3 @@ spec: - echo - "$message" restartPolicy: Never - diff --git a/pkg/cmd/testdata/testcharts/trailing-newlines/Chart.yaml b/pkg/cmd/testdata/testcharts/trailing-newlines/Chart.yaml new file mode 100644 index 000000000..6df0457c4 --- /dev/null +++ b/pkg/cmd/testdata/testcharts/trailing-newlines/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: trailing-newlines +version: 0.1.0 diff --git a/pkg/cmd/testdata/testcharts/trailing-newlines/README.md b/pkg/cmd/testdata/testcharts/trailing-newlines/README.md new file mode 100644 index 000000000..4653b029a --- /dev/null +++ b/pkg/cmd/testdata/testcharts/trailing-newlines/README.md @@ -0,0 +1 @@ +The `00-no-newline*.yaml` templates intentionally have no final newline. The other templates contain two documents ending in one and two newlines, respectively. Keep chomping (`|+`) makes these newlines part of the ConfigMap value. diff --git a/pkg/cmd/testdata/testcharts/trailing-newlines/templates/00-no-newline-hook.yaml b/pkg/cmd/testdata/testcharts/trailing-newlines/templates/00-no-newline-hook.yaml new file mode 100644 index 000000000..63c90f588 --- /dev/null +++ b/pkg/cmd/testdata/testcharts/trailing-newlines/templates/00-no-newline-hook.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-no-newline + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 \ No newline at end of file diff --git a/pkg/cmd/testdata/testcharts/trailing-newlines/templates/00-no-newline.yaml b/pkg/cmd/testdata/testcharts/trailing-newlines/templates/00-no-newline.yaml new file mode 100644 index 000000000..75cad133a --- /dev/null +++ b/pkg/cmd/testdata/testcharts/trailing-newlines/templates/00-no-newline.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-no-newline +data: + cfg: |+ + line1 + line2 \ No newline at end of file diff --git a/pkg/cmd/testdata/testcharts/trailing-newlines/templates/hooks.yaml b/pkg/cmd/testdata/testcharts/trailing-newlines/templates/hooks.yaml new file mode 100644 index 000000000..4dd98465e --- /dev/null +++ b/pkg/cmd/testdata/testcharts/trailing-newlines/templates/hooks.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-one-newline + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: hook-two-newlines + annotations: + helm.sh/hook: pre-install +data: + cfg: |+ + line1 + line2 + diff --git a/pkg/cmd/testdata/testcharts/trailing-newlines/templates/regular.yaml b/pkg/cmd/testdata/testcharts/trailing-newlines/templates/regular.yaml new file mode 100644 index 000000000..9484b9f00 --- /dev/null +++ b/pkg/cmd/testdata/testcharts/trailing-newlines/templates/regular.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-one-newline +data: + cfg: |+ + line1 + line2 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: regular-two-newlines +data: + cfg: |+ + line1 + line2 +