Skip to content

Commit 9270f34

Browse files
authored
Add Zig Version Manager (zvm) (#1052)
1 parent a8919f9 commit 9270f34

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

modules/zvm/README.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Zig Version Manager (zvm)
2+
3+
Manage several versions of [Zig](https://ziglang.org/) using your favorite shell.
4+
5+
## Dependencies
6+
7+
- [ouch](https://github.com/ouch-org/ouch)
8+
- [minisign](https://github.com/jedisct1/minisign)
9+
10+
## Install and setup
11+
12+
Clone this repo or copy the `zvm.nu` file wherever you prefer to keep your Nushell scripts.
13+
14+
Edit your Nushell config file (`$nu.config-path`) and add the line:
15+
16+
```nu
17+
use `~/.config/nushell/scripts/zvm.nu` *
18+
```
19+
20+
List available Zig versions:
21+
22+
```nu
23+
zvm list
24+
```
25+
26+
**Install** a specific version:
27+
28+
```nu
29+
zvm install master
30+
```
31+
32+
At this point, the nightly build of Zig should be installed in `~/.local/share/zvm/`.
33+
34+
You can then **use** this version:
35+
36+
```nu
37+
zvm use master
38+
```
39+
40+
A symlink to Zig’s binary is created in the zvm folder: `~/.local/share/zvm/zig`.
41+
Add this to your `$env.PATH` to make zig accessible.
42+
43+
## Roadmap
44+
45+
- Windows support
46+
- [zls](https://github.com/zigtools/zls) support

modules/zvm/zvm.nu

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Zig version manager
2+
# - list available Zig versions
3+
# - install tagged and/or nightly versions
4+
# - point symlink to current active version
5+
# - remove versions
6+
7+
# List all available versions of Zig
8+
export def "zvm list" [
9+
--system (-s) # List locally installed versions
10+
] {
11+
if $system == true {
12+
let path_prefix = get_or_create_path_prefix
13+
let current_version = get_current_version $path_prefix
14+
15+
ls --short-names $path_prefix
16+
| where type == dir
17+
| get name
18+
| sort --reverse
19+
| each { |it| { version: $it active: ($it == $current_version) }}
20+
} else {
21+
http get https://ziglang.org/download/index.json | columns
22+
}
23+
}
24+
25+
# Fetch info about specified version
26+
export def "zvm info" [version: string] {
27+
http get https://ziglang.org/download/index.json
28+
| get $version
29+
| select version? date docs stdDocs notes?
30+
}
31+
32+
# Install Zig for the specified version
33+
export def "zvm install" [version: string] {
34+
let version_data = http get https://ziglang.org/download/index.json | get $version
35+
let version_to_install = if $version_data.version? != null { $version_data.version } else { $version }
36+
37+
let local_versions = zvm list --system | get version
38+
if $version_to_install in $local_versions {
39+
error make {
40+
msg: $"Version ($version_to_install) is already installed."
41+
label: {
42+
text: $"Version ($version) is already installed locally"
43+
span: (metadata $version).span
44+
}
45+
}
46+
}
47+
48+
let tarball = $version_data | get $"($nu.os-info.arch)-($nu.os-info.name)" | get tarball
49+
let temp_dir = mktemp --directory --suffix "-zvm"
50+
http get $tarball | save --progress $"($temp_dir)/archive.tar.xz"
51+
verify_signature $temp_dir $tarball
52+
ouch decompress --dir $temp_dir --quiet $"($temp_dir)/archive.tar.xz"
53+
54+
let path_prefix = get_or_create_path_prefix
55+
cp --recursive $"($temp_dir)/zig-($nu.os-info.name)-($nu.os-info.arch)-($version)" $"($path_prefix)/($version)"
56+
rm --recursive --permanent $temp_dir
57+
58+
echo $"Successfully installed Zig ($version)"
59+
}
60+
61+
# Make the zig symlink point to the specified version
62+
export def "zvm use" [
63+
version: string # Version to use. Nightly version is expressed using one of these terms: master, dev or nightly.
64+
] {
65+
let zig_to_use = find_installed_version_or_err $version
66+
if $zig_to_use.active {
67+
error make {
68+
msg: $"Version ($zig_to_use.version) is already in use."
69+
label: {
70+
text: $"Version ($version) is already targeted by symlink"
71+
span: (metadata $version).span
72+
}
73+
}
74+
}
75+
76+
let path_prefix = get_or_create_path_prefix
77+
ln -sf $"($path_prefix)/($zig_to_use.version)/zig" $"($path_prefix)/zig"
78+
echo $"Successfully switched to Zig ($version)"
79+
}
80+
81+
# Remove the specified version
82+
export def "zvm remove" [
83+
version: string # Version to remove. Nightly version is expressed using one of these terms: master, dev or nightly.
84+
] {
85+
let zig_to_remove = find_installed_version_or_err $version
86+
let path_prefix = get_or_create_path_prefix
87+
rm --recursive --permanent $"($path_prefix)/($zig_to_remove.version)"
88+
89+
if $zig_to_remove.active {
90+
rm --permanent $"($path_prefix)/zig"
91+
}
92+
93+
echo $"Successfully removed Zig ($zig_to_remove.version)"
94+
}
95+
96+
def verify_signature [temp_dir: string, tarball: string] {
97+
http get $"($tarball).minisig" | save $"($temp_dir)/archive.tar.xz.minisig"
98+
99+
let minisign_result = minisign -Vm $"($temp_dir)/archive.tar.xz" -P "RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U" | complete
100+
if $minisign_result.exit_code != 0 {
101+
error make {
102+
msg: $"Failed to verify archive signature. Temporary data left as is in ($temp_dir)."
103+
label: {
104+
text: "Received error from minisign verification"
105+
span: (metadata $minisign_result).span
106+
}
107+
}
108+
}
109+
}
110+
111+
def get_or_create_path_prefix [] {
112+
let path_prefix = $"($nu.home-path)/.local/share/zvm"
113+
if not ($path_prefix | path exists) { mkdir $path_prefix }
114+
$path_prefix
115+
}
116+
117+
def get_current_version [path_prefix: string] {
118+
let zig_path = $"($path_prefix)/zig"
119+
if ($zig_path | path exists) {
120+
ls -l $zig_path
121+
| where type == symlink
122+
| get 0.target
123+
| parse $"($path_prefix)/{current_version}/zig"
124+
| get 0.current_version
125+
} else {
126+
null
127+
}
128+
}
129+
130+
def find_installed_version_or_err [version: string] {
131+
let actual_version = match $version {
132+
"master" | "dev" | "nightly" => (zvm info master | get version)
133+
_ => $version
134+
}
135+
136+
let version_search_result = zvm list --system | where version == $actual_version
137+
if $version_search_result == [] {
138+
error make {
139+
msg: $"Version ($actual_version) is not installed."
140+
label: {
141+
text: $"Version ($version) not found on system"
142+
span: (metadata $version).span
143+
}
144+
}
145+
}
146+
147+
$version_search_result | first
148+
}

0 commit comments

Comments
 (0)