forked from AdamBien/lightmetal
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlminstall
More file actions
executable file
·61 lines (55 loc) · 2.89 KB
/
Copy pathlminstall
File metadata and controls
executable file
·61 lines (55 loc) · 2.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/usr/bin/env -S java --source 25
import module java.net.http;
String name = MethodHandles.lookup().lookupClass().getName();
String version = "2026-06-10.9";
String baseUrl = "https://github.com/AdamBien/lightmetal/releases/latest/download";
Path jarTarget = Path.of("zbo", "lightmetal.jar");
List<String> scripts = List.of("lmlist", "lmprobe", "lmprompt", "lmtps");
/// JAR lands in `./zbo/` and the sibling `lm*` scripts in `./`. The shebangs
/// use `-cp ./zbo/lightmetal.jar:./lightmetal.jar`, so the scripts also work
/// when the JAR sits next to them instead of under `zbo/` — but either way
/// they must be invoked from the directory that holds the JAR.
///
/// No flag handling — per the `/java-cli-script` convention, scripts with no
/// operands surface their version as the first line of normal output instead
/// of gating it behind `-version`.
void main(String... args) throws Exception {
IO.println("%s %s".formatted(name, version));
install(baseUrl + "/lightmetal.jar", jarTarget, false);
for (var script : scripts) {
install(baseUrl + "/" + script, Path.of(script), true);
}
}
void install(String url, Path destination, boolean executable) throws Exception {
IO.println("%s: fetching %s".formatted(name, url));
download(url, destination);
if (executable) {
destination.toFile().setExecutable(true, false);
}
IO.println("%s: installed %s (%d bytes)".formatted(name, destination, Files.size(destination)));
}
/// Atomic-replace: `destination` either keeps its previous content or becomes
/// the fully-downloaded payload, never a half-written stream.
///
/// The `.part` staging file lives next to `destination`, not in the system
/// temp directory, so [Files#move] with [StandardCopyOption#ATOMIC_MOVE]
/// cannot fail with a cross-device link error on hosts where `/tmp` sits on
/// a different filesystem. [Path#toAbsolutePath] is what makes the lookup
/// work for `Path.of("lmlist")` — a bare filename has a null relative parent.
///
/// Exit-on-failure on non-200 is deliberate — a single-file script has no
/// caller to unwind to.
void download(String url, Path destination) throws Exception {
var parent = destination.toAbsolutePath().getParent();
Files.createDirectories(parent);
var staging = Files.createTempFile(parent, destination.getFileName() + "-", ".part");
var client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build();
var request = HttpRequest.newBuilder(URI.create(url)).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofFile(staging));
if (response.statusCode() != HttpURLConnection.HTTP_OK) {
Files.deleteIfExists(staging);
System.err.println("%s: download failed (HTTP %d)".formatted(name, response.statusCode()));
System.exit(1);
}
Files.move(staging, destination, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
}