Skip to content

Commit e097834

Browse files
committed
Implement remote chart via vibe coding
1 parent 5c866a1 commit e097834

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

pkg/skaffold/deploy/helm/helm.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import (
6767
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/walk"
6868
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/warnings"
6969
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/yaml"
70+
"regexp"
7071
)
7172

7273
var (
@@ -521,6 +522,26 @@ func (h *Deployer) deployRelease(ctx context.Context, out io.Writer, releaseName
521522
version: chartVersion,
522523
}
523524

525+
// --- Begin: Handle :chart:values.yaml for remote charts ---
526+
var tempFiles []func()
527+
for i, vf := range r.ValuesFiles {
528+
if strings.HasPrefix(vf, ":chart:") && r.RemoteChart != "" {
529+
fileInChart := strings.TrimPrefix(vf, ":chart:")
530+
localPath, cleanup, err := helm.PullAndExtractChartFile(r.RemoteChart, chartVersion, fileInChart)
531+
if err != nil {
532+
return nil, nil, fmt.Errorf("failed to extract %s from remote chart: %w", fileInChart, err)
533+
}
534+
r.ValuesFiles[i] = localPath
535+
tempFiles = append(tempFiles, cleanup)
536+
}
537+
}
538+
defer func() {
539+
for _, cleanup := range tempFiles {
540+
cleanup()
541+
}
542+
}()
543+
// --- End: Handle :chart:values.yaml for remote charts ---
544+
524545
opts.namespace, err = helm.ReleaseNamespace(h.namespace, r)
525546
if err != nil {
526547
return nil, nil, err

pkg/skaffold/helm/util.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ import (
2020
"encoding/json"
2121
"fmt"
2222
"os"
23+
"archive/tar"
24+
"compress/gzip"
25+
"os/exec"
26+
"path/filepath"
27+
"io"
2328

2429
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/graph"
2530
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/schema/latest"
@@ -119,3 +124,89 @@ func ReleaseNamespace(namespace string, release latest.HelmRelease) (string, err
119124
}
120125
return "", nil
121126
}
127+
128+
// PullAndExtractChartFile pulls a remote Helm chart and extracts a file from it (e.g., values-prod.yaml).
129+
// chartRef: the remote chart reference (e.g., oci://...)
130+
// version: the chart version
131+
// fileInChart: the file to extract (e.g., values-prod.yaml)
132+
// Returns the path to the extracted file, and a cleanup function.
133+
func PullAndExtractChartFile(chartRef, version, fileInChart string) (string, func(), error) {
134+
tmpDir, err := os.MkdirTemp("", "skaffold-helm-pull-*")
135+
if err != nil {
136+
return "", nil, err
137+
}
138+
cleanup := func() { os.RemoveAll(tmpDir) }
139+
140+
// Pull the chart
141+
pullArgs := []string{"pull", chartRef, "--version", version, "--destination", tmpDir}
142+
cmd := exec.Command("helm", pullArgs...)
143+
if out, err := cmd.CombinedOutput(); err != nil {
144+
cleanup()
145+
return "", nil, fmt.Errorf("failed to pull chart: %v\n%s", err, string(out))
146+
}
147+
148+
// Find the .tgz file
149+
var tgzPath string
150+
dirEntries, err := os.ReadDir(tmpDir)
151+
if err != nil {
152+
cleanup()
153+
return "", nil, err
154+
}
155+
for _, entry := range dirEntries {
156+
if filepath.Ext(entry.Name()) == ".tgz" {
157+
tgzPath = filepath.Join(tmpDir, entry.Name())
158+
break
159+
}
160+
}
161+
if tgzPath == "" {
162+
cleanup()
163+
return "", nil, fmt.Errorf("no chart archive found after helm pull")
164+
}
165+
166+
// Extract the requested file
167+
tgzFile, err := os.Open(tgzPath)
168+
if err != nil {
169+
cleanup()
170+
return "", nil, err
171+
}
172+
defer tgzFile.Close()
173+
gzReader, err := gzip.NewReader(tgzFile)
174+
if err != nil {
175+
cleanup()
176+
return "", nil, err
177+
}
178+
tarReader := tar.NewReader(gzReader)
179+
180+
var extractedPath string
181+
for {
182+
hdr, err := tarReader.Next()
183+
if err == io.EOF {
184+
break
185+
}
186+
if err != nil {
187+
cleanup()
188+
return "", nil, err
189+
}
190+
// Chart files are inside a top-level dir, e.g. united/values-prod.yaml
191+
if filepath.Base(hdr.Name) == fileInChart {
192+
extractedPath = filepath.Join(tmpDir, fileInChart)
193+
outFile, err := os.Create(extractedPath)
194+
if err != nil {
195+
cleanup()
196+
return "", nil, err
197+
}
198+
if _, err := io.Copy(outFile, tarReader); err != nil {
199+
outFile.Close()
200+
cleanup()
201+
return "", nil, err
202+
}
203+
outFile.Close()
204+
break
205+
}
206+
}
207+
if extractedPath == "" {
208+
cleanup()
209+
return "", nil, fmt.Errorf("file %s not found in chart", fileInChart)
210+
}
211+
return extractedPath, cleanup, nil
212+
}

0 commit comments

Comments
 (0)