@@ -120,14 +120,22 @@ def build(self, common_config: CommonConfig, strategy_config: HybridStrategyConf
120120 cr_repo_name = self ._prepare_cr_config (strategy_config .cr_repo_name , common_config .agent_name )
121121 if cr_repo_name != strategy_config .cr_repo_name :
122122 config_updates .add ('cr_repo_name' , cr_repo_name )
123-
123+
124124 should_push , reason = self ._should_push_to_cr (strategy_config , cr_repo_name )
125125 if should_push :
126- cr_updates = self ._handle_cr_push (result , strategy_config , cr_repo_name )
126+ cr_updates = self ._handle_cr_push (common_config , result , strategy_config , cr_repo_name )
127127 config_updates .merge (cr_updates )
128128 else :
129129 self ._report_cr_skip_reason (reason , strategy_config )
130-
130+
131+ # Persist local build metadata
132+ if result .build_timestamp :
133+ config_updates .add ('build_timestamp' , result .build_timestamp .isoformat ())
134+ if result .image :
135+ config_updates .add ('full_image_name' , result .image .full_name )
136+ if getattr (result .image , 'digest' , None ):
137+ config_updates .add ('image_id' , result .image .digest )
138+
131139 result .config_updates = config_updates if config_updates .has_updates () else None
132140 return result
133141
@@ -235,21 +243,28 @@ def _should_push_to_cr(self, strategy_config: HybridStrategyConfig, cr_repo_name
235243 Returns:
236244 Tuple of (should_push: bool, reason: str).
237245 """
246+ # Validate instance name
238247 if not strategy_config .cr_instance_name :
239248 return False , "CR instance name is empty"
240-
241249 if strategy_config .cr_instance_name == AUTO_CREATE_VE :
242250 return False , "CR instance name is 'Auto'"
243-
244- if '{{' in strategy_config .cr_instance_name :
251+ if '{{' in (strategy_config .cr_instance_name or '' ):
245252 return False , "CR instance name contains unrendered template variables"
246-
253+
254+ # Validate namespace name
255+ if not strategy_config .cr_namespace_name :
256+ return False , "CR namespace name is empty"
257+ if strategy_config .cr_namespace_name == AUTO_CREATE_VE :
258+ return False , "CR namespace name is 'Auto'"
259+ if '{{' in (strategy_config .cr_namespace_name or '' ):
260+ return False , "CR namespace name contains unrendered template variables"
261+
247262 if not cr_repo_name :
248263 return False , "CR repository name is empty"
249264
250265 return True , ""
251266
252- def _handle_cr_push (self , result : BuildResult , strategy_config : HybridStrategyConfig ,
267+ def _handle_cr_push (self , common_config : CommonConfig , result : BuildResult , strategy_config : HybridStrategyConfig ,
253268 cr_repo_name : str ) -> 'ConfigUpdates' :
254269 """
255270 Handle pushing image to Container Registry.
@@ -272,17 +287,48 @@ def _handle_cr_push(self, result: BuildResult, strategy_config: HybridStrategyCo
272287
273288 config_updates = ConfigUpdates ()
274289
275- local_image = result .image .full_name if result .image else None
276- if not local_image :
290+ # Use image_id (SHA) from build result for pushing
291+ image_id = result .image .digest if result .image else None
292+ if not image_id :
277293 return config_updates
278-
279- cr_image_url = self ._push_to_cr (
280- local_image ,
281- strategy_config .cr_instance_name ,
282- strategy_config .cr_namespace_name ,
283- cr_repo_name ,
284- strategy_config .image_tag
294+
295+ # Ensure CR resources exist (instance/namespace/repo)
296+ cr_cfg = CRServiceConfig (
297+ instance_name = strategy_config .cr_instance_name ,
298+ namespace_name = strategy_config .cr_namespace_name ,
299+ repo_name = cr_repo_name
300+ )
301+ cr_service = CRService (reporter = self .reporter )
302+ ensure_result = cr_service .ensure_cr_resources (cr_cfg , common_config = common_config )
303+ if not ensure_result .success :
304+ raise Exception (ensure_result .error or "Failed to ensure CR resources" )
305+
306+ # Ensure public endpoint is enabled for CR instance
307+ public_result = cr_service .ensure_public_endpoint (cr_cfg )
308+ if not public_result .success :
309+ raise Exception (public_result .error or "Failed to enable CR public endpoint" )
310+
311+ # Write back any auto-created names to config
312+ if ensure_result .instance_name and ensure_result .instance_name != strategy_config .cr_instance_name :
313+ config_updates .add ('cr_instance_name' , ensure_result .instance_name )
314+ if ensure_result .namespace_name and ensure_result .namespace_name != strategy_config .cr_namespace_name :
315+ config_updates .add ('cr_namespace_name' , ensure_result .namespace_name )
316+ if ensure_result .repo_name and ensure_result .repo_name != cr_repo_name :
317+ config_updates .add ('cr_repo_name' , ensure_result .repo_name )
318+
319+ # Push image (inline call to CRService.login_and_push_image)
320+ self .reporter .info (
321+ f"Pushing image to CR: { cr_cfg .instance_name } /{ cr_cfg .namespace_name } /{ cr_cfg .repo_name } :{ strategy_config .image_tag } "
285322 )
323+ success , push_result = cr_service .login_and_push_image (
324+ cr_config = cr_cfg ,
325+ image_id = image_id ,
326+ image_tag = strategy_config .image_tag ,
327+ namespace = cr_cfg .namespace_name
328+ )
329+ if not success :
330+ raise Exception (f"Image push failed: { push_result } " )
331+ cr_image_url = push_result
286332
287333 config_updates .add ('cr_image_full_url' , cr_image_url )
288334
@@ -296,14 +342,14 @@ def _handle_cr_push(self, result: BuildResult, strategy_config: HybridStrategyCo
296342
297343 def _report_cr_skip_reason (self , reason : str , strategy_config : HybridStrategyConfig ) -> None :
298344 """Report reason for skipping CR push."""
299- if '{{' in strategy_config .cr_instance_name :
300- self .reporter .warning (f"⚠️ CR instance name contains unrendered template variables: { strategy_config . cr_instance_name } " )
301- self .reporter .warning ("⚠️ Ensure volcenginesdkcore is installed to render template variables " )
345+ if '{{' in ( strategy_config .cr_instance_name or '' ) or '{{' in ( strategy_config . cr_namespace_name or '' ) :
346+ self .reporter .warning (" CR names contain unrendered template variables" )
347+ self .reporter .warning ("Ensure Volcengine AK/SK are configured and STS can fetch account_id for template rendering. " )
302348 elif strategy_config .cr_instance_name == AUTO_CREATE_VE :
303- self .reporter .warning ("⚠️ CR instance name is 'Auto', skipping push to CR" )
304- self .reporter .warning ("⚠️ Use 'agentkit config' to configure a valid CR instance name" )
349+ self .reporter .warning ("CR instance name is 'Auto', skipping push to CR" )
350+ self .reporter .warning ("Use 'agentkit config' to configure a valid CR instance name" )
305351 else :
306- self .reporter .warning (f"⚠️ Invalid CR configuration, skipping push to CR: { reason } " )
352+ self .reporter .warning (f"Invalid CR configuration, skipping push to CR: { reason } " )
307353
308354 def _validate_cr_image_url (self , strategy_config : HybridStrategyConfig ) -> DeployResult :
309355 """
@@ -316,42 +362,54 @@ def _validate_cr_image_url(self, strategy_config: HybridStrategyConfig) -> Deplo
316362 if image_url :
317363 return DeployResult (success = True )
318364
319- if '{{' in strategy_config .cr_instance_name :
365+ from agentkit .toolkit .errors import ErrorCode
366+
367+ if '{{' in (strategy_config .cr_instance_name or '' ) or '{{' in (strategy_config .cr_namespace_name or '' ):
320368 error_msg = (
321- f"CR instance name contains unrendered template variables: { strategy_config .cr_instance_name } \n "
322- f"Ensure volcenginesdkcore is installed to render template variables."
369+ "CR names contain unrendered template variables. Ensure Volcengine AK/SK are configured and STS can fetch account_id."
323370 )
371+ error_code = ErrorCode .CONFIG_INVALID
324372 elif strategy_config .cr_instance_name == AUTO_CREATE_VE or not strategy_config .cr_instance_name :
325373 error_msg = (
326374 f"Hybrid mode requires valid CR configuration. Current cr_instance_name='{ strategy_config .cr_instance_name } ' is invalid.\n "
327375 f"Use 'agentkit config' to configure a valid CR instance name, or switch to local/cloud mode."
328376 )
377+ error_code = ErrorCode .CONFIG_INVALID
329378 else :
330379 error_msg = (
331380 "CR image URL not found. Run 'agentkit build' to build and push the image to CR."
332381 )
382+ error_code = ErrorCode .RESOURCE_NOT_FOUND
333383
334384 return DeployResult (
335385 success = False ,
336386 error = error_msg ,
337- error_code = "INVALID_IMAGE_URL"
387+ error_code = error_code
338388 )
339389
340390 def _to_builder_config (self , common_config : CommonConfig ,
341391 strategy_config : HybridStrategyConfig ) -> LocalDockerBuilderConfig :
342392 """
343- Convert HybridVeAgentkitConfig to LocalDockerBuilderConfig.
393+ Convert HybridStrategyConfig to LocalDockerBuilderConfig.
344394 """
395+ # Retrieve Docker build config from manager (contains CLI runtime options)
396+ docker_build_config = None
397+ if self .config_manager :
398+ try :
399+ docker_build_config = self .config_manager .get_docker_build_config ()
400+ except Exception :
401+ docker_build_config = None
345402 return LocalDockerBuilderConfig (
346403 common_config = common_config ,
347404 image_name = common_config .agent_name or "agentkit-app" ,
348- image_tag = strategy_config .image_tag
405+ image_tag = strategy_config .image_tag ,
406+ docker_build_config = docker_build_config
349407 )
350408
351409 def _to_runner_config (self , common_config : CommonConfig ,
352410 strategy_config : HybridStrategyConfig ) -> VeAgentkitRunnerConfig :
353411 """
354- Convert HybridVeAgentkitConfig to VeAgentkitRunnerConfig.
412+ Convert HybridStrategyConfig to VeAgentkitRunnerConfig.
355413 """
356414 merged_envs = merge_runtime_envs (common_config , strategy_config .to_dict ())
357415
@@ -367,46 +425,4 @@ def _to_runner_config(self, common_config: CommonConfig,
367425 image_url = strategy_config .cr_image_full_url
368426 )
369427
370- def _push_to_cr (self , local_image : str , cr_instance : str ,
371- cr_namespace : str , cr_repo : str , tag : str ) -> str :
372- """
373- Push local image to Container Registry.
374-
375- Args:
376- local_image: Local image name (e.g., agentkit-app:v1.0).
377- cr_instance: CR instance name.
378- cr_namespace: CR namespace.
379- cr_repo: CR repository name.
380- tag: Image tag.
381-
382- Returns:
383- Full CR image URL.
384-
385- Raises:
386- Exception: If image push fails. Exceptions are handled by Executor.
387- """
388- cr_config = CRServiceConfig (
389- instance_name = cr_instance ,
390- namespace_name = cr_namespace ,
391- repo_name = cr_repo
392- )
393-
394- cr_service = CRService (reporter = self .reporter )
395-
396- import docker
397- client = docker .from_env ()
398- image = client .images .get (local_image )
399- image_id = image .id
400-
401- self .reporter .info (f"Pushing image to CR: { cr_instance } /{ cr_namespace } /{ cr_repo } :{ tag } " )
402- success , result = cr_service .login_and_push_image (
403- cr_config = cr_config ,
404- image_id = image_id ,
405- image_tag = tag ,
406- namespace = cr_namespace
407- )
408-
409- if not success :
410- raise Exception (f"Image push failed: { result } " )
411-
412- return result
428+ # _push_to_cr removed: logic is handled inline within _handle_cr_push
0 commit comments