Skip to content

Commit

Permalink
Add JShell-based basic installation support
Browse files Browse the repository at this point in the history
  • Loading branch information
sormuras committed Apr 15, 2024
1 parent 4abe504 commit 87943f7
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 0 deletions.
37 changes: 37 additions & 0 deletions .bach/install.jshell
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 Christian Stein
* Licensed under the Universal Permissive License v 1.0 -> https://opensource.org/license/upl
*/

System.out.printf(
"""
___ ___ ___ ___
/\\ \\ /\\ \\ /\\ \\ /\\__\\
/::\\ \\ /::\\ \\ /::\\ \\ /:/__/_
/::\\:\\__\\/::\\:\\__\\/:/\\:\\__\\/::\\/\\__\\
\\:\\::/ /\\/\\::/ /\\:\\ \\/__/\\/\\::/ / Java %s
\\::/ / /:/ / \\:\\__\\ /:/ / %s
\\/__/ \\/__/ \\/__/ \\/__/ %s
""",
Runtime.version(),
System.getProperty("os.name"),
Path.of("").toUri()
)

System.out.println("| Source Bach's installer from " + Path.of("src/bach.run/BachInstaller.java").toUri())

/open src/bach.run/BachInstaller.java

int code = 0
try {
// BachInstaller.installDefaultVersionIntoDefaultDirectory();
new BachInstaller("main", Path.of(".bach/var/tmp/installed")).install();
}
catch(Throwable throwable) {
System.err.println(throwable);
code = 1;
}

System.out.println("| Installation of Bach finished with exit code " + code)

/exit code
1 change: 1 addition & 0 deletions .idea/bach.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Bach's Modular Sources

For the time being, this folder only contains a subdirectory named `bach.run`.
The `bach.run` subdirectory contains helper files used by https://bach.run path forwarding rules.
133 changes: 133 additions & 0 deletions src/bach.run/BachInstaller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2024 Christian Stein
* Licensed under the Universal Permissive License v 1.0 -> https://opensource.org/license/upl
*/

import java.net.*;
import java.nio.file.*;
import java.util.*;

record BachInstaller(String version, Path home) {
static String DEFAULT_VERSION = System.getProperty("-Default.version".substring(2), "main");
static Path DEFAULT_HOME = Path.of(System.getProperty("-Default.home".substring(2), ".bach"));

@SuppressWarnings("unused")
static void installDefaultVersionIntoDefaultDirectory() throws Exception {
var installer = new BachInstaller(DEFAULT_VERSION);
installer.install();
}

@SuppressWarnings("unused")
static void listInstallableVersions() {
System.out.println("- Default version: " + DEFAULT_VERSION);
System.out.println("- Released versions: https://github.com/sormuras/run.bach/releases");
System.out.println("- Head revisions: https://github.com/sormuras/run.bach/branches");
}

BachInstaller(String version) {
this(version, DEFAULT_HOME);
}

void install() throws Exception {
var uris =
List.of(
"https://github.com/sormuras/run.bach/archive/refs/tags/" + version + ".zip",
"https://github.com/sormuras/run.bach/archive/refs/heads/" + version + ".zip");
for (var uri : uris) {
if (Internal.head(uri)) {
install(uri);
return;
}
}
}

void install(String uri) throws Exception {
System.out.println("Install Bach " + version + " to " + home.toUri() + "...");
var tmp = Files.createTempDirectory("run.bach-" + version + "-");
var dir = Files.createDirectories(home.resolve("src/run.bach/run/bach"));
var zip = tmp.resolve("run.bach-" + version + ".zip");
// download and unzip
Internal.copy(uri, zip, StandardCopyOption.REPLACE_EXISTING);
Internal.unzip(zip, dir, 1);

// clean up
Internal.delete(tmp);
}

private interface Internal {
boolean DEBUG = Boolean.getBoolean("-Debug".substring(2));

static void debug(String message) {
if (DEBUG) System.out.println(message);
}

static boolean head(String source) throws Exception {
var url = URI.create(source).toURL();
var con = (HttpURLConnection) url.openConnection();
try {
con.setRequestMethod("HEAD");
var status = con.getResponseCode();
debug("%d <- HEAD %s".formatted(status, source));
if (status < 299) return true;
} finally {
con.disconnect();
}
return false;
}

static void copy(String source, Path target, CopyOption... options) throws Exception {
debug("<< %s".formatted(source));
Files.createDirectories(target.getParent());
try (var stream =
source.startsWith("http")
? URI.create(source).toURL().openStream()
: Files.newInputStream(Path.of(source))) {
var size = Files.copy(stream, target, options);
debug(">> %,7d %s".formatted(size, target.getFileName()));
}
}

static void delete(Path path) throws Exception {
var start = path.normalize().toAbsolutePath();
if (Files.notExists(start)) return;
for (var root : start.getFileSystem().getRootDirectories()) {
if (start.equals(root)) {
debug("deletion of root directory?! " + path);
return;
}
}
debug("delete directory tree " + start);
try (var stream = Files.walk(start)) {
var files = stream.sorted((p, q) -> -p.compareTo(q));
for (var file : files.toArray(Path[]::new)) Files.deleteIfExists(file);
}
}

static void unzip(Path zip, Path dir, int sub) throws Exception {
debug("<< %s".formatted(zip.toUri()));
debug(">> %s".formatted(dir.toUri()));
var files = new ArrayList<Path>();
try (var fs = FileSystems.newFileSystem(zip)) {
for (var root : fs.getRootDirectories()) {
try (var stream = Files.walk(root)) {
var list = stream.filter(Files::isRegularFile).toList();
for (var file : list) {
var relative = root.relativize(file);
var source = sub == 0 ? relative : relative.subpath(sub, relative.getNameCount());
var target = dir.resolve(source.toString());
debug(">> - " + target.toUri());
Files.createDirectories(target.getParent());
Files.copy(file, target, StandardCopyOption.REPLACE_EXISTING);
files.add(target);
}
}
}
}
debug(">> %d files unzipped".formatted(files.size()));
}
}

public static void main(String... args) throws Exception {
installDefaultVersionIntoDefaultDirectory();
}
}
28 changes: 28 additions & 0 deletions src/bach.run/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Bach's `src/bach.run` folder

This folder contains Java source files (`.java`) and Java Shell scripts (`.jshell`) helping to install Bach.

## Install default version of Bach

```shell
mkdir example && cd example
jshell
/open https://install.bach.run
```

- https://install.bach.run forwards to the _"install default version of Bach into `.bach` directory of the current working directory"_ [Java Shell](install.jshell) script.

Above's script is a shortcut for the following Java Shell commands and snippets.

```shell
/open https://src.bach.run/BachInstaller.java
BachInstaller.installDefaultVersionIntoDefaultDirectory()
/exit
```

## Path forwarding

- https://src.bach.run forwards to https://raw.githubusercontent.com/sormuras/bach/main/src/bach.run - resulting in `404: Not Found`
- https://src.bach.run/install.jshell forwards to https://raw.githubusercontent.com/sormuras/bach/main/src/bach.run/install.jshell
- https://src.bach.run/BachInstaller.java forwards to https://raw.githubusercontent.com/sormuras/bach/main/src/bach.run/BachInstaller.java
- https://src.bach.run/Hello.java forwards to https://raw.githubusercontent.com/sormuras/bach/main/src/bach.run/Hello.java
36 changes: 36 additions & 0 deletions src/bach.run/install.jshell
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2024 Christian Stein
* Licensed under the Universal Permissive License v 1.0 -> https://opensource.org/license/upl
*/

System.out.printf(
"""
___ ___ ___ ___
/\\ \\ /\\ \\ /\\ \\ /\\__\\
/::\\ \\ /::\\ \\ /::\\ \\ /:/__/_
/::\\:\\__\\/::\\:\\__\\/:/\\:\\__\\/::\\/\\__\\
\\:\\::/ /\\/\\::/ /\\:\\ \\/__/\\/\\::/ / Java %s
\\::/ / /:/ / \\:\\__\\ /:/ / %s
\\/__/ \\/__/ \\/__/ \\/__/ %s
""",
Runtime.version(),
System.getProperty("os.name"),
Path.of("").toUri()
)

System.out.println("| Source Bach's installer from https://src.bach.run/BachInstaller.java ...")

/open https://src.bach.run/BachInstaller.java

int code = 0
try {
BachInstaller.installDefaultVersionIntoDefaultDirectory();
}
catch(Throwable throwable) {
System.err.println(throwable);
code = 1;
}

System.out.println("| Installation of Bach finished with exit code " + code)

/exit code

0 comments on commit 87943f7

Please sign in to comment.