@@ -2971,6 +2971,7 @@ def main():
29712971 cpu_specified = False
29722972 serial_user_specified = False
29732973 vnc_user_specified = False
2974+ arch_specified = False
29742975
29752976
29762977 script_home = os .path .dirname (os .path .abspath (__file__ ))
@@ -3002,6 +3003,7 @@ def main():
30023003 i += 1
30033004 elif arg == "--arch" :
30043005 config ['arch' ] = args [i + 1 ].lower ()
3006+ arch_specified = True
30053007 i += 1
30063008 elif arg == "--mem" :
30073009 config ['mem' ] = args [i + 1 ]
@@ -3170,19 +3172,21 @@ def main():
31703172 # Fetch release info
31713173 releases_cache = {}
31723174
3173- def get_releases (repo_slug ):
3175+ def get_releases (repo_slug , force_refresh = False ):
31743176 cache_name = "{}-releases.json" .format (repo_slug .replace ("/" , "_" ))
31753177 cache_path = os .path .join (working_dir_os , cache_name )
3176- if repo_slug in releases_cache :
3178+ if not force_refresh and repo_slug in releases_cache :
31773179 return releases_cache [repo_slug ]
3178- if os .path .exists (cache_path ):
3180+ if not force_refresh and os .path .exists (cache_path ):
31793181 try :
31803182 with open (cache_path , 'r' ) as f :
31813183 releases_cache [repo_slug ] = json .load (f )
31823184 return releases_cache [repo_slug ]
31833185 except ValueError :
31843186 pass
31853187
3188+ debuglog (config ['debug' ], "Fetching fresh releases for {} (force_refresh={})" .format (repo_slug , force_refresh ))
3189+
31863190 gh_headers = {
31873191 "Accept" : "application/vnd.github+json" ,
31883192 }
@@ -3246,6 +3250,29 @@ def get_releases(repo_slug):
32463250 debuglog (config ['debug' ], "Candidate URL (xz) exists!" )
32473251 use_this_builder = True
32483252 found_zst_link = candidate_url_xz
3253+ elif config ['arch' ] == "aarch64" and not arch_specified :
3254+ # Fallback to x86_64 if aarch64 failed and arch was not user-specified
3255+ log ("No aarch64 image found for {} {} in {}. Trying x86_64 fallback..." .format (config ['os' ], config ['release' ], search_repo ))
3256+ config ['arch' ] = "" # Empty string means x86_64 in anyvm
3257+ # Sync VM arch for debug logging later
3258+ debuglog (config ['debug' ], "Fallback to x86_64 due to missing aarch64 asset" )
3259+ target_zst_fallback = "{}-{}.qcow2.zst" .format (config ['os' ], config ['release' ])
3260+ candidate_url_fallback = "https://github.com/{}/releases/download/{}/{}" .format (search_repo , tag , target_zst_fallback )
3261+ debuglog (config ['debug' ], "Checking fallback x86_64 URL: {}" .format (candidate_url_fallback ))
3262+ if check_url_exists (candidate_url_fallback , config ['debug' ]):
3263+ debuglog (config ['debug' ], "Fallback x86_64 URL exists!" )
3264+ use_this_builder = True
3265+ found_zst_link = candidate_url_fallback
3266+ else :
3267+ target_xz_fallback = target_zst_fallback .replace ('.zst' , '.xz' )
3268+ candidate_url_xz_fallback = "https://github.com/{}/releases/download/{}/{}" .format (search_repo , tag , target_xz_fallback )
3269+ debuglog (config ['debug' ], "Checking fallback x86_64 URL (xz): {}" .format (candidate_url_xz_fallback ))
3270+ if check_url_exists (candidate_url_xz_fallback , config ['debug' ]):
3271+ debuglog (config ['debug' ], "Fallback x86_64 URL (xz) exists!" )
3272+ use_this_builder = True
3273+ found_zst_link = candidate_url_xz_fallback
3274+ else :
3275+ debuglog (config ['debug' ], "Candidate URL not found (including fallback), falling back to full search" )
32493276 else :
32503277 debuglog (config ['debug' ], "Candidate URL not found, falling back to full search" )
32513278 else :
@@ -3292,41 +3319,60 @@ def get_releases(repo_slug):
32923319 target_tag = config ['builder' ]
32933320 if not target_tag .startswith ('v' ):
32943321 target_tag = "v" + target_tag
3322+
3323+ def filter_releases (data , tag ):
3324+ return [r for r in data if r .get ('tag_name' ) == tag ]
3325+
32953326 debuglog (config ['debug' ], "Filtering releases for tag: {}" .format (target_tag ))
3296- releases_data = [r for r in releases_data if r .get ('tag_name' ) == target_tag ]
3327+ filtered = filter_releases (releases_data , target_tag )
3328+
3329+ if not filtered :
3330+ debuglog (config ['debug' ], "Builder version {} not found in cache. Refreshing..." .format (target_tag ))
3331+ releases_data = get_releases (builder_repo , force_refresh = True )
3332+ if not releases_data :
3333+ fatal ("Unsupported OS: {}. Builder repository {} not found or inaccessible." .format (config ['os' ], builder_repo ))
3334+ filtered = filter_releases (releases_data , target_tag )
3335+
3336+ releases_data = filtered
32973337 if not releases_data :
3298- fatal ("Builder version {} not found in repository {}." .format (target_tag , builder_repo ))
3338+ fatal ("Builder version {} not found in repository {} even after refresh ." .format (target_tag , builder_repo ))
32993339 else :
33003340 releases_data = []
33013341
33023342 published_at = ""
33033343 # Find release version if not provided
33043344 if not config ['release' ]:
3305- for r in releases_data :
3306- #log(r)
3307- for asset in r .get ('assets' , []):
3308- u = asset .get ('browser_download_url' , '' )
3309- #log(u)
3310- if u .endswith ("qcow2.zst" ) or u .endswith ("qcow2.xz" ):
3311- if config ['arch' ] and config ['arch' ] != "x86_64" and config ['arch' ] not in u :
3312- continue
3313- # Extract version roughly
3314- filename = u .split ('/' )[- 1 ]
3315- filename = removesuffix (filename , ".qcow2.zst" )
3316- filename = removesuffix (filename , ".qcow2.xz" )
3317- parts = filename .split ('-' )
3318- if len (parts ) > 1 :
3319- ver = parts [1 ]
3320- debuglog (config ['debug' ], "Candidate release found: {} from asset {}" .format (ver , filename ))
3321- if published_at and published_at > r .get ('published_at' , '' ):
3345+ def find_latest_release (data , arch ):
3346+ p_at = ""
3347+ found_v = ""
3348+ for r in data :
3349+ for asset in r .get ('assets' , []):
3350+ u = asset .get ('browser_download_url' , '' )
3351+ if u .endswith ("qcow2.zst" ) or u .endswith ("qcow2.xz" ):
3352+ if arch and arch != "x86_64" and arch not in u :
33223353 continue
3323- if not published_at :
3324- published_at = r .get ('published_at' , '' )
3325- config ['release' ] = ver
3326- elif cmp_version (ver , config ['release' ]) > 0 :
3327- published_at = r .get ('published_at' , '' )
3328- config ['release' ] = ver
3329- debuglog (config ['debug' ],"Updated latest release to: " + config ['release' ])
3354+ filename = u .split ('/' )[- 1 ]
3355+ filename = removesuffix (filename , ".qcow2.zst" )
3356+ filename = removesuffix (filename , ".qcow2.xz" )
3357+ parts = filename .split ('-' )
3358+ if len (parts ) > 1 :
3359+ ver = parts [1 ]
3360+ debuglog (config ['debug' ], "Candidate release found: {} from asset {}" .format (ver , filename ))
3361+ if p_at and p_at > r .get ('published_at' , '' ):
3362+ continue
3363+ if not p_at or cmp_version (ver , found_v ) > 0 :
3364+ p_at = r .get ('published_at' , '' )
3365+ found_v = ver
3366+ return found_v , p_at
3367+
3368+ config ['release' ], published_at = find_latest_release (releases_data , config ['arch' ])
3369+
3370+ if not config ['release' ] and config ['arch' ] == "aarch64" and not arch_specified :
3371+ debuglog (config ['debug' ], "No aarch64 release found, searching for x86_64 fallback release..." )
3372+ config ['release' ], published_at = find_latest_release (releases_data , "" )
3373+ if config ['release' ]:
3374+ log ("No aarch64 release found for {}. Falling back to x86_64." .format (config ['os' ]))
3375+ config ['arch' ] = ""
33303376
33313377
33323378 log ("Using release: " + config ['release' ])
@@ -3360,6 +3406,26 @@ def find_image_link(releases, target_zst, target_xz):
33603406 releases_data = repo_releases
33613407 zst_link = link
33623408 break
3409+
3410+ # If still no link and we are on aarch64 and it wasn't specified, fallback to x86_64 full search
3411+ if not zst_link and config ['arch' ] == "aarch64" and not arch_specified :
3412+ log ("No aarch64 image found in any repository. Trying x86_64 fallback search..." )
3413+ config ['arch' ] = "" # x86_64
3414+ target_zst_fallback = "{}-{}.qcow2.zst" .format (config ['os' ], config ['release' ])
3415+ target_xz_fallback = "{}-{}.qcow2.xz" .format (config ['os' ], config ['release' ])
3416+
3417+ searched = set ()
3418+ for repo in search_repos :
3419+ if repo in searched :
3420+ continue
3421+ searched .add (repo )
3422+ repo_releases = releases_data if repo == builder_repo else get_releases (repo )
3423+ link = find_image_link (repo_releases , target_zst_fallback , target_xz_fallback )
3424+ if link :
3425+ builder_repo = repo
3426+ releases_data = repo_releases
3427+ zst_link = link
3428+ break
33633429
33643430 if not zst_link :
33653431 fatal ("Cannot find the image link." )
0 commit comments