From 6f1fbd9ab9f13e6fa457891c75ae0b8e9170fa86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80=20=D0=91=D1=8B=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=B2?= Date: Tue, 10 Nov 2026 15:38:57 +0500 Subject: [PATCH] fix(tool): preserve filenames in changed chart detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Read NUL-delimited git diff output without trimming whitespace or mixing stderr into paths. Cover Unicode, whitespace and quoted filenames in a temporary Git repository. Signed-off-by: Виктор Быстров --- pkg/tool/git.go | 11 ++++--- pkg/tool/git_test.go | 71 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 pkg/tool/git_test.go diff --git a/pkg/tool/git.go b/pkg/tool/git.go index 7fcb7ce69..965c2c629 100644 --- a/pkg/tool/git.go +++ b/pkg/tool/git.go @@ -55,15 +55,18 @@ func (g Git) MergeBase(commit1 string, commit2 string) (string, error) { } func (g Git) ListChangedFilesInDirs(commit string, dirs ...string) ([]string, error) { - changedChartFilesString, err := - g.exec.RunProcessAndCaptureOutput("git", "diff", "--find-renames", "--name-only", commit, "--", dirs) + cmd, err := g.exec.CreateProcess("git", "diff", "--find-renames", "--name-only", "-z", commit, "--", dirs) if err != nil { return nil, fmt.Errorf("failed creating diff: %w", err) } - if changedChartFilesString == "" { + output, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("failed creating diff: %w", err) + } + if len(output) == 0 { return nil, nil } - return strings.Split(changedChartFilesString, "\n"), nil + return strings.Split(strings.TrimSuffix(string(output), "\x00"), "\x00"), nil } func (g Git) GetURLForRemote(remote string) (string, error) { diff --git a/pkg/tool/git_test.go b/pkg/tool/git_test.go new file mode 100644 index 000000000..0b05c7b63 --- /dev/null +++ b/pkg/tool/git_test.go @@ -0,0 +1,71 @@ +// Copyright The Helm Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tool + +import ( + "os" + osexec "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/helm/chart-testing/v3/pkg/exec" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGitListChangedFilesPreservesPaths(t *testing.T) { + if _, err := osexec.LookPath("git"); err != nil { + t.Skip("git is required") + } + t.Chdir(t.TempDir()) + run := func(args ...string) string { + t.Helper() + out, err := osexec.Command("git", args...).CombinedOutput() + require.NoError(t, err, "%s", out) + return strings.TrimSpace(string(out)) + } + run("init", "--quiet") + run("config", "core.quotepath", "true") + // Comparing against the empty tree avoids creating any fixture commits. + emptyTree := run("hash-object", "-w", "-t", "tree", "--stdin") + git := NewGit(exec.NewProcessExecutor(false)) + files, err := git.ListChangedFilesInDirs(emptyTree, "charts") + require.NoError(t, err) + assert.Empty(t, files) + + paths := []string{ + "charts/plain/values.yaml", + "charts/café/values.yaml", + "charts/приложение/values.yaml", + "charts/with space/values.yaml", + " leading/values.yaml", + } + if runtime.GOOS != "windows" { + paths = append(paths, "charts/with\ttab/values.yaml", "charts/with\nnewline/values.yaml", "charts/with\"quote/values.yaml") + } + for _, path := range paths { + require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o700)) + require.NoError(t, os.WriteFile(path, []byte("value: true\n"), 0o600)) + } + run("add", "--all") + files, err = git.ListChangedFilesInDirs(emptyTree, "charts", " leading") + require.NoError(t, err) + assert.ElementsMatch(t, paths, files) + + _, err = git.ListChangedFilesInDirs("missing-revision", "charts") + require.ErrorContains(t, err, "failed creating diff") +}