@@ -3099,6 +3099,8 @@ def deps_from_container(args: argparse.Namespace, cnt: dict) -> set:
30993099async def compose_up (compose : PodmanCompose , args : argparse .Namespace ) -> int | None :
31003100 excluded = get_excluded (compose , args )
31013101
3102+ log .info ("building images: ..." )
3103+
31023104 if not args .no_build :
31033105 # `podman build` does not cache, so don't always build
31043106 build_args = argparse .Namespace (if_not_exists = (not args .build ), ** args .__dict__ )
@@ -3108,8 +3110,11 @@ async def compose_up(compose: PodmanCompose, args: argparse.Namespace) -> int |
31083110 if not args .dry_run :
31093111 return build_exit_code
31103112
3111- hashes = (
3112- (
3113+ # if needed, tear down existing containers
3114+
3115+ existing_containers : dict [str , str | None ] = {
3116+ c ['Names' ][0 ]: c ['Labels' ].get ('io.podman.compose.config-hash' )
3117+ for c in json .loads (
31133118 await compose .podman .output (
31143119 [],
31153120 "ps" ,
@@ -3118,44 +3123,73 @@ async def compose_up(compose: PodmanCompose, args: argparse.Namespace) -> int |
31183123 f"label=io.podman.compose.project={ compose .project_name } " ,
31193124 "-a" ,
31203125 "--format" ,
3121- '{{ index .Labels "io.podman.compose.config-hash"}}' ,
3126+ "json" ,
31223127 ],
31233128 )
31243129 )
3125- .decode ("utf-8" )
3126- .splitlines ()
3127- )
3128- diff_hashes = [i for i in hashes if i and i != compose .yaml_hash ]
3129- if (args .force_recreate and len (hashes ) > 0 ) or len (diff_hashes ):
3130- log .info ("recreating: ..." )
3131- down_args = argparse .Namespace (** dict (args .__dict__ , volumes = False , rmi = None ))
3132- await compose .commands ["down" ](compose , down_args )
3133- log .info ("recreating: done\n \n " )
3134- # args.no_recreate disables check for changes (which is not implemented)
3130+ }
3131+
3132+ if len (existing_containers ) > 0 :
3133+ if args .force_recreate and args .no_recreate :
3134+ log .error (
3135+ "Cannot use --force-recreate and --no-recreate at the same time, "
3136+ "please remove one of them"
3137+ )
3138+ return 1
3139+
3140+ if args .force_recreate :
3141+ teardown_needed = True
3142+ elif args .no_recreate :
3143+ teardown_needed = False
3144+ else :
3145+ # default is to tear down everything if any container is stale
3146+ teardown_needed = (
3147+ len ([h for h in existing_containers .values () if h != compose .yaml_hash ]) > 0
3148+ )
3149+
3150+ if teardown_needed :
3151+ log .info ("tearing down existing containers: ..." )
3152+ down_args = argparse .Namespace (** dict (args .__dict__ , volumes = False , rmi = None ))
3153+ await compose .commands ["down" ](compose , down_args )
3154+ existing_containers = {}
3155+ log .info ("tearing down existing containers: done\n \n " )
31353156
31363157 await create_pods (compose )
3137- exit_code = 0
3158+
3159+ log .info ("creating missing containers: ..." )
3160+
3161+ create_error_codes : list [int | None ] = []
31383162 for cnt in compose .containers :
3139- if cnt ["_service" ] in excluded :
3140- log .debug ("** skipping: %s" , cnt ["name" ])
3163+ if cnt ["_service" ] in excluded or cnt [ "name" ] in existing_containers :
3164+ log .debug ("** skipping create : %s" , cnt ["name" ])
31413165 continue
31423166 podman_args = await container_to_args (compose , cnt , detached = False , no_deps = args .no_deps )
3143- subproc_exit_code = await compose .podman .run ([], "create" , podman_args )
3144- if subproc_exit_code is not None and subproc_exit_code != 0 :
3145- exit_code = subproc_exit_code
3167+ exit_code = await compose .podman .run ([], "create" , podman_args )
3168+ create_error_codes .append (exit_code )
3169+
3170+ if args .dry_run :
3171+ return None
31463172
3147- if not args .no_start and args .detach and subproc_exit_code is not None :
3148- container_exit_code = await run_container (
3173+ if args .no_start :
3174+ # return first error code from create calls, if any
3175+ return next ((code for code in create_error_codes if code is not None and code != 0 ), 0 )
3176+
3177+ if args .detach :
3178+ log .info ("starting containers (detached): ..." )
3179+ start_error_codes : list [int | None ] = []
3180+ for cnt in compose .containers :
3181+ if cnt ["_service" ] in excluded :
3182+ log .debug ("** skipping start: %s" , cnt ["name" ])
3183+ continue
3184+ exit_code = await run_container (
31493185 compose , cnt ["name" ], deps_from_container (args , cnt ), ([], "start" , [cnt ["name" ]])
31503186 )
3187+ start_error_codes .append (exit_code )
31513188
3152- if container_exit_code is not None and container_exit_code != 0 :
3153- exit_code = container_exit_code
3189+ # return first error code from start calls, if any
3190+ return next (( code for code in start_error_codes if code is not None and code != 0 ), 0 )
31543191
3155- if args .dry_run :
3156- return None
3157- if args .no_start or args .detach :
3158- return exit_code
3192+ log .info ("starting containers (attached): ..." )
31593193
31603194 # TODO: handle already existing
31613195 # TODO: if error creating do not enter loop
@@ -3393,6 +3427,7 @@ async def compose_run(compose: PodmanCompose, args: argparse.Namespace) -> None:
33933427 no_build = False ,
33943428 build = None ,
33953429 force_recreate = False ,
3430+ no_recreate = False ,
33963431 no_start = False ,
33973432 no_cache = False ,
33983433 build_arg = [],
0 commit comments