@@ -224,7 +224,7 @@ class Console:
224224
225225 def __init__ (self ):
226226 self ._last_is_space = False
227- self ._partial_msg = ""
227+ self ._partial_msg = None
228228
229229 def title (self , text ):
230230 LOG .info ("Console title: [%s]" , text )
@@ -259,22 +259,27 @@ def print_log(self, log_path: pathlib.Path) -> None:
259259 self ._last_is_space = True
260260
261261 @contextlib .contextmanager
262- def partial (self ):
262+ def start_partial (self ):
263263 print (self .MARGIN , end = "" )
264264
265- def console_partial (text ):
266- print (text , end = "" )
267- sys .stdout .flush ()
268- self ._partial_msg += text
269-
265+ if self ._partial_msg is not None :
266+ raise ValueError ("Console partial is already started." )
267+ self ._partial_msg = ""
270268 try :
271- yield console_partial
269+ yield self . partial
272270 finally :
273271 print ("" )
274272 LOG .info ("Console message: [%s]" , self ._partial_msg )
275- self ._partial_msg = ""
273+ self ._partial_msg = None
276274 self ._last_is_space = False
277275
276+ def partial (self , text ):
277+ if self ._partial_msg is None :
278+ raise ValueError ("Console partial has not been started." )
279+ print (text , end = "" )
280+ sys .stdout .flush ()
281+ self ._partial_msg += text
282+
278283 @contextlib .contextmanager
279284 def tee (self , file_path , append = False ):
280285 tee_lines = ["" if append else None ]
@@ -797,7 +802,7 @@ def execute(self, args):
797802 action_fail_exception = None
798803 action_fail_step = None
799804 for step in action_steps :
800- with CONSOLE .partial () as partial :
805+ with CONSOLE .start_partial () as partial :
801806 partial (f"{ step .label } ... " )
802807 try :
803808 if action_fail_exception :
@@ -1921,22 +1926,42 @@ def create_compose_file(self, action, args, username, password, ssl_cert_file, s
19211926
19221927class TestGenPullImagesStep (Step ):
19231928 label = "Pulling docker images"
1929+ required = False
19241930
19251931 def execute (self , action , args ):
19261932 action .analytics .additional_properties ["pull_timeout" ] = args .pull_timeout
19271933
19281934 try :
1929- action .run_cmd_retries (
1935+ with action .start_cmd (
19301936 "docker" ,
19311937 "compose" ,
19321938 "-f" ,
19331939 action .docker_compose_file_path ,
19341940 "pull" ,
19351941 "--policy" ,
19361942 "always" ,
1937- timeout = args .pull_timeout * 60 ,
1938- retries = TESTGEN_PULL_RETRIES ,
1939- )
1943+ ) as (proc , _ , stderr ):
1944+ complete_re = re .compile (r"^ ([0-9a-f]{12}) (Already exists|Pull complete)" )
1945+ hash_discovery_re = re .compile (r"^ ([0-9a-f]{12}) (Already exists|Pulling fs layer|Waiting)" )
1946+ discovering = True
1947+ hashes : set [str ] = set ()
1948+ completed_count = 0
1949+ reported = 0
1950+ try :
1951+ for line in stderr :
1952+ if disc_match := hash_discovery_re .match (line ):
1953+ hashes .add (disc_match .group (1 ))
1954+ elif hashes and discovering :
1955+ discovering = False
1956+ if complete_re .match (line ):
1957+ completed_count += 1
1958+ if not discovering :
1959+ to_be_reported = list (range (reported , int (completed_count * 100 / len (hashes )) + 1 , 20 ))[1 :]
1960+ for progress in to_be_reported :
1961+ CONSOLE .partial (f"{ progress } % " )
1962+ reported = progress
1963+ except Exception :
1964+ pass
19401965 except CommandFailed :
19411966 # Pulling the images before starting is not mandatory, so we just proceed if it fails
19421967 raise SkipStep
0 commit comments