1212import hashlib
1313import logging
1414
15- INDEX_URL = os .getenv (" ASDF_ZIG_INDEX_URL" , " https://ziglang.org/download/index.json" )
16- HTTP_TIMEOUT = int (os .getenv (" ASDF_ZIG_HTTP_TIMEOUT" , "30" ))
17- USER_AGENT = " asdf-zig (https://github.com/asdf-community/asdf-zig)"
15+ INDEX_URL = os .getenv (' ASDF_ZIG_INDEX_URL' , ' https://ziglang.org/download/index.json' )
16+ HTTP_TIMEOUT = int (os .getenv (' ASDF_ZIG_HTTP_TIMEOUT' , '30' ))
17+ USER_AGENT = ' asdf-zig (https://github.com/asdf-community/asdf-zig)'
1818
1919# https://github.com/mlugg/setup-zig/blob/main/mirrors.json
2020# If any of these mirrors are down, please open an issue!
2121MIRRORS = [
22- " https://pkg.machengine.org/zig" ,
23- " https://zigmirror.hryx.net/zig" ,
24- " https://zig.linus.dev/zig" ,
25- " https://fs.liujiacai.net/zigbuilds" ,
22+ ' https://pkg.machengine.org/zig' ,
23+ ' https://zigmirror.hryx.net/zig' ,
24+ ' https://zig.linus.dev/zig' ,
25+ ' https://fs.liujiacai.net/zigbuilds' ,
2626]
2727OS_MAPPING = {
28- " darwin" : " macos" ,
28+ ' darwin' : ' macos' ,
2929}
3030ARCH_MAPPING = {
31- " i386" : " x86" ,
32- " i686" : " x86" ,
33- " amd64" : " x86_64" ,
34- " arm64" : " aarch64" ,
31+ ' i386' : ' x86' ,
32+ ' i686' : ' x86' ,
33+ ' amd64' : ' x86_64' ,
34+ ' arm64' : ' aarch64' ,
3535}
3636
37+
3738class HTTPAccessError (Exception ):
3839 def __init__ (self , url , code , reason , body ):
39- super ().__init__ (f"{ url } access failed, code:{ code } , reason:{ reason } , body:{ body } " )
40+ super ().__init__ (
41+ f'{ url } access failed, code:{ code } , reason:{ reason } , body:{ body } '
42+ )
4043 self .url = url
4144 self .code = code
4245 self .reason = reason
4346 self .body = body
4447
48+
4549def http_get (url , timeout = HTTP_TIMEOUT ):
4650 try :
4751 req = urllib .request .Request (url , headers = {'User-Agent' : USER_AGENT })
4852 return urllib .request .urlopen (req , timeout = timeout )
4953 except HTTPError as e :
50- body = e .read ().decode (" utf-8" )
54+ body = e .read ().decode (' utf-8' )
5155 raise HTTPAccessError (url , e .code , e .reason , body )
5256
57+
5358def fetch_index ():
5459 with http_get (INDEX_URL ) as response :
55- body = response .read ().decode ("utf-8" )
60+ body = response .read ().decode ('utf-8' )
61+ return json .loads (body )
62+
63+
64+ def query_zls (zig_version ):
65+ url = f'https://releases.zigtools.org/v1/zls/select-version?zig_version={ zig_version } &compatibility=full'
66+ with http_get (url ) as response :
67+ body = response .read ().decode ('utf-8' )
5668 return json .loads (body )
5769
5870
5971def all_versions ():
6072 index = fetch_index ()
61- versions = [k for k in index .keys () if k != " master" ]
62- versions .sort (key = lambda v : tuple (map (int , v .split ("." ))))
73+ versions = [k for k in index .keys () if k != ' master' ]
74+ versions .sort (key = lambda v : tuple (map (int , v .split ('.' ))))
6375 return versions
6476
6577
6678def download_and_check (url , out_file , expected_shasum , total_size ):
67- logging .info (f" Begin download tarball({ total_size } ) from { url } to { out_file } ..." )
68- chunk_size = 1024 * 1024 # 1M chunks
79+ logging .info (f' Begin download tarball({ total_size } ) from { url } to { out_file } ...' )
80+ chunk_size = 1024 * 1024 # 1M chunks
6981 sha256_hash = hashlib .sha256 ()
7082 with http_get (url ) as response :
7183 read_size = 0
72- with open (out_file , "wb" ) as f :
84+ with open (out_file , 'wb' ) as f :
7385 while True :
7486 chunk = response .read (chunk_size )
7587 read_size += len (chunk )
76- progress_percentage = (read_size / total_size ) * 100 if total_size > 0 else 0
77- logging .info (f'Downloaded: { read_size } /{ total_size } bytes ({ progress_percentage :.2f} %)' )
88+ progress_percentage = (
89+ (read_size / total_size ) * 100 if total_size > 0 else 0
90+ )
91+ logging .info (
92+ f'Downloaded: { read_size } /{ total_size } bytes ({ progress_percentage :.2f} %)'
93+ )
7894 if not chunk :
7995 break # eof
8096 sha256_hash .update (chunk )
@@ -83,62 +99,77 @@ def download_and_check(url, out_file, expected_shasum, total_size):
8399 actual = sha256_hash .hexdigest ()
84100 if actual != expected_shasum :
85101 raise Exception (
86- f" Shasum not match, expected:{ expected_shasum } , actual:{ actual } "
102+ f' Shasum not match, expected:{ expected_shasum } , actual:{ actual } '
87103 )
88104
89105
90- def download_tarball (url , out_file , expected_shasum , total_size ):
91- filename = url .split ("/" )[- 1 ]
106+ def download_tarball (out_file , tarball_info , use_mirror = True ):
107+ url = tarball_info ['tarball' ]
108+ expected_shasum = tarball_info ['shasum' ]
109+ total_size = int (tarball_info ['size' ])
110+
111+ if use_mirror is False :
112+ download_and_check (url , out_file , expected_shasum , total_size )
113+ return
114+
115+ filename = url .split ('/' )[- 1 ]
92116 random .shuffle (MIRRORS )
93117
94118 for mirror in MIRRORS :
95119 try :
96120 # Ensure base_url has a trailing slash
97121 mirror = mirror if mirror .endswith ('/' ) else mirror + '/'
98- download_and_check (urljoin (mirror , filename ), out_file , expected_shasum , total_size )
122+ download_and_check (
123+ urljoin (mirror , filename ), out_file , expected_shasum , total_size
124+ )
99125 return
100126 except Exception as e :
101- logging .error (f" Current mirror failed, try next. err:{ e } " )
127+ logging .error (f' Current mirror failed, try next. err:{ e } ' )
102128
103129 # All mirrors failed, fallback to original url
104130 download_and_check (url , out_file , expected_shasum , total_size )
105131
106132
107- def download (version , out_file ):
133+ def download (version , zig_outfile , zls_outfile ):
108134 index = fetch_index ()
109135 if version not in index :
110- raise Exception (f" There is no such version: { version } " )
136+ raise Exception (f' There is no such version: { version } ' )
111137
112138 links = index [version ]
113139 os_name = platform .system ().lower ()
114140 arch = platform .machine ().lower ()
115141 os_name = OS_MAPPING .get (os_name , os_name )
116142 arch = ARCH_MAPPING .get (arch , arch )
117- link_key = f" { arch } -{ os_name } "
143+ link_key = f' { arch } -{ os_name } '
118144 if link_key not in links :
119- raise Exception (f"No tarball link for { link_key } in { version } " )
145+ raise Exception (f'No tarball link for { link_key } in { version } ' )
146+
147+ tarball_info = links [link_key ]
148+ download_tarball (zig_outfile , tarball_info )
149+
150+ zls_links = query_zls (version )
151+ if link_key not in zls_links :
152+ return
120153
121- tarball_url = links [link_key ]["tarball" ]
122- tarball_shasum = links [link_key ]["shasum" ]
123- tarball_size = int (links [link_key ]["size" ])
124- download_tarball (tarball_url , out_file , tarball_shasum , tarball_size )
154+ tarball_info = zls_links [link_key ]
155+ download_tarball (zls_outfile , tarball_info , use_mirror = False )
125156
126157
127158def main (args ):
128- command = args [0 ] if args else " all-versions"
129- if command == " all-versions" :
159+ command = args [0 ] if args else ' all-versions'
160+ if command == ' all-versions' :
130161 versions = all_versions ()
131- print (" " .join (versions ))
132- elif command == " latest-version" :
162+ print (' ' .join (versions ))
163+ elif command == ' latest-version' :
133164 versions = all_versions ()
134165 print (versions [- 1 ])
135- elif command == " download" :
136- download (args [1 ], args [2 ])
166+ elif command == ' download' :
167+ download (args [1 ], args [2 ], args [ 3 ] )
137168 else :
138- logging .error (f" Unknown command: { command } " )
169+ logging .error (f' Unknown command: { command } ' )
139170 sys .exit (1 )
140171
141172
142- if __name__ == " __main__" :
173+ if __name__ == ' __main__' :
143174 logging .basicConfig (level = logging .INFO , format = '[%(asctime)s] %(message)s' )
144175 main (sys .argv [1 :])
0 commit comments