@@ -177,13 +177,22 @@ def generate_tags(
177177
178178 return tags
179179
180- def build (self , tag : str , platform : Optional [str ] = None , version : Optional [str ] = None ) -> bool :
180+ def build (
181+ self ,
182+ tag : str ,
183+ platform : Optional [str ] = None ,
184+ version : Optional [str ] = None ,
185+ push : bool = False ,
186+ additional_tags : Optional [list [str ]] = None ,
187+ ) -> bool :
181188 """Build Docker image with the specified tag.
182189
183190 Args:
184191 tag: Image tag to build
185192 platform: Target platform (defaults to detected platform)
186193 version: Version to pass as build arg (will be set as BUILD_VERSION env var in container)
194+ push: If True, push directly to registry (preserves manifest metadata)
195+ additional_tags: Additional tags to apply during build (only used with push=True)
187196 """
188197 # Use detected platform if not specified
189198 build_platform = platform or self .platform
@@ -205,6 +214,8 @@ def build(self, tag: str, platform: Optional[str] = None, version: Optional[str]
205214 print (f"INFO: Target platform: { build_platform } " , file = sys .stderr )
206215 if version :
207216 print (f"INFO: Build version: { version } " , file = sys .stderr )
217+ if push :
218+ print (f"INFO: Build mode: push directly to registry (preserves manifest)" , file = sys .stderr )
208219
209220 os .chdir (self .project_root )
210221
@@ -215,13 +226,28 @@ def build(self, tag: str, platform: Optional[str] = None, version: Optional[str]
215226 if version :
216227 build_cmd .extend (["--build-arg" , f"VERSION={ version } " ])
217228
218- # Use --load to load the image into Docker (instead of just building to cache)
219- build_cmd .extend (["--load" , "--tag" , tag , "." ])
229+ # Add primary tag
230+ build_cmd .extend (["--tag" , tag ])
231+
232+ # Add additional tags if provided (only relevant for push mode)
233+ if additional_tags :
234+ for additional_tag in additional_tags :
235+ build_cmd .extend (["--tag" , additional_tag ])
236+
237+ # Use --push for registry builds (preserves manifest metadata)
238+ # Use --load for local builds (loads into local Docker daemon)
239+ if push :
240+ build_cmd .extend (["--push" , "." ])
241+ else :
242+ build_cmd .extend (["--load" , "." ])
220243
221244 result = self ._run_command (build_cmd , check = False )
222245
223246 if result .returncode == 0 :
224247 print (f"INFO: Successfully built: { tag } " , file = sys .stderr )
248+ if additional_tags :
249+ for additional_tag in additional_tags :
250+ print (f"INFO: Successfully tagged: { additional_tag } " , file = sys .stderr )
225251 return True
226252 else :
227253 print (f"ERROR: Failed to build image" , file = sys .stderr )
@@ -269,6 +295,10 @@ def build_and_push(self, version: str, include_latest: bool = True, arch_specifi
269295
270296 Supports architecture-specific builds. Builds for the current platform by default.
271297 Use arch_specific=True to include architecture suffix in tags (e.g., v1.0.0-arm64).
298+
299+ Uses docker buildx build --push to build and push in a single step, which preserves
300+ the proper manifest with architecture metadata. This is required for the validation
301+ step to detect the image architecture.
272302 """
273303 if not self ._check_docker ():
274304 return False
@@ -289,23 +319,18 @@ def build_and_push(self, version: str, include_latest: bool = True, arch_specifi
289319 print (f"INFO: Generated { len (tags )} image tags:" , file = sys .stderr )
290320 for ref in tags :
291321 print (f"INFO: - { ref .uri } " , file = sys .stderr )
322+ print ("" , file = sys .stderr )
292323
293- # Build with first tag, passing version as build arg
324+ # Build and push with all tags in a single buildx command
325+ # This preserves the proper manifest with architecture metadata
294326 primary_tag = tags [0 ].uri
295- if not self .build (primary_tag , version = version ):
296- return False
297-
298- # Tag with additional tags
299- for ref in tags [1 :]:
300- if not self .tag (primary_tag , ref .uri ):
301- return False
327+ additional_tags = [ref .uri for ref in tags [1 :]]
302328
303- # Push all tags
304- for ref in tags :
305- if not self .push (ref .uri ):
306- return False
329+ if not self .build (primary_tag , version = version , push = True , additional_tags = additional_tags ):
330+ return False
307331
308- print (f"INFO: Docker push completed successfully" , file = sys .stderr )
332+ print ("" , file = sys .stderr )
333+ print (f"INFO: Docker build and push completed successfully" , file = sys .stderr )
309334 print (f"INFO: Pushed { len (tags )} tags to registry: { self .registry } " , file = sys .stderr )
310335
311336 # Output the primary image URI for capture by CI
0 commit comments