@@ -503,7 +503,8 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
503503 images := GetImagesAndAliasesFromApplication (app )
504504
505505 var helmNewValues yaml.Node
506- if isOnlyWhitespace (originalData ) {
506+ emptyOriginalData := isOnlyWhitespace (originalData )
507+ if emptyOriginalData {
507508 // allow non-exists target file
508509 helmNewValues = yaml.Node {
509510 Kind : yaml .DocumentNode ,
@@ -559,7 +560,26 @@ func marshalParamsOverride(app *v1alpha1.Application, originalData []byte) ([]by
559560 return nil , fmt .Errorf ("%s parameter not found" , helmAnnotationParamName )
560561 }
561562
562- err = setHelmValue (& helmNewValues , helmAnnotationParamName , helmParamName .Value )
563+ // Determine which value to use for the image name parameter
564+ valueToSet := helmParamName .Value
565+ if ! emptyOriginalData && image .HasRegistryPrefix (valueToSet ) {
566+ // helmParamName.Value is in long form (has registry URL)
567+ // Check the original value in helmNewValues to see if it's in short form
568+ // Skip this check if originalData is empty
569+ originalValue , err := getHelmValue (& helmNewValues , helmAnnotationParamName )
570+ if err == nil {
571+ // Original value exists and was found
572+ if ! image .HasRegistryPrefix (originalValue ) {
573+ // Original value is in short form, use the short form of the value to set
574+ valueToSet = image .ExtractShortForm (valueToSet )
575+ }
576+ // If originalValue is also in long form, keep using helmParamName.Value
577+ }
578+ // If getHelmValue returns an error (key not found), use helmParamName.Value as-is
579+ }
580+ // If helmParamName.Value is already in short form or originalData is empty, use it as-is
581+
582+ err = setHelmValue (& helmNewValues , helmAnnotationParamName , valueToSet )
563583 if err != nil {
564584 return nil , fmt .Errorf ("failed to set image parameter name value: %v" , err )
565585 }
@@ -757,6 +777,98 @@ func setHelmValue(currentValues *yaml.Node, key string, value interface{}) error
757777 return err
758778}
759779
780+ // getHelmValue retrieves a value from a yaml.Node using a key path.
781+ // The key can be in the form of "a.b.c" which can be:
782+ // 1. A nested hierarchy where "a" has "b" which has "c"
783+ // 2. A literal key "a.b.c" if the nested structure doesn't exist
784+ // Returns the value as a string and an error if the key is not found.
785+ func getHelmValue (values * yaml.Node , key string ) (string , error ) {
786+ current := values
787+
788+ // an unmarshalled document has a DocumentNode at the root, but
789+ // we navigate from a MappingNode.
790+ if current .Kind == yaml .DocumentNode {
791+ if len (current .Content ) == 0 {
792+ return "" , fmt .Errorf ("empty document node" )
793+ }
794+ current = current .Content [0 ]
795+ }
796+
797+ if current .Kind != yaml .MappingNode {
798+ return "" , fmt .Errorf ("unexpected type %s for root" , nodeKindString (current .Kind ))
799+ }
800+
801+ // First, try to navigate as nested path (a.b.c)
802+ keys := strings .Split (key , "." )
803+ currentForNested := current
804+
805+ for i , k := range keys {
806+ var idPtr * int
807+ // Handle array indexing pattern like "key[0]"
808+ keyPart := k
809+ matches := re .FindStringSubmatch (k )
810+ if matches != nil {
811+ idStr := matches [2 ]
812+ id , err := strconv .Atoi (idStr )
813+ if err != nil {
814+ return "" , fmt .Errorf ("id \" %s\" in yaml array must match pattern ^(.*)\\ [(.*)\\ ]$" , idStr )
815+ }
816+ idPtr = & id
817+ keyPart = matches [1 ]
818+ }
819+
820+ if idx , found := findHelmValuesKey (currentForNested , keyPart ); found {
821+ // Navigate deeper into the map
822+ currentForNested = currentForNested .Content [idx ]
823+ // unpack one level of alias; an alias of an alias is not supported
824+ if currentForNested .Kind == yaml .AliasNode {
825+ currentForNested = currentForNested .Alias
826+ }
827+
828+ if currentForNested .Kind == yaml .SequenceNode {
829+ if idPtr == nil {
830+ // Can't navigate into sequence without index
831+ break
832+ }
833+ if * idPtr < 0 || * idPtr >= len (currentForNested .Content ) {
834+ break
835+ }
836+ currentForNested = currentForNested .Content [* idPtr ]
837+ }
838+
839+ if i == len (keys )- 1 {
840+ // If we're at the final key, return the value
841+ if currentForNested .Kind == yaml .ScalarNode {
842+ return currentForNested .Value , nil
843+ }
844+ // If it's not a scalar, the nested path doesn't match, fall through to literal check
845+ break
846+ } else if currentForNested .Kind != yaml .MappingNode {
847+ // Can't navigate further, nested path doesn't exist
848+ break
849+ }
850+ } else {
851+ // Key not found in nested path, fall through to literal check
852+ break
853+ }
854+ }
855+
856+ // If nested path didn't work, try as a literal key "a.b.c"
857+ if idx , found := findHelmValuesKey (current , key ); found {
858+ valueNode := current .Content [idx ]
859+ // unpack one level of alias
860+ if valueNode .Kind == yaml .AliasNode {
861+ valueNode = valueNode .Alias
862+ }
863+ if valueNode .Kind == yaml .ScalarNode {
864+ return valueNode .Value , nil
865+ }
866+ return "" , fmt .Errorf ("literal key \" %s\" found but is not a scalar value" , key )
867+ }
868+
869+ return "" , fmt .Errorf ("key \" %s\" not found as nested path or literal key" , key )
870+ }
871+
760872func getWriteBackConfig (app * v1alpha1.Application , kubeClient * kube.ImageUpdaterKubernetesClient , argoClient ArgoCD ) (* WriteBackConfig , error ) {
761873 wbc := & WriteBackConfig {}
762874 // Default write-back is to use Argo CD API
0 commit comments