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

194 lines
8.8 KiB
Java

package io.gitlab.jfronny.inceptum.common;
import gsoncompile.extensions.io.gitlab.jfronny.inceptum.common.model.inceptum.UpdateMetadata.GC_UpdateMetadata;
import gsoncompile.extensions.io.gitlab.jfronny.inceptum.common.model.inceptum.WrapperConfig.GC_WrapperConfig;
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.*;
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.Stream;
public class Updater {
public static final String PROJECT_MAVEN = "https://maven.frohnmeyer-wds.de/artifacts/";
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 UpdateMetadata getUpdate(boolean versionCompare, boolean checkEnv) throws UpdateCheckException {
return Updater.check(InceptumConfig.channel, versionCompare, checkEnv, 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 {
Utils.LOGGER.info("Downloading version " + source.version);
WrapperConfig config = new WrapperConfig(
new LinkedHashSet<>(),
new LinkedHashSet<>(source.repositories),
new HashMap<>()
);
source.natives.forEach((k, v) -> config.natives[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.currentFlavor)) {
Set<String> natives = new LinkedHashSet<>();
for (String lib : source.natives[Utils.currentFlavor]) {
downloadLibrary(source.repositories, lib, natives);
}
currentLibraries.addAll(natives);
config.natives[Utils.currentFlavor] = natives;
}
GC_WrapperConfig.write(config, MetaHolder.WRAPPER_CONFIG_PATH);
if (relaunch) {
Runtime.runtime.addShutdownHook(new Thread(() -> {
try {
new ProcessBuilder(OSUtils.jvmBinary,
"-cp",
buildClasspath(currentLibraries.stream())
.map(Path::toString)
.join(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[Utils.currentFlavor];
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 = ArtifactMeta.parse(lib).localPath;
if (!Files.exists(p)) {
configChanged = true;
downloadLibrary(wrapperConfig.repositories, lib, libs);
}
}
for (String lib : natives) {
Path p = ArtifactMeta.parse(lib).localPath;
if (!Files.exists(p)) {
configChanged = true;
downloadLibrary(wrapperConfig.repositories, lib, natives);
}
}
if (configChanged) GC_WrapperConfig.write(wrapperConfig, MetaHolder.WRAPPER_CONFIG_PATH);
return buildClasspath(libs.stream().concat(natives.stream())).toList();
}
private static Stream<Path> buildClasspath(Stream<String> libraries) {
return libraries.map(ArtifactMeta::parse).map(ArtifactMeta::getLocalPath);
}
private static DependencyNode downloadLibrary(Set<String> repositories, final String artifact, Set<String> libraries) throws IOException, URISyntaxException {
List<FileNotFoundException> suppressed = new LinkedList<>();
for (String repository : Stream.of(PROJECT_MAVEN).concat(repositories.stream())) {
ArtifactMeta meta;
try {
meta = MavenApi.getMetadata(repository, artifact);
} catch (FileNotFoundException ignored) {
meta = ArtifactMeta.parse(artifact);
} catch (IOException | URISyntaxException | SAXException e) {
throw new IOException("Could not download artifact from " + repository, e);
}
Pom pom;
try {
pom = MavenApi.getPom(repository, meta);
} catch (FileNotFoundException notFound) {
suppressed.add(notFound);
continue;
} catch (IOException | URISyntaxException | XMLStreamException | SAXException e) {
throw new IOException("Could not download artifact " + meta.mavenNotation + " from " + repository, e);
}
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, meta);
libraries.add(artifact);
return new DependencyNode(artifact, dependencies);
}
IOException e = new IOException("Could not find any repository containing the artifact " + artifact + " (searched: " + String.join(", ", repositories) + ")");
for (FileNotFoundException ex : suppressed) e.addSuppressed(ex);
throw e;
}
public static @Nullable UpdateMetadata check(UpdateChannel channel, boolean versionCompare, boolean checkEnv, Consumer<UpdateChannel> channelInvalid) throws UpdateCheckException {
try {
UpdateMetadata experimental = Net.downloadObject(ARTIFACTS_URL + "version.json", json -> GC_UpdateMetadata.read(json));
UpdateMetadata stable = null;
try {
stable = Net.downloadObject(STABLE_URL + "version.json", json -> GC_UpdateMetadata.read(json));
} 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 (checkEnv) {
if (info.jvm > Runtime.version().feature()) throw new UpdateCheckException("A newer JVM is required to use the latest inceptum version. Please update!", "Outdated Java");
if (info.wrapperVersion != BuildMetadata.WRAPPER_VERSION) throw new UpdateCheckException("A different version of the Inceptum Wrapper is required for this update!", "Mismatched Wrapper");
}
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.currentFlavor + ".jar";
}
public static class UpdateCheckException extends Exception {
public final String message;
public final String title;
public UpdateCheckException(String message, String title) {
super(message);
this.message = message;
this.title = title;
}
}
}