|
2 | 2 | # coding: utf-8
|
3 | 3 |
|
4 | 4 | import os
|
| 5 | +import random |
5 | 6 | import platform
|
6 | 7 | import sys
|
7 | 8 | import urllib.request
|
8 | 9 | import json
|
9 | 10 | import hashlib
|
10 | 11 |
|
11 |
| -INDEX_URL = os.getenv('ASDF_ZIG_INDEX_URL', 'https://ziglang.org/download/index.json') |
12 |
| -HTTP_TIMEOUT = int(os.getenv('ASDF_ZIG_HTTP_TIMEOUT', '30')) |
| 12 | +INDEX_URL = os.getenv("ASDF_ZIG_INDEX_URL", "https://ziglang.org/download/index.json") |
| 13 | +HTTP_TIMEOUT = int(os.getenv("ASDF_ZIG_HTTP_TIMEOUT", "30")) |
13 | 14 |
|
| 15 | +# https://github.com/mlugg/setup-zig/blob/main/mirrors.json |
| 16 | +# If any of these mirrors are down, please open an issue! |
| 17 | +MIRRORS = [ |
| 18 | + "https://pkg.machengine.org/zig", |
| 19 | + "https://zigmirror.hryx.net/zig", |
| 20 | + "https://zig.linus.dev/zig", |
| 21 | + "https://fs.liujiacai.net/zigbuilds", |
| 22 | +] |
14 | 23 | OS_MAPPING = {
|
15 |
| - 'darwin': 'macos', |
| 24 | + "darwin": "macos", |
16 | 25 | }
|
17 | 26 | ARCH_MAPPING = {
|
18 |
| - 'i386': 'x86', |
19 |
| - 'i686': 'x86', |
20 |
| - 'amd64': 'x86_64', |
21 |
| - 'arm64':'aarch64', |
| 27 | + "i386": "x86", |
| 28 | + "i686": "x86", |
| 29 | + "amd64": "x86_64", |
| 30 | + "arm64": "aarch64", |
22 | 31 | }
|
23 | 32 |
|
| 33 | + |
24 | 34 | def fetch_index():
|
25 | 35 | with urllib.request.urlopen(INDEX_URL, timeout=HTTP_TIMEOUT) as response:
|
26 | 36 | if response.getcode() == 200:
|
27 |
| - body = response.read().decode('utf-8') |
| 37 | + body = response.read().decode("utf-8") |
28 | 38 | return json.loads(body)
|
29 | 39 | else:
|
30 | 40 | raise Exception(f"Fetch index.json error: {response.getcode()}")
|
31 | 41 |
|
32 | 42 |
|
33 | 43 | def all_versions():
|
34 | 44 | index = fetch_index()
|
35 |
| - versions = [k for k in index.keys() if k != 'master'] |
36 |
| - versions.sort(key=lambda v: tuple(map(int, v.split('.')))) |
| 45 | + versions = [k for k in index.keys() if k != "master"] |
| 46 | + versions.sort(key=lambda v: tuple(map(int, v.split(".")))) |
37 | 47 | return versions
|
38 | 48 |
|
39 | 49 |
|
40 |
| -def download_tarball(url, out_file, expected_shasum): |
| 50 | +def download_and_check(url, out_file, expected_shasum): |
| 51 | + print(f"Download tarball from {url} to {out_file}...") |
41 | 52 | chunk_size = 8192 # 8KB chunks
|
42 | 53 | sha256_hash = hashlib.sha256()
|
43 | 54 | with urllib.request.urlopen(url, timeout=HTTP_TIMEOUT) as response:
|
44 | 55 | if response.getcode() != 200:
|
45 | 56 | raise Exception(f"Fetch index.json error: {response.getcode()}")
|
46 | 57 |
|
47 |
| - with open(out_file, 'wb') as f: |
48 |
| - while True: |
49 |
| - chunk = response.read(chunk_size) |
50 |
| - if not chunk: |
51 |
| - break # eof |
52 |
| - sha256_hash.update(chunk) |
53 |
| - f.write(chunk) |
| 58 | + with open(out_file, "wb") as f: |
| 59 | + while True: |
| 60 | + chunk = response.read(chunk_size) |
| 61 | + if not chunk: |
| 62 | + break # eof |
| 63 | + sha256_hash.update(chunk) |
| 64 | + f.write(chunk) |
54 | 65 |
|
55 | 66 | actual = sha256_hash.hexdigest()
|
56 | 67 | if actual != expected_shasum:
|
57 |
| - raise Exception(f"Shasum not match, expected:{expected_shasum}, actual:{actual}") |
| 68 | + raise Exception( |
| 69 | + f"Shasum not match, expected:{expected_shasum}, actual:{actual}" |
| 70 | + ) |
| 71 | + |
| 72 | + |
| 73 | +def download_tarball(url, out_file, expected_shasum): |
| 74 | + filename = url.split("/")[-1] |
| 75 | + random.shuffle(MIRRORS) |
| 76 | + urls = [f"{mirror}/{filename}" for mirror in MIRRORS] |
| 77 | + |
| 78 | + for url in urls: |
| 79 | + try: |
| 80 | + download_and_check(url, out_file, expected_shasum) |
| 81 | + return |
| 82 | + except Exception as e: |
| 83 | + print(f"Current mirror failed, try next. err:{e}") |
| 84 | + |
| 85 | + # All mirror failed, fallback to original url |
| 86 | + download_and_check(url, out_file, expected_shasum) |
| 87 | + |
58 | 88 |
|
59 | 89 | def download(version, out_file):
|
60 | 90 | index = fetch_index()
|
61 |
| - if version in index is False: |
62 |
| - raise Exception(f'There is no such version: {version}') |
| 91 | + if version not in index: |
| 92 | + raise Exception(f"There is no such version: {version}") |
63 | 93 |
|
64 | 94 | links = index[version]
|
65 | 95 | os_name = platform.system().lower()
|
66 | 96 | arch = platform.machine().lower()
|
67 | 97 | os_name = OS_MAPPING.get(os_name, os_name)
|
68 | 98 | arch = ARCH_MAPPING.get(arch, arch)
|
69 |
| - link_key = f'{arch}_{os_name}' |
70 |
| - if link_key in links is False: |
71 |
| - raise Exception(f'No tarball link for {link_key}') |
| 99 | + link_key = f"{arch}-{os_name}" |
| 100 | + if link_key not in links: |
| 101 | + raise Exception(f"No tarball link for {link_key}") |
| 102 | + |
| 103 | + tarball_url = links[link_key]["tarball"] |
| 104 | + tarball_shasum = links[link_key]["shasum"] |
| 105 | + download_tarball(tarball_url, out_file, tarball_shasum) |
72 | 106 |
|
73 |
| - tarball_url = link_key[link_key]['tarball'] |
74 |
| - tarball_shasum = link_key[link_key]['shasum'] |
75 |
| - download_tarball(url ,out_file, tarball_shasum) |
76 | 107 |
|
77 | 108 | def main(args):
|
78 |
| - command = args[0] if args else 'default' |
79 |
| - if command == 'all-versions': |
| 109 | + command = args[0] if args else "default" |
| 110 | + if command == "all-versions": |
80 | 111 | versions = all_versions()
|
81 | 112 | print(" ".join(versions))
|
82 |
| - elif command == 'latest-version': |
| 113 | + elif command == "latest-version": |
83 | 114 | versions = all_versions()
|
84 | 115 | print(versions[-1])
|
85 |
| - elif command == 'download': |
| 116 | + elif command == "download": |
86 | 117 | download(args[1], args[2])
|
87 | 118 | else:
|
88 | 119 | print(f"Unknown command: {command}")
|
89 | 120 | sys.exit(1)
|
90 | 121 |
|
| 122 | + |
91 | 123 | if __name__ == "__main__":
|
92 | 124 | main(sys.argv[1:])
|
0 commit comments