diff --git a/internal/charts/chart_dependecies.go b/internal/charts/chart_dependecies.go index a139c32b..41697cb8 100644 --- a/internal/charts/chart_dependecies.go +++ b/internal/charts/chart_dependecies.go @@ -1,21 +1,11 @@ package charts import ( - "os" - "path/filepath" - "go.lsp.dev/uri" ) func (c *Chart) GetDependecyURI(dependencyName string) uri.URI { - unpackedPath := filepath.Join(c.RootURI.Filename(), "charts", dependencyName) - fileInfo, err := os.Stat(unpackedPath) - - if err == nil && fileInfo.IsDir() { - return uri.File(unpackedPath) - } - - return uri.File(filepath.Join(c.RootURI.Filename(), "charts", DependencyCacheFolder, dependencyName)) + return uri.File(c.getDependencyDir(dependencyName)) } func (c *Chart) GetDependeciesTemplates() []*DependencyTemplateFile { diff --git a/internal/charts/chart_test.go b/internal/charts/chart_test.go index bc4a9c8d..ffcf9fea 100644 --- a/internal/charts/chart_test.go +++ b/internal/charts/chart_test.go @@ -255,3 +255,20 @@ func TestGetValueLocation(t *testing.T) { assert.Equal(t, expected, valueLocation) } + +func TestLoadsHelmChartWithDependeciesAsFileURI(t *testing.T) { + chart := charts.NewChart(uri.File("../../testdata/dependencyFileURIExample/"), util.ValuesFilesConfig{}) + + dependecyTemplates := chart.GetDependeciesTemplates() + assert.Len(t, dependecyTemplates, 2) + + filePaths := []string{} + for _, dependency := range dependecyTemplates { + filePaths = append(filePaths, dependency.Path) + } + path, _ := filepath.Abs("../../testdata/dependenciesExample/charts/subchartexample/templates/subchart.yaml") + assert.Contains(t, filePaths, path) + + path, _ = filepath.Abs("../../testdata/dependenciesExample/charts/subchartexample/templates/_helpers_subchart.tpl") + assert.Contains(t, filePaths, path) +} diff --git a/internal/charts/dependency_files.go b/internal/charts/dependency_files.go index bf65c0f3..07043684 100644 --- a/internal/charts/dependency_files.go +++ b/internal/charts/dependency_files.go @@ -1,6 +1,7 @@ package charts import ( + "net/url" "os" "path/filepath" "strings" @@ -26,7 +27,32 @@ type PossibleDependencyFile interface { GetPath() string } +func (c *Chart) getDependencyPathFromMetadata(chartName string) string { + for _, dep := range c.ChartMetadata.Metadata.Dependencies { + if dep.Name == chartName { + return dep.Repository + } + } + return "" +} + func (c *Chart) getDependencyDir(chartName string) string { + dependencyURI := c.getDependencyPathFromMetadata(chartName) + fileURIPrefix := "file://" + if dependencyURI != "" && strings.HasPrefix(dependencyURI, fileURIPrefix) { + relativePath := dependencyURI[len(fileURIPrefix):] + relativePathClean, err := url.PathUnescape(relativePath) + if err != nil { + logger.Error("Could not unescape dependency file path", relativePath, err) + } + absolutePath := filepath.Join(c.RootURI.Filename(), relativePathClean) + + _, err = os.Stat(absolutePath) + if err == nil { + return absolutePath + } + } + extractedPath := filepath.Join(c.RootURI.Filename(), "charts", chartName) _, err := os.Stat(extractedPath) if err == nil { diff --git a/internal/handler/yaml_handler/definition_test.go b/internal/handler/yaml_handler/definition_test.go index 654ff577..1559a784 100644 --- a/internal/handler/yaml_handler/definition_test.go +++ b/internal/handler/yaml_handler/definition_test.go @@ -132,6 +132,19 @@ func TestDefinition(t *testing.T) { }, "", }, + { + "From chart to dependency defined by URI", + "../../../testdata/dependencyFileURIExample/values.yaml", + "^onlyInSubchartValues:", + + []testutil.ExpectedLocationsResult{ + { + Filepath: "../../../testdata/dependenciesExample/charts/subchartexample/values.yaml", + MarkedLine: "§onlyInSubchartValues§: ", + }, + }, + "", + }, } for _, tc := range testCases { diff --git a/testdata/dependencyFileURIExample/.helmignore b/testdata/dependencyFileURIExample/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/testdata/dependencyFileURIExample/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/testdata/dependencyFileURIExample/Chart.lock b/testdata/dependencyFileURIExample/Chart.lock new file mode 100644 index 00000000..3d768586 --- /dev/null +++ b/testdata/dependencyFileURIExample/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: subchartexample + repository: file://../dependenciesExample/charts/subchartexample + version: 0.1.0 +digest: sha256:4ca6d785cb293110a14cfd230b21a1a420a5a3d817c9ebdf7d1ad8d77c139900 +generated: "2025-08-03T13:33:39.788449204+02:00" diff --git a/testdata/dependencyFileURIExample/Chart.yaml b/testdata/dependencyFileURIExample/Chart.yaml new file mode 100644 index 00000000..02e50709 --- /dev/null +++ b/testdata/dependencyFileURIExample/Chart.yaml @@ -0,0 +1,28 @@ +apiVersion: v2 +name: dependencyFileURIExample +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" +dependencies: + - name: subchartexample + version: x.x.x + repository: "file://../dependenciesExample/charts/subchartexample" diff --git a/testdata/dependencyFileURIExample/charts/.helm_ls_cache/subchartexample/values.yaml b/testdata/dependencyFileURIExample/charts/.helm_ls_cache/subchartexample/values.yaml new file mode 100644 index 00000000..b43e5851 --- /dev/null +++ b/testdata/dependencyFileURIExample/charts/.helm_ls_cache/subchartexample/values.yaml @@ -0,0 +1,5 @@ +global: + subchart: works + globalFromSubchart: works +subchartWithoutGlobal: works +onlyInSubchartValues: hi diff --git a/testdata/dependencyFileURIExample/charts/subchartexample-0.1.0.tgz b/testdata/dependencyFileURIExample/charts/subchartexample-0.1.0.tgz new file mode 100644 index 00000000..1b3142a6 Binary files /dev/null and b/testdata/dependencyFileURIExample/charts/subchartexample-0.1.0.tgz differ diff --git a/testdata/dependencyFileURIExample/values.yaml b/testdata/dependencyFileURIExample/values.yaml new file mode 100644 index 00000000..0af6c5e0 --- /dev/null +++ b/testdata/dependencyFileURIExample/values.yaml @@ -0,0 +1,2 @@ +subchartexample: + onlyInSubchartValues: hi