From 741532f397d4e7bfc020a7321dfb93300570f8ca Mon Sep 17 00:00:00 2001 From: Dmitry Bochkarev Date: Sun, 14 Dec 2025 13:55:07 +0100 Subject: [PATCH] Introduce RuntimeFiles This is requirements to implement Typescript support into werf without filtering chart.Files before release write RuntimeFiles to tar Add RuntimeDepsFiles field Signed-off-by: Dmitry Bochkarev --- pkg/chart/chart.go | 4 ++ pkg/chart/loader/load.go | 4 ++ pkg/chart/loader/load_test.go | 100 ++++++++++++++++++++++++++++++++-- pkg/chartutil/save.go | 12 +++- 4 files changed, 113 insertions(+), 7 deletions(-) diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go index fcaadd9a82c..da5e6df3c8f 100644 --- a/pkg/chart/chart.go +++ b/pkg/chart/chart.go @@ -53,6 +53,10 @@ type Chart struct { // Files are miscellaneous files in a chart archive, // e.g. README, LICENSE, etc. Files []*File `json:"files"` + // Files that are used at runtime, but should not be saved to secret/configmap. + RuntimeFiles []*File `json:"-"` + // Dependencies for RuntimeFiles that are used at runtime, but should not be saved to secret/configmap and not added to packaged chart. + RuntimeDepsFiles []*File `json:"-"` parent *Chart dependencies []*Chart diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go index 9e4d1cb3618..4e4739220d1 100644 --- a/pkg/chart/loader/load.go +++ b/pkg/chart/loader/load.go @@ -173,6 +173,10 @@ func LoadFiles(files []*BufferedFile, opts helmopts.HelmOptions) (*chart.Chart, fname := strings.TrimPrefix(f.Name, "charts/") cname := strings.SplitN(fname, "/", 2)[0] subcharts[cname] = append(subcharts[cname], &BufferedFile{Name: fname, Data: f.Data}) + case strings.HasPrefix(f.Name, "ts/node_modules/"): + c.RuntimeDepsFiles = append(c.RuntimeDepsFiles, &chart.File{Name: f.Name, Data: f.Data}) + case strings.HasPrefix(f.Name, "ts/"): + c.RuntimeFiles = append(c.RuntimeFiles, &chart.File{Name: f.Name, Data: f.Data}) default: c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) } diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go index 0b0013f5097..510dba9877c 100644 --- a/pkg/chart/loader/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -30,8 +30,12 @@ import ( "time" "github.com/werf/3p-helm/pkg/chart" + "github.com/werf/3p-helm/pkg/werf/helmopts" ) +// GlobalLoadOptions is a default set of options for testing +var GlobalLoadOptions = &helmopts.HelmOptions{} + func TestLoadDir(t *testing.T) { l, err := Loader("testdata/frobnitz") if err != nil { @@ -379,7 +383,7 @@ icon: https://example.com/64x64.png // Packaging the chart on a Windows machine will produce an // archive that has \\ as delimiters. Test that we support these archives func TestLoadFileBackslash(t *testing.T) { - c, err := Load("testdata/frobnitz_backslash-1.2.3.tgz") + c, err := Load("testdata/frobnitz_backslash-1.2.3.tgz", *GlobalLoadOptions) if err != nil { t.Fatalf("Failed to load testdata: %s", err) } @@ -454,7 +458,7 @@ func TestLoadInvalidArchive(t *testing.T) { } { illegalChart := filepath.Join(tmpdir, tt.chartname) writeTar(illegalChart, tt.internal, []byte("hello: world")) - _, err := Load(illegalChart) + _, err := Load(illegalChart, *GlobalLoadOptions) if err == nil { t.Fatal("expected error when unpacking illegal files") } @@ -466,7 +470,7 @@ func TestLoadInvalidArchive(t *testing.T) { // Make sure that absolute path gets interpreted as relative illegalChart := filepath.Join(tmpdir, "abs-path.tgz") writeTar(illegalChart, "/Chart.yaml", []byte("hello: world")) - _, err := Load(illegalChart) + _, err := Load(illegalChart, *GlobalLoadOptions) if err.Error() != "validation: chart.metadata.name is required" { t.Error(err) } @@ -474,7 +478,7 @@ func TestLoadInvalidArchive(t *testing.T) { // And just to validate that the above was not spurious illegalChart = filepath.Join(tmpdir, "abs-path2.tgz") writeTar(illegalChart, "files/whatever.yaml", []byte("hello: world")) - _, err = Load(illegalChart) + _, err = Load(illegalChart, *GlobalLoadOptions) if err.Error() != "Chart.yaml file is missing" { t.Errorf("Unexpected error message: %s", err) } @@ -482,7 +486,7 @@ func TestLoadInvalidArchive(t *testing.T) { // Finally, test that drive letter gets stripped off on Windows illegalChart = filepath.Join(tmpdir, "abs-winpath.tgz") writeTar(illegalChart, "c:\\Chart.yaml", []byte("hello: world")) - _, err = Load(illegalChart) + _, err = Load(illegalChart, *GlobalLoadOptions) if err.Error() != "validation: chart.metadata.name is required" { t.Error(err) } @@ -646,3 +650,89 @@ func verifyBomStripped(t *testing.T, files []*chart.File) { } } } + +func TestLoadFilesRuntimeFiles(t *testing.T) { + files := []*BufferedFile{ + { + Name: "Chart.yaml", + Data: []byte(`apiVersion: v2 +name: test-chart +version: "1.0.0" +`), + }, + { + Name: "ts/runtime.ts", + Data: []byte("console.log('runtime');"), + }, + { + Name: "ts/utils/helper.ts", + Data: []byte("export function helper() {}"), + }, + { + Name: "templates/deployment.yaml", + Data: []byte("some deployment"), + }, + { + Name: "values.yaml", + Data: []byte("key: value"), + }, + } + + c, err := LoadFiles(files, *GlobalLoadOptions) + if err != nil { + t.Fatalf("Expected files to be loaded, got %v", err) + } + + // Verify RuntimeFiles are loaded correctly + if len(c.RuntimeFiles) != 2 { + t.Errorf("Expected 2 runtime files, got %d", len(c.RuntimeFiles)) + } + + expectedRuntimeFiles := map[string][]byte{ + "ts/runtime.ts": []byte("console.log('runtime');"), + "ts/utils/helper.ts": []byte("export function helper() {}"), + } + + for _, rf := range c.RuntimeFiles { + expected, ok := expectedRuntimeFiles[rf.Name] + if !ok { + t.Errorf("Unexpected runtime file: %s", rf.Name) + continue + } + if !bytes.Equal(rf.Data, expected) { + t.Errorf("Runtime file %s has unexpected content", rf.Name) + } + } + + // Verify runtime files are NOT in Files collection + for _, f := range c.Files { + if strings.HasPrefix(f.Name, "ts/") { + t.Errorf("Runtime file %s should not be in Files collection", f.Name) + } + } + + // Verify runtime files are NOT in Templates collection + for _, f := range c.Templates { + if strings.HasPrefix(f.Name, "ts/") { + t.Errorf("Runtime file %s should not be in Templates collection", f.Name) + } + } + + // Verify other files are loaded correctly + // Note: default ChartTypeChart adds _werf_helpers.tpl template, so we expect 2 templates + if len(c.Templates) != 2 { + t.Errorf("Expected 2 templates, got %d", len(c.Templates)) + } + + // Verify the user template is present + foundDeployment := false + for _, tmpl := range c.Templates { + if tmpl.Name == "templates/deployment.yaml" { + foundDeployment = true + break + } + } + if !foundDeployment { + t.Error("Expected to find templates/deployment.yaml template") + } +} diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index fd7e6593049..51b3c001394 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -91,8 +91,8 @@ func SaveIntoDir(c *chart.Chart, dest string) error { } } - // Save templates and files - for _, o := range [][]*chart.File{c.Templates, c.Files} { + // Save templates, files, and runtime files (e.g., ts/ for TypeScript charts) + for _, o := range [][]*chart.File{c.Templates, c.Files, c.RuntimeFiles} { for _, f := range o { n := filepath.Join(outdir, f.Name) if err := writeFile(n, f.Data); err != nil { @@ -243,6 +243,14 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { } } + // Save runtime files (e.g., ts/ directory for TypeScript charts) + for _, f := range c.RuntimeFiles { + n := filepath.Join(base, f.Name) + if err := writeToTar(out, n, f.Data); err != nil { + return err + } + } + // Save dependencies for _, dep := range c.Dependencies() { if err := writeTarContents(out, dep, filepath.Join(base, ChartsDir)); err != nil {