Skip to content

Commit f472629

Browse files
chengfangclaude
andauthored
fix: helm values write-back for multi-source apps (#1289) (#1293)
Signed-off-by: Cheng Fang <[email protected]> Co-authored-by: Claude <[email protected]>
1 parent 2c92bc8 commit f472629

File tree

2 files changed

+106
-3
lines changed

2 files changed

+106
-3
lines changed

pkg/argocd/update.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,21 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
455455
appType := GetApplicationType(app)
456456
appSource := getApplicationSource(app)
457457

458+
// Special handling for multi-source applications with helmvalues write-back target
459+
// We need to ensure we get the Helm source, not the Kustomize source
460+
if strings.HasPrefix(app.Annotations[common.WriteBackTargetAnnotation], common.HelmPrefix) {
461+
// For helmvalues write-back, we need the Helm source specifically
462+
if app.Spec.HasMultipleSources() {
463+
for _, s := range app.Spec.Sources {
464+
if s.Helm != nil {
465+
appSource = &s
466+
appType = ApplicationTypeHelm
467+
break
468+
}
469+
}
470+
}
471+
}
472+
458473
switch appType {
459474
case ApplicationTypeKustomize:
460475
if appSource.Kustomize == nil {

pkg/argocd/update_test.go

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,6 +2242,82 @@ replicas: 1
22422242
assert.Error(t, err)
22432243
})
22442244

2245+
t.Run("Multi-source application with Kustomize first and Helm values write-back", func(t *testing.T) {
2246+
// This reproduces issue #1289 where getApplicationSource returns the Kustomize source
2247+
// instead of the Helm source for multi-source applications
2248+
expected := `
2249+
# auto generated by argocd image updater
2250+
2251+
theme:
2252+
image:
2253+
tag: latest
2254+
name: docker.io/hello-world
2255+
`
2256+
app := v1alpha1.Application{
2257+
ObjectMeta: v1.ObjectMeta{
2258+
Name: "multi-source-app",
2259+
Annotations: map[string]string{
2260+
"argocd-image-updater.argoproj.io/image-list": "theme=docker.io/hello-world:latest",
2261+
"argocd-image-updater.argoproj.io/write-back-method": "git",
2262+
"argocd-image-updater.argoproj.io/write-back-target": "helmvalues:/apps/test/theme-version.yaml",
2263+
"argocd-image-updater.argoproj.io/theme.helm.image-name": "theme.image.name",
2264+
"argocd-image-updater.argoproj.io/theme.helm.image-tag": "theme.image.tag",
2265+
},
2266+
},
2267+
Spec: v1alpha1.ApplicationSpec{
2268+
Sources: []v1alpha1.ApplicationSource{
2269+
{
2270+
// First source: Kustomize (this gets picked up by getApplicationSource!)
2271+
Path: "apps/test/overlays/dev",
2272+
RepoURL: "https://example.com/gitops.git",
2273+
Kustomize: &v1alpha1.ApplicationSourceKustomize{
2274+
// Empty kustomize
2275+
},
2276+
},
2277+
{
2278+
// Second source: Helm chart (this is what we want!)
2279+
Chart: "my-chart",
2280+
RepoURL: "https://example.com/charts",
2281+
Helm: &v1alpha1.ApplicationSourceHelm{
2282+
Parameters: []v1alpha1.HelmParameter{
2283+
{
2284+
Name: "theme.image.name",
2285+
Value: "docker.io/hello-world",
2286+
ForceString: true,
2287+
},
2288+
{
2289+
Name: "theme.image.tag",
2290+
Value: "latest",
2291+
ForceString: true,
2292+
},
2293+
},
2294+
},
2295+
},
2296+
},
2297+
},
2298+
Status: v1alpha1.ApplicationStatus{
2299+
SourceTypes: []v1alpha1.ApplicationSourceType{
2300+
v1alpha1.ApplicationSourceTypeKustomize,
2301+
v1alpha1.ApplicationSourceTypeHelm,
2302+
},
2303+
Summary: v1alpha1.ApplicationSummary{
2304+
Images: []string{
2305+
"docker.io/hello-world:latest",
2306+
},
2307+
},
2308+
},
2309+
}
2310+
2311+
// Simulate creating a new file (nil originalData)
2312+
yamlOutput, err := marshalParamsOverride(&app, nil)
2313+
require.NoError(t, err)
2314+
assert.NotEmpty(t, yamlOutput, "output should not be empty")
2315+
2316+
// The output should contain the helm values, not be empty
2317+
yamlStr := string(yamlOutput)
2318+
assert.Equal(t, strings.TrimSpace(strings.ReplaceAll(expected, "\t", " ")), strings.TrimSpace(yamlStr))
2319+
})
2320+
22452321
t.Run("Whitespace only values file from helm source does not cause error", func(t *testing.T) {
22462322
expected := `
22472323
# auto generated by argocd image updater
@@ -2302,10 +2378,22 @@ nginx:
23022378

23032379
originalData := []byte(`
23042380
`)
2305-
yaml, err := marshalParamsOverride(&app, originalData)
2381+
yamlOutput, err := marshalParamsOverride(&app, originalData)
23062382
require.NoError(t, err)
2307-
assert.NotEmpty(t, yaml)
2308-
assert.Equal(t, strings.TrimSpace(strings.ReplaceAll(expected, "\t", " ")), strings.TrimSpace(string(yaml)))
2383+
assert.NotEmpty(t, yamlOutput)
2384+
assert.Equal(t, strings.TrimSpace(strings.ReplaceAll(expected, "\t", " ")), strings.TrimSpace(string(yamlOutput)))
2385+
2386+
// Verify round-trip: marshal → unmarshal → marshal
2387+
// This ensures the generated YAML structure can be read back correctly
2388+
var roundTripNode yaml.Node
2389+
err = yaml.Unmarshal(yamlOutput, &roundTripNode)
2390+
require.NoError(t, err, "should be able to unmarshal the generated YAML")
2391+
2392+
roundTripYaml, err := yaml.Marshal(&roundTripNode)
2393+
require.NoError(t, err, "should be able to re-marshal the unmarshaled YAML")
2394+
assert.NotEmpty(t, roundTripYaml, "round-tripped YAML should not be empty")
2395+
2396+
assert.YAMLEq(t, string(yamlOutput), string(roundTripYaml), "round-tripped YAML should be semantically equivalent")
23092397
})
23102398
}
23112399

0 commit comments

Comments
 (0)