Inceptum/common/src/main/java/io/gitlab/jfronny/inceptum/common/Updater.java

187 lines
8.3 KiB
Java

package io.gitlab.jfronny.inceptum.common;
import io.gitlab.jfronny.commons.OSUtils;
import io.gitlab.jfronny.inceptum.common.api.MavenApi;
import io.gitlab.jfronny.inceptum.common.model.inceptum.*;
import io.gitlab.jfronny.inceptum.common.model.maven.*;
import org.jetbrains.annotations.Nullable;
import org.xml.sax.SAXException;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Updater {
private static final String ARTIFACTS_URL = "https://pages.frohnmeyer-wds.de/JfMods/Inceptum/artifacts/";
private static final String STABLE_URL = "https://pages.frohnmeyer-wds.de/JfMods/Inceptum/stable/";
public static final String PROJECT_MAVEN = "https://maven.frohnmeyer-wds.de/artifacts/";
public static UpdateMetadata getUpdate() {
return Updater.check(InceptumConfig.channel, true, channel -> {
Utils.LOGGER.error("No stable version was found, switching to experimental channel");
InceptumConfig.channel = channel;
InceptumConfig.saveConfig();
});
}
public static void update(UpdateMetadata source, boolean relaunch) throws IOException, URISyntaxException {
if (Runtime.version().feature() < source.jvm) {
throw new OutdatedException("A newer JVM version is required for the current build of Inceptum. Please update!");
} else if (source.wrapperVersion > BuildMetadata.WRAPPER_VERSION) {
throw new OutdatedException("The current build of Inceptum requires a newer wrapper version. Please update!");
} else if (source.wrapperVersion < BuildMetadata.WRAPPER_VERSION) {
throw new OutdatedException("The current build of Inceptum requires an older wrapper version. Please update!");
}
Utils.LOGGER.info("Downloading version " + source.version);
WrapperConfig config = new WrapperConfig();
config.natives = new HashMap<>();
config.libraries = new LinkedHashSet<>();
config.repositories = new LinkedHashSet<>(source.repositories);
source.natives.forEach((k, v) -> config.natives.put(k, new LinkedHashSet<>(v)));
DependencyNode node = downloadLibrary(source.repositories, "io.gitlab.jfronny.inceptum:launcher-dist:" + source.version, config.libraries);
Utils.LOGGER.info("Downloaded Dependencies:\n" + node);
List<String> currentLibraries = new LinkedList<>(config.libraries);
if (source.natives.containsKey(Utils.getCurrentFlavor())) {
Set<String> natives = new LinkedHashSet<>();
for (String lib : source.natives.get(Utils.getCurrentFlavor())){
downloadLibrary(source.repositories, lib, natives);
}
currentLibraries.addAll(natives);
config.natives.put(Utils.getCurrentFlavor(), natives);
}
GC_WrapperConfig.write(MetaHolder.WRAPPER_CONFIG_PATH, config);
if (relaunch) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
new ProcessBuilder(OSUtils.getJvmBinary(),
"-cp",
buildClasspath(currentLibraries.stream())
.map(Path::toString)
.collect(Collectors.joining("" + File.pathSeparatorChar))
).inheritIO().start();
} catch (IOException e) {
Utils.LOGGER.error("Could not relaunch", e);
}
}));
}
}
public static List<Path> getLaunchClasspath(WrapperConfig wrapperConfig) throws IOException, URISyntaxException {
Set<String> natives = wrapperConfig.natives.get(Utils.getCurrentFlavor());
if (natives == null) natives = new LinkedHashSet<>();
Set<String> libs = wrapperConfig.libraries;
if (libs == null) libs = new LinkedHashSet<>();
boolean configChanged = false;
for (String lib : libs) {
Path p = artifactToPath(lib);
if (!Files.exists(p)) {
configChanged = true;
downloadLibrary(wrapperConfig.repositories, lib, libs);
}
}
for (String lib : natives) {
Path p = artifactToPath(lib);
if (!Files.exists(p)) {
configChanged = true;
downloadLibrary(wrapperConfig.repositories, lib, natives);
}
}
if (configChanged) GC_WrapperConfig.write(MetaHolder.WRAPPER_CONFIG_PATH, wrapperConfig);
return buildClasspath(Stream.concat(libs.stream(), natives.stream())).toList();
}
private static Stream<Path> buildClasspath(Stream<String> libraries) {
return libraries.map(Updater::artifactToPath);
}
private static DependencyNode downloadLibrary(Set<String> repositories, final String artifact, Set<String> libraries) throws IOException, URISyntaxException {
List<Exception> exceptions = new LinkedList<>();
for (String repository : Stream.concat(Stream.of(PROJECT_MAVEN), repositories.stream()).toList()) {
Pom pom;
try {
pom = MavenApi.getPom(repository, artifact);
} catch (IOException | URISyntaxException | XMLStreamException | SAXException e) {
exceptions.add(new Exception("Could not download artifact from " + repository, e));
continue;
}
Set<DependencyNode> dependencies = new LinkedHashSet<>();
if (pom.dependencies != null) {
for (MavenDependency dependency : pom.dependencies) {
String mvnName = dependency.groupId + ":" + dependency.artifactId + ":" + dependency.version;
dependencies.add(downloadLibrary(repositories, mvnName, libraries));
}
}
MavenApi.downloadLibrary(repository, pom);
libraries.add(artifact);
return new DependencyNode(pom, dependencies);
}
IOException exception = new IOException("Could not find any repository containing the artifact " + artifact + " (searched: " + String.join(", ", repositories) + ")");
for (Exception e : exceptions) {
exception.addSuppressed(e);
}
throw exception;
}
private static Path artifactToPath(String artifact) {
return MetaHolder.LIBRARIES_DIR.resolve(MavenApi.mavenNotationToJarPath(artifact)).toAbsolutePath();
}
public static @Nullable UpdateMetadata check(UpdateChannel channel, boolean versionCompare, Consumer<UpdateChannel> channelInvalid) {
try {
UpdateMetadata experimental = Net.downloadObject(ARTIFACTS_URL + "version.json", GC_UpdateMetadata::read);
UpdateMetadata stable = null;
try {
stable = Net.downloadObject(STABLE_URL + "version.json", GC_UpdateMetadata::read);
} catch (Throwable ignored) {}
if (stable == null && channel == UpdateChannel.Stable) {
channel = UpdateChannel.CI;
channelInvalid.accept(channel);
}
UpdateMetadata info = switch (channel) {
case CI -> experimental;
case Stable -> stable;
};
if (info.jvm > Runtime.version().feature()) {
//TODO visual indication for this
Utils.LOGGER.error("A newer JVM is required to use the latest inceptum version. Please update!");
return null;
}
if (versionCompare) {
Utils.LOGGER.info("Latest version is " + info.version + ", current is " + BuildMetadata.VERSION);
if (BuildMetadata.BUILD_TIME >= info.buildTime) {
Utils.LOGGER.info("Up-to-date");
return null;
}
}
return info;
} catch (IOException e) {
Utils.LOGGER.error("Could not check for updates", e);
}
return null;
}
public static String getShadowJarUrl(UpdateChannel channel) {
return switch (channel) {
case CI -> ARTIFACTS_URL;
case Stable -> STABLE_URL;
} + "/Inceptum-" + Utils.getCurrentFlavor() + ".jar";
}
}