1
1
import logging
2
+ import platform
3
+ import re
2
4
import sys
3
5
import nox
4
6
10
12
11
13
nox .options .default_venv_backend = "uv"
12
14
15
+ BIN_STALL_CARGO = re .compile (r"^([^\s]+)\s([^:]+):" )
16
+ CARGO_BINSTALL = "cargo-binstall"
17
+
18
+
19
+ class CargoInstaller :
20
+ def __init__ (self , session : nox .Session ):
21
+ cargo_installed : str | None = session .run (
22
+ "cargo" , "install" , "--list" , external = True , silent = True
23
+ )
24
+ assert cargo_installed is not None , "Is rust cargo installed?"
25
+ cargo_bins : dict [str , str ] = {}
26
+ for line in cargo_installed .splitlines ():
27
+ found = BIN_STALL_CARGO .match (line )
28
+ if found is not None :
29
+ name , version = found .groups ()
30
+ cargo_bins .setdefault (name , version )
31
+ self .cargo_bins = cargo_bins
32
+ self .cargo_install_cmd : tuple [str , ...] = ("cargo" , "install" )
33
+
34
+ if CARGO_BINSTALL in cargo_bins :
35
+ ci_logger .info (
36
+ "Found %s: %s" % (CARGO_BINSTALL , cargo_bins [CARGO_BINSTALL ])
37
+ )
38
+ self .cargo_install_cmd = ("cargo" , "binstall" , "-y" )
39
+ else :
40
+ ci_logger .info ("Installing %s" % CARGO_BINSTALL )
41
+ match platform .system ():
42
+ case "Windows" :
43
+ one_liner = """Set-ExecutionPolicy Unrestricted -Scope Process; iex (iwr "https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.ps1").Content"""
44
+ session .run (* one_liner .split (), external = True )
45
+ case "Linux" | "Darwin" :
46
+ one_liner = "curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash"
47
+ session .run (* one_liner .split (), external = True )
48
+ case _:
49
+ session .run (
50
+ * self .cargo_install_cmd ,
51
+ CARGO_BINSTALL ,
52
+ "--locked" ,
53
+ external = True ,
54
+ )
55
+
56
+ def check_install (self , req : str , session : nox .Session ):
57
+ """Use cargo to ensure `req` is installed.
58
+
59
+ Parameters:
60
+ req: The package name (on crates.io) to check and install.
61
+ Supports explicit version in the form
62
+
63
+ Typically this is the project's name as published on crates.io.
64
+ """
65
+
66
+ ver = None
67
+ if "@" in req :
68
+ req , ver = req .split ("@" , 1 )[:2 ]
69
+
70
+ def install ():
71
+ ci_logger .info ("Installing %s" % req )
72
+ dep = req if not ver else f"{ req } @{ ver } "
73
+ session .run (* self .cargo_install_cmd , dep , "--locked" , external = True )
74
+
75
+ installed = False
76
+ if req in self .cargo_bins :
77
+ ci_logger .info ("Found %s %s" % (req , self .cargo_bins [req ]))
78
+ installed = True
79
+ if ver or not installed :
80
+ install ()
81
+
13
82
14
83
def uv_sync (session : nox .Session , * args : str ):
15
84
session .run_install (
@@ -79,6 +148,13 @@ def test(session: nox.Session):
79
148
Otherwise, the default profile is used.
80
149
"""
81
150
uv_sync (session , "--group" , "test" )
151
+ installer = CargoInstaller (session )
152
+ for req in [
153
+ "cargo-llvm-cov" ,
154
+ "cargo-nextest" ,
155
+ ]:
156
+ installer .check_install (req , session )
157
+
82
158
session .run (
83
159
"cargo" ,
84
160
"llvm-cov" ,
@@ -102,6 +178,8 @@ def test_clean(session: nox.Session):
102
178
This is useful if coverage data needs to be
103
179
completely refreshed.
104
180
"""
181
+ installer = CargoInstaller (session )
182
+ installer .check_install ("cargo-llvm-cov" , session )
105
183
session .run ("cargo" , "llvm-cov" , "clean" , external = True )
106
184
107
185
@@ -114,6 +192,8 @@ def llvm_cov(session: nox.Session):
114
192
115
193
Otherwise, the default profile is used.
116
194
"""
195
+ installer = CargoInstaller (session )
196
+ installer .check_install ("cargo-llvm-cov" , session )
117
197
session .run (
118
198
"cargo" ,
119
199
"llvm-cov" ,
@@ -135,6 +215,9 @@ def pretty_cov(session: nox.Session):
135
215
136
216
Otherwise, the default profile is used.
137
217
"""
218
+ installer = CargoInstaller (session )
219
+ for req in ["cargo-llvm-cov" , "llvm-cov-pretty" ]:
220
+ installer .check_install (req , session )
138
221
session .run (
139
222
"cargo" ,
140
223
"llvm-cov" ,
@@ -156,6 +239,8 @@ def lcov(session: nox.Session):
156
239
Useful for codecov uploads and VSCode extensions
157
240
like "Coverage Gutters".
158
241
"""
242
+ installer = CargoInstaller (session )
243
+ installer .check_install ("cargo-llvm-cov" , session )
159
244
session .run (
160
245
"cargo" ,
161
246
"llvm-cov" ,
@@ -177,3 +262,13 @@ def lint(session: nox.Session):
177
262
"cargo" , "clippy" , "--allow-staged" , "--allow-dirty" , "--fix" , external = True
178
263
)
179
264
session .run ("cargo" , "fmt" , external = True )
265
+
266
+
267
+ @nox .session (python = False )
268
+ def install (session : nox .Session ):
269
+ """Install necessary software in global env"""
270
+
271
+ installer = CargoInstaller (session )
272
+
273
+ for req in session .posargs :
274
+ installer .check_install (req , session )
0 commit comments