@@ -79,70 +79,81 @@ function updateContainerImagesInManifestFiles(
7979 filePaths : string [ ] ,
8080 containers : string [ ]
8181) : string [ ] {
82- if ( filePaths ?. length <= 0 ) return filePaths
82+ if ( ! filePaths ?. length ) return filePaths
8383
84- // update container images
8584 filePaths . forEach ( ( filePath : string ) => {
86- let contents = fs . readFileSync ( filePath ) . toString ( )
87- containers . forEach ( ( container : string ) => {
88- let [ imageName ] = container . split ( ':' )
89- if ( imageName . indexOf ( '@' ) > 0 ) {
90- imageName = imageName . split ( '@' ) [ 0 ]
91- }
85+ const fileContents = fs . readFileSync ( filePath , 'utf8' )
86+ const inputObjects = yaml . loadAll ( fileContents ) as K8sObject [ ]
9287
93- if ( contents . indexOf ( imageName ) > 0 )
94- contents = substituteImageNameInSpecFile (
95- contents ,
96- imageName ,
97- container
98- )
99- } )
88+ const updatedObjects = inputObjects . map ( ( obj ) => {
89+ if ( ! isWorkloadEntity ( obj . kind ) ) return obj
10090
101- // write updated files
102- fs . writeFileSync ( path . join ( filePath ) , contents )
103- } )
91+ containers . forEach ( ( container : string ) => {
92+ let [ imageName ] = container . split ( ':' )
93+ if ( imageName . includes ( '@' ) ) {
94+ imageName = imageName . split ( '@' ) [ 0 ]
95+ }
96+ updateImagesInK8sObject ( obj , imageName , container )
97+ } )
10498
99+ return obj
100+ } )
101+ const newYaml = updatedObjects . map ( ( o ) => yaml . dump ( o ) ) . join ( '---\n' )
102+ fs . writeFileSync ( path . join ( filePath ) , newYaml )
103+ } )
105104 return filePaths
106105}
107106
108- /*
109- Example:
107+ const SPECIAL_CONTAINER_SPEC_PATHS : Record < string , string > = {
108+ [ KubernetesWorkload . POD . toLowerCase ( ) ] : 'spec' ,
109+ [ KubernetesWorkload . CRON_JOB . toLowerCase ( ) ] :
110+ 'spec.jobTemplate.spec.template.spec' ,
111+ [ KubernetesWorkload . SCALED_JOB . toLowerCase ( ) ] :
112+ 'spec.jobTargetRef.template.spec'
113+ }
110114
111- Input of
112- currentString: `image: "example/example-image"`
113- imageName: `example/example-image`
114- imageNameWithNewTag: `example/example-image:identifiertag`
115+ const DEFAULT_CONTAINER_SPEC_PATH = 'spec.template.spec'
115116
116- would return
117- `image: "example/example-image:identifiertag"`
118- */
119- export function substituteImageNameInSpecFile (
120- spec : string ,
117+ export function updateImagesInK8sObject (
118+ obj : any ,
121119 imageName : string ,
122- imageNameWithNewTag : string
120+ newImage : string
123121) {
124- if ( spec . indexOf ( imageName ) < 0 ) return spec
125-
126- return spec . split ( '\n' ) . reduce ( ( acc , line ) => {
127- const imageKeyword = line . match ( / ^ * - ? * i m a g e : / )
128- if ( imageKeyword ) {
129- let [ currentImageName ] = line
130- . substring ( imageKeyword [ 0 ] . length ) // consume the line from keyword onwards
131- . trim ( )
132- . replace ( / [ ' , " ] / g, '' ) // replace allowed quotes with nothing
133- . split ( ':' )
134-
135- if ( currentImageName ?. indexOf ( ' ' ) > 0 ) {
136- currentImageName = currentImageName . split ( ' ' ) [ 0 ] // remove comments
137- }
122+ const kind = obj ?. kind ?. toLowerCase ( )
123+ const specPath =
124+ SPECIAL_CONTAINER_SPEC_PATHS [ kind ] || DEFAULT_CONTAINER_SPEC_PATH
125+
126+ // Convert dot-separated path string into nested object traversal with full optional chaining
127+ // Example: 'spec.jobTargetRef.template.spec' becomes obj?.spec?.jobTargetRef?.template?.spec
128+ // The reduce function walks through each key safely with optional chaining at every step
129+ const path = specPath
130+ . split ( '.' )
131+ . reduce ( ( current , key ) => current ?. [ key ] , obj )
132+
133+ if ( path ?. containers ) {
134+ updateImageInContainerArray ( path . containers , imageName , newImage )
135+ }
136+ if ( path ?. initContainers ) {
137+ updateImageInContainerArray ( path . initContainers , imageName , newImage )
138+ }
139+ }
138140
139- if ( currentImageName === imageName ) {
140- return acc + `${ imageKeyword [ 0 ] } ${ imageNameWithNewTag } \n`
141- }
141+ function updateImageInContainerArray (
142+ containers : any [ ] ,
143+ imageName : string ,
144+ newImage : string
145+ ) {
146+ if ( ! Array . isArray ( containers ) ) return
147+ containers . forEach ( ( container ) => {
148+ if (
149+ container . image &&
150+ ( container . image === imageName ||
151+ container . image . startsWith ( imageName + ':' ) ||
152+ container . image . startsWith ( imageName + '@' ) )
153+ ) {
154+ container . image = newImage
142155 }
143-
144- return acc + line + '\n'
145- } , '' )
156+ } )
146157}
147158
148159export function getReplicaCount ( inputObject : any ) : any {
@@ -152,12 +163,17 @@ export function getReplicaCount(inputObject: any): any {
152163 throw InputObjectKindNotDefinedError
153164 }
154165
155- const { kind} = inputObject
156- if (
157- kind . toLowerCase ( ) !== KubernetesWorkload . POD . toLowerCase ( ) &&
158- kind . toLowerCase ( ) !== KubernetesWorkload . DAEMON_SET . toLowerCase ( )
159- )
160- return inputObject . spec . replicas
166+ const kind = inputObject . kind . toLowerCase ( )
167+
168+ const workloadsWithReplicas = new Set ( [
169+ KubernetesWorkload . DEPLOYMENT . toLowerCase ( ) ,
170+ KubernetesWorkload . REPLICASET . toLowerCase ( ) ,
171+ KubernetesWorkload . STATEFUL_SET . toLowerCase ( )
172+ ] )
173+
174+ if ( workloadsWithReplicas . has ( kind ) ) {
175+ return inputObject . spec ?. replicas
176+ }
161177
162178 return 0
163179}
0 commit comments