Skip to content

Commit 51fc7b7

Browse files
authored
feat: support zls installation (#23)
1 parent 5d6807b commit 51fc7b7

File tree

6 files changed

+100
-49
lines changed

6 files changed

+100
-49
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
script-test:
1212
env:
1313
ASDF_DOWNLOAD_PATH: /tmp/asdf-download
14-
ASDF_INSTALL_VERSION: 0.14.1
14+
ASDF_INSTALL_VERSION: 0.15.1
1515
ASDF_INSTALL_TYPE: version
1616
ASDF_INSTALL_PATH: /tmp/asdf-install
1717
strategy:
@@ -22,6 +22,7 @@ jobs:
2222
runs-on: ${{ matrix.os }}
2323
steps:
2424
- uses: actions/checkout@v5
25+
- uses: astral-sh/ruff-action@v3
2526
- run: |
2627
bin/download
2728
- run: |

.ruff.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[format]
2+
quote-style = "single"

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
[Zig](http://ziglang.org/) plugin for the [asdf version manager](https://asdf-vm.com).
66

7+
As a bonus, this plugin supports installing zls as well, so zls and zig version can match exactly.
8+
9+
710
</div>
811

912
# Dependencies
@@ -31,8 +34,11 @@ asdf install zig latest
3134
# Set a version globally (on your ~/.tool-versions file)
3235
asdf set --home zig latest
3336

34-
# Now asdf-zig commands are available
37+
# Now zig commands are available
3538
zig version
39+
40+
# Also you can check zls match zig version
41+
zls version
3642
```
3743

3844
Check [asdf](https://github.com/asdf-vm/asdf) readme for more instructions on how to

bin/download

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)
77
download() {
88
local download_path="$1"
99
local version="$2"
10-
local release_file="${download_path}/${version}.tar.xz"
10+
local zig_outfile="${download_path}/zig-${version}.tar.xz"
11+
local zls_outfile="${download_path}/zls-${version}.tar.xz"
1112
mkdir -p "$download_path"
1213

13-
"${SCRIPT_DIR}/../lib/utils.py" download "${version}" "${release_file}"
14-
tar -xf "$release_file" -C "$download_path" --strip-components=1
15-
rm "$release_file"
14+
"${SCRIPT_DIR}/../lib/utils.py" download "${version}" "${zig_outfile}" "${zls_outfile}"
15+
tar -xf "$zig_outfile" -C "$download_path" --strip-components=1
16+
if [ -f "$zls_outfile" ]; then
17+
tar -xf "$zls_outfile" -C "$download_path"
18+
rm "$zls_outfile"
19+
fi
20+
rm "$zig_outfile"
1621
}
1722

1823
download "$ASDF_DOWNLOAD_PATH" "$ASDF_INSTALL_VERSION"

bin/install

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ install_version() {
1616
cp -r "$ASDF_DOWNLOAD_PATH"/* "$install_path"
1717
mkdir -p "$install_path/bin"
1818
mv "$install_path/zig" "$install_path/bin/zig"
19+
if test -x "$install_path/zls"; then
20+
mv "$install_path/zls" "$install_path/bin/zls"
21+
fi
1922

2023
local tool_cmd="zig"
2124
if ! test -x "$install_path/bin/$tool_cmd"; then
@@ -24,6 +27,9 @@ install_version() {
2427
fi
2528

2629
echo "zig $version installation was successful!"
30+
if test -x "$install_path/bin/zls"; then
31+
echo "zls $version also get installed!"
32+
fi
2733
) || (
2834
rm -rf "$install_path"
2935
fail "An error occurred while installing $TOOL_NAME $version."

lib/utils.py

Lines changed: 74 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,69 +12,85 @@
1212
import hashlib
1313
import 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!
2121
MIRRORS = [
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
]
2727
OS_MAPPING = {
28-
"darwin": "macos",
28+
'darwin': 'macos',
2929
}
3030
ARCH_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+
3738
class 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+
4549
def 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+
5358
def 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

5971
def 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

6678
def 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

127158
def 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

Comments
 (0)