fix: only skip broken symlinks when matched by .helmignore

Instead of silently skipping all broken symlinks unconditionally,
pass the error to the walkFn callback so chart loaders can check
.helmignore patterns before deciding whether to error.

- internal/sympath/walk.go: call walkFn with the error instead of
  returning nil for broken symlinks
- pkg/chart/v2/loader/directory.go: skip broken symlink errors only
  when the file matches a .helmignore rule
- internal/chart/v3/loader/directory.go: same fix for v3 charts
- Add TestLoadDirWithBrokenSymlinkNotInHelmignore to verify that
  broken symlinks not in .helmignore still cause an error

Signed-off-by: Kartik Suryavanshi <kartik.suryavanshi@outlook.com>
Signed-off-by: Kartik Suryavanshi <158498247+KartikSuryavanshi@users.noreply.github.com>
pull/32264/head
Kartik Suryavanshi 3 months ago
parent e98a25134a
commit 3d42cb4c6a

@ -77,6 +77,10 @@ func LoadDir(dir string) (*chart.Chart, error) {
n = filepath.ToSlash(n) n = filepath.ToSlash(n)
if err != nil { if err != nil {
// For broken symlinks, respect .helmignore before erroring.
if os.IsNotExist(err) && fi != nil && rules.Ignore(n, fi) {
return nil
}
return err return err
} }
if fi.IsDir() { if fi.IsDir() {

@ -90,6 +90,46 @@ func TestLoadDirWithSymlink(t *testing.T) {
verifyDependenciesLock(t, c) verifyDependenciesLock(t, c)
} }
func TestLoadDirWithBrokenSymlinkNotInHelmignore(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("symlink tests require unix")
}
tmpDir := t.TempDir()
chartYAML := `apiVersion: v2
name: test
version: 0.1.0
`
valuesYAML := `{}
`
if err := os.WriteFile(filepath.Join(tmpDir, "Chart.yaml"), []byte(chartYAML), 0644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(tmpDir, "values.yaml"), []byte(valuesYAML), 0644); err != nil {
t.Fatal(err)
}
// Create a broken symlink NOT listed in .helmignore
brokenLink := filepath.Join(tmpDir, "broken")
if err := os.Symlink("nonexistent", brokenLink); err != nil {
t.Fatal(err)
}
// Empty .helmignore — broken symlink is not ignored
if err := os.WriteFile(filepath.Join(tmpDir, ".helmignore"), []byte(""), 0644); err != nil {
t.Fatal(err)
}
l, err := Loader(tmpDir)
if err != nil {
t.Fatal(err)
}
if _, err := l.Load(); err == nil {
t.Fatal("loading chart with broken symlink not in .helmignore should fail")
}
}
func TestLoadDirWithBrokenSymlinkInHelmignore(t *testing.T) { func TestLoadDirWithBrokenSymlinkInHelmignore(t *testing.T) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
t.Skip("symlink tests require unix") t.Skip("symlink tests require unix")

@ -68,11 +68,10 @@ func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
resolved, err := filepath.EvalSymlinks(path) resolved, err := filepath.EvalSymlinks(path)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
// If a symlink's target does not exist (broken symlink), // Pass the broken symlink error to walkFn so callers
// silently skip it. Broken symlinks do not contribute // (e.g. chart loaders) can decide whether to skip it
// content and should not cause errors, especially when // based on .helmignore rules.
// they match .helmignore patterns. return walkFn(path, info, err)
return nil
} }
return err return err
} }

@ -77,6 +77,10 @@ func LoadDir(dir string) (*chart.Chart, error) {
n = filepath.ToSlash(n) n = filepath.ToSlash(n)
if err != nil { if err != nil {
// For broken symlinks, respect .helmignore before erroring.
if os.IsNotExist(err) && fi != nil && rules.Ignore(n, fi) {
return nil
}
return err return err
} }
if fi.IsDir() { if fi.IsDir() {

@ -90,6 +90,46 @@ func TestLoadDirWithSymlink(t *testing.T) {
verifyDependenciesLock(t, c) verifyDependenciesLock(t, c)
} }
func TestLoadDirWithBrokenSymlinkNotInHelmignore(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("symlink tests require unix")
}
tmpDir := t.TempDir()
chartYAML := `apiVersion: v2
name: test
version: 0.1.0
`
valuesYAML := `{}
`
if err := os.WriteFile(filepath.Join(tmpDir, "Chart.yaml"), []byte(chartYAML), 0644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(tmpDir, "values.yaml"), []byte(valuesYAML), 0644); err != nil {
t.Fatal(err)
}
// Create a broken symlink NOT listed in .helmignore
brokenLink := filepath.Join(tmpDir, "broken")
if err := os.Symlink("nonexistent", brokenLink); err != nil {
t.Fatal(err)
}
// Empty .helmignore — broken symlink is not ignored
if err := os.WriteFile(filepath.Join(tmpDir, ".helmignore"), []byte(""), 0644); err != nil {
t.Fatal(err)
}
l, err := Loader(tmpDir)
if err != nil {
t.Fatal(err)
}
if _, err := l.Load(); err == nil {
t.Fatal("loading chart with broken symlink not in .helmignore should fail")
}
}
func TestLoadDirWithBrokenSymlinkInHelmignore(t *testing.T) { func TestLoadDirWithBrokenSymlinkInHelmignore(t *testing.T) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
t.Skip("symlink tests require unix") t.Skip("symlink tests require unix")

Loading…
Cancel
Save