@@ -383,7 +383,8 @@ func marshalParamsOverride(ctx context.Context, applicationImages *ApplicationIm
383383 images := GetImagesAndAliasesFromApplication (applicationImages )
384384
385385 var helmNewValues yaml.Node
386- if isOnlyWhitespace (originalData ) {
386+ emptyOriginalData := isOnlyWhitespace (originalData )
387+ if emptyOriginalData {
387388 // allow non-exists target file
388389 helmNewValues = yaml.Node {
389390 Kind : yaml .DocumentNode ,
@@ -439,7 +440,26 @@ func marshalParamsOverride(ctx context.Context, applicationImages *ApplicationIm
439440 return nil , fmt .Errorf ("%s parameter not found" , helmParamName )
440441 }
441442
442- err = setHelmValue (& helmNewValues , helmParamName , helmParamN .Value )
443+ // Determine which value to use for the image name parameter
444+ valueToSet := helmParamN .Value
445+ if ! emptyOriginalData && image .HasRegistryPrefix (valueToSet ) {
446+ // helmParamN.Value is in long form (has registry URL)
447+ // Check the original value in helmNewValues to see if it's in short form
448+ // Skip this check if originalData is empty
449+ originalValue , err := getHelmValue (& helmNewValues , helmParamName )
450+ if err == nil {
451+ // Original value exists and was found
452+ if ! image .HasRegistryPrefix (originalValue ) {
453+ // Original value is in short form, use the short form of the value to set
454+ valueToSet = image .ExtractShortForm (valueToSet )
455+ }
456+ // If originalValue is also in long form, keep using helmParamN.Value
457+ }
458+ // If getHelmValue returns an error (key not found), use helmParamN.Value as-is
459+ }
460+ // If helmParamN.Value is already in short form or originalData is empty, use it as-is
461+
462+ err = setHelmValue (& helmNewValues , helmParamName , valueToSet )
443463 if err != nil {
444464 return nil , fmt .Errorf ("failed to set image parameter name value: %v" , err )
445465 }
@@ -637,6 +657,98 @@ func setHelmValue(currentValues *yaml.Node, key string, value interface{}) error
637657 return err
638658}
639659
660+ // getHelmValue retrieves a value from a yaml.Node using a key path.
661+ // The key can be in the form of "a.b.c" which can be:
662+ // 1. A nested hierarchy where "a" has "b" which has "c"
663+ // 2. A literal key "a.b.c" if the nested structure doesn't exist
664+ // Returns the value as a string and an error if the key is not found.
665+ func getHelmValue (values * yaml.Node , key string ) (string , error ) {
666+ current := values
667+
668+ // an unmarshalled document has a DocumentNode at the root, but
669+ // we navigate from a MappingNode.
670+ if current .Kind == yaml .DocumentNode {
671+ if len (current .Content ) == 0 {
672+ return "" , fmt .Errorf ("empty document node" )
673+ }
674+ current = current .Content [0 ]
675+ }
676+
677+ if current .Kind != yaml .MappingNode {
678+ return "" , fmt .Errorf ("unexpected type %s for root" , nodeKindString (current .Kind ))
679+ }
680+
681+ // First, try to navigate as nested path (a.b.c)
682+ keys := strings .Split (key , "." )
683+ currentForNested := current
684+
685+ for i , k := range keys {
686+ var idPtr * int
687+ // Handle array indexing pattern like "key[0]"
688+ keyPart := k
689+ matches := re .FindStringSubmatch (k )
690+ if matches != nil {
691+ idStr := matches [2 ]
692+ id , err := strconv .Atoi (idStr )
693+ if err != nil {
694+ return "" , fmt .Errorf ("id \" %s\" in yaml array must match pattern ^(.*)\\ [(.*)\\ ]$" , idStr )
695+ }
696+ idPtr = & id
697+ keyPart = matches [1 ]
698+ }
699+
700+ if idx , found := findHelmValuesKey (currentForNested , keyPart ); found {
701+ // Navigate deeper into the map
702+ currentForNested = currentForNested .Content [idx ]
703+ // unpack one level of alias; an alias of an alias is not supported
704+ if currentForNested .Kind == yaml .AliasNode {
705+ currentForNested = currentForNested .Alias
706+ }
707+
708+ if currentForNested .Kind == yaml .SequenceNode {
709+ if idPtr == nil {
710+ // Can't navigate into sequence without index
711+ break
712+ }
713+ if * idPtr < 0 || * idPtr >= len (currentForNested .Content ) {
714+ break
715+ }
716+ currentForNested = currentForNested .Content [* idPtr ]
717+ }
718+
719+ if i == len (keys )- 1 {
720+ // If we're at the final key, return the value
721+ if currentForNested .Kind == yaml .ScalarNode {
722+ return currentForNested .Value , nil
723+ }
724+ // If it's not a scalar, the nested path doesn't match, fall through to literal check
725+ break
726+ } else if currentForNested .Kind != yaml .MappingNode {
727+ // Can't navigate further, nested path doesn't exist
728+ break
729+ }
730+ } else {
731+ // Key not found in nested path, fall through to literal check
732+ break
733+ }
734+ }
735+
736+ // If nested path didn't work, try as a literal key "a.b.c"
737+ if idx , found := findHelmValuesKey (current , key ); found {
738+ valueNode := current .Content [idx ]
739+ // unpack one level of alias
740+ if valueNode .Kind == yaml .AliasNode {
741+ valueNode = valueNode .Alias
742+ }
743+ if valueNode .Kind == yaml .ScalarNode {
744+ return valueNode .Value , nil
745+ }
746+ return "" , fmt .Errorf ("literal key \" %s\" found but is not a scalar value" , key )
747+ }
748+
749+ return "" , fmt .Errorf ("key \" %s\" not found as nested path or literal key" , key )
750+ }
751+
640752func parseDefaultTarget (appNamespace string , appName string , path string , kubeClient * kube.ImageUpdaterKubernetesClient ) string {
641753 // when running from command line and argocd-namespace is not set, e.g., via --argocd-namespace option,
642754 // kubeClient.Namespace may be resolved to "default". In this case, also use the file name without namespace
0 commit comments