Get rid of JGit, please use a normal git install.

.gitignore is now parsed manually during export
This commit is contained in:
Johannes Frohnmeyer 2022-08-02 16:55:16 +02:00
parent 4656b40a9d
commit 35ffea6feb
Signed by: Johannes
GPG Key ID: E76429612C2929F4
22 changed files with 419 additions and 455 deletions

View File

@ -14,6 +14,7 @@ dependencies {
api("io.gitlab.jfronny:commons-gson:${rootProject.extra["jfCommonsVersion"]}")
implementation("io.gitlab.jfronny:commons-slf4j:${rootProject.extra["jfCommonsVersion"]}")
implementation("ch.qos.logback:logback-classic:${rootProject.extra["logbackVersion"]}")
compileOnly("org.jetbrains:annotations:23.0.0")
}
tasks.processResources {

View File

@ -27,9 +27,3 @@ is equivalent to the bash script
inceptum git pull icesrv
inceptum run server restart icesrv
```
## The "git" command
The git command does NOT mirror the behavior of the actual git binary.
It merely provides subcommands for running common actions such as cloning, pulling, committing and pushing on instances using the included JGit
and performs additional sanitization.
Using actual git commands is recommended when the commands provided are not enough for a particular purpose.

View File

@ -22,10 +22,9 @@ dependencies {
val imguiVersion: String by rootProject.extra
implementation(project(":common"))
implementation("org.eclipse.jgit:org.eclipse.jgit:${rootProject.extra["jgitVersion"]}")
implementation(platform("org.lwjgl:lwjgl-bom:$lwjglVersion"))
arrayOf("", "-opengl", "-glfw", "-tinyfd").forEach { it ->
arrayOf("", "-opengl", "-glfw", "-tinyfd").forEach {
implementation("org.lwjgl:lwjgl$it:$lwjglVersion")
if (flavor == "windows" || flavor == "fat") implementation("org.lwjgl:lwjgl$it::natives-windows")
if (flavor == "linux" || flavor == "fat") implementation("org.lwjgl:lwjgl$it::natives-linux")

View File

@ -94,8 +94,8 @@ public class Inceptum {
else if (!defaultCancel) ok.run();
}
public static void getInput(String prompt, String defaultValue, Consumer<String> ok, Runnable cancel) throws IOException {
if (IS_GUI) InceptumGui.WINDOWS.add(new TextBoxWindow(prompt, prompt, defaultValue, ok, cancel));
public static void getInput(String prompt, String details, String defaultValue, Consumer<String> ok, Runnable cancel) throws IOException {
if (IS_GUI) InceptumGui.WINDOWS.add(new TextBoxWindow(prompt, details, defaultValue, ok, cancel));
else {
Scanner scanner = new Scanner(System.in);
System.out.println(prompt);

View File

@ -1,33 +0,0 @@
package io.gitlab.jfronny.inceptum.frontend.cli;
import io.gitlab.jfronny.inceptum.model.inceptum.Config;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.ConfigHolder;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import java.nio.file.Path;
import java.util.List;
public abstract class BaseGitCommand extends BaseInstanceCommand {
public BaseGitCommand(String help, String usage, String... aliases) {
super(help, usage, aliases);
}
public BaseGitCommand(String help, String usage, List<String> aliases, List<Command> subCommands) {
super(help, usage, aliases, subCommands);
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception {
try (Git git = Git.open(instancePath.toFile())) {
Config.GitConfig.GitAuth config = ConfigHolder.CONFIG.git.instanceAuths.get(instancePath.getFileName().toString());
invoke(args.subArgs(), instancePath, meta, git, config.username != null && config.password != null
? new UsernamePasswordCredentialsProvider(config.username, config.password)
: CredentialsProvider.getDefault());
}
}
protected abstract void invoke(CommandArgs args, Path instancePath, InstanceMeta meta, Git git, CredentialsProvider credentials) throws Exception;
}

View File

@ -14,7 +14,6 @@ public class Commands {
new LaunchCommand(),
new ListCommand(),
new ModCommand(),
new GitCommand(),
new ExportCommand(),
new UpdateCheckCommand(),
new JvmStateCommand(),

View File

@ -1,21 +1,17 @@
package io.gitlab.jfronny.inceptum.frontend.cli.commands;
import io.gitlab.jfronny.inceptum.frontend.cli.BaseGitCommand;
import io.gitlab.jfronny.inceptum.frontend.cli.Command;
import io.gitlab.jfronny.inceptum.frontend.cli.CommandArgs;
import io.gitlab.jfronny.inceptum.frontend.cli.*;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.InstanceExporter;
import io.gitlab.jfronny.inceptum.util.ProcessState;
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.transport.CredentialsProvider;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
//TODO modrinth
public class ExportCommand extends BaseGitCommand {
public class ExportCommand extends BaseInstanceCommand {
public ExportCommand() {
this(List.of("export"), List.of(
new MultiMCExportCommand(List.of("multimc", "mmc"), List.of())
@ -23,32 +19,33 @@ public class ExportCommand extends BaseGitCommand {
}
private ExportCommand(List<String> aliases, List<Command> subCommands) {
super("Export a CurseForge instance", "<export path>", List.of("export"), subCommands);
super("Export a CurseForge instance", "<export path> <version>", aliases, subCommands);
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta, Git git, CredentialsProvider credentials) throws Exception {
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception {
if (args.length == 0) throw new IllegalAccessException("You must specify a target path");
if (args.length != 1) throw new IllegalAccessException("Too many arguments");
if (args.length == 1) throw new IllegalAccessException("You must specify a version number");
if (args.length != 2) throw new IllegalAccessException("Too many arguments");
ProcessState state = new ProcessState();
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
mds.runOnce((path, mod) -> {});
InstanceExporter.exportCurseZip(state, instancePath, meta, mds, git, Paths.get(args.get(0)));
InstanceExporter.exportCurseZip(state, instancePath, meta, mds, Paths.get(args.get(0)), args.get(1));
}
private static class MultiMCExportCommand extends BaseGitCommand {
private static class MultiMCExportCommand extends BaseInstanceCommand {
public MultiMCExportCommand(List<String> aliases, List<Command> subCommands) {
super("Export a MultiMC instance", "<export path>", aliases, subCommands);
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta, Git git, CredentialsProvider credentials) throws Exception {
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta) throws Exception {
if (args.length == 0) throw new IllegalAccessException("You must specify a target path");
if (args.length != 1) throw new IllegalAccessException("Too many arguments");
ProcessState state = new ProcessState();
ModsDirScanner mds = ModsDirScanner.get(instancePath.resolve("mods"), meta);
mds.runOnce((path, mod) -> {});
InstanceExporter.exportMultiMCZip(state, instancePath, meta, mds, git, Paths.get(args.get(0)));
InstanceExporter.exportMultiMCZip(state, instancePath, meta, mds, Paths.get(args.get(0)));
}
}
}

View File

@ -1,152 +0,0 @@
package io.gitlab.jfronny.inceptum.frontend.cli.commands;
import io.gitlab.jfronny.inceptum.frontend.cli.BaseGitCommand;
import io.gitlab.jfronny.inceptum.frontend.cli.Command;
import io.gitlab.jfronny.inceptum.frontend.cli.CommandArgs;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.install.Steps;
import io.gitlab.jfronny.inceptum.util.ConfigHolder;
import io.gitlab.jfronny.inceptum.util.MetaHolder;
import io.gitlab.jfronny.inceptum.util.Utils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.URIish;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class GitCommand extends Command {
public GitCommand() {
super("Aliases to some git commands. Intended to be used in batch commands", "", List.of("git", "sync"), List.of(
new CommitCommand(),
new PushCommand(),
new PullCommand(),
new ResetCommand(),
new CloneCommand()
));
}
@Override
protected void invoke(CommandArgs args) {
Utils.LOGGER.error("No known git command was specified");
}
private static class CommitCommand extends BaseGitCommand {
public CommitCommand() {
super("Adds and commits all changes in the instance.", "[message...]", "commit");
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta, Git git, CredentialsProvider credentials) throws GitAPIException {
git.commit()
.setAll(true)
.setMessage(args.length > 1
? String.join(" ", args.getArgs())
: "Commit at t" + System.currentTimeMillis())
.setSign(ConfigHolder.CONFIG.git.signCommits)
.setAuthor(ConfigHolder.CONFIG.git.commitUsername, ConfigHolder.CONFIG.git.commitMail)
.setCredentialsProvider(credentials)
.call();
}
}
private static class PushCommand extends BaseGitCommand {
public PushCommand() {
super("Pushes the current branch to the configured remote", "", "push");
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta, Git git, CredentialsProvider credentials) throws GitAPIException {
git.push()
.setCredentialsProvider(credentials)
.call();
}
}
private static class PullCommand extends BaseGitCommand {
public PullCommand() {
super("Pulls remote changes into the current branch and working tree", "", "pull");
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta, Git git, CredentialsProvider credentials) throws GitAPIException {
git.pull()
.setRebase(true)
.setCredentialsProvider(credentials)
.call();
}
}
private static class ResetCommand extends BaseGitCommand {
public ResetCommand() {
super("Resets the current instance state to the last commit", "", "reset");
}
@Override
protected void invoke(CommandArgs args, Path instancePath, InstanceMeta meta, Git git, CredentialsProvider credentials) throws GitAPIException {
git.checkout()
.setAllPaths(true)
.call();
git.clean()
.setForce(true)
.setCleanDirectories(true)
.call();
}
}
public static class CloneCommand extends Command {
public CloneCommand() {
super("Clones an instance from a URL", "<remote> [name]", "clone");
}
@Override
protected void invoke(CommandArgs args) throws Exception {
if (args.length == 0) {
System.err.println("You haven't specified a remote");
return;
}
String name = args.length > 1 ? args.get(1) : getName(args.get(0));
name = name.replaceAll("^\\.*", "");
clone(args.get(0), name);
}
public static String getName(String url) throws URISyntaxException {
try {
String name;
name = new URIish(url).getHumanishName();
name = name.replaceAll("^\\.*", "");
return name;
}
catch (IllegalArgumentException e) {
throw new URISyntaxException(url, "Could not extract name");
}
}
public static void clone(String url, String name) throws GitAPIException, IOException {
if (!name.matches("^[a-zA-Z0-9]+.*$")) {
throw new IOException("Invalid name: " + name);
}
Path instanceDir = MetaHolder.INSTANCE_DIR.resolve(name);
if (Files.exists(instanceDir)) {
throw new FileAlreadyExistsException("An instance by that name already exists");
}
Git.cloneRepository()
.setURI(url)
.setDirectory(instanceDir.toFile())
.call()
.close();
Path instanceMeta = instanceDir.resolve("instance.json");
if (!Files.exists(instanceMeta)) {
Utils.deleteRecursive(instanceDir);
throw new FileNotFoundException("Invalid repository: Contains no instance.json");
}
Steps.reDownload(instanceDir, Steps.createProcessState());
}
}
}

View File

@ -1,23 +1,15 @@
package io.gitlab.jfronny.inceptum.frontend.gui.window;
import imgui.ImGui;
import imgui.type.ImString;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.frontend.cli.commands.GitCommand;
import io.gitlab.jfronny.inceptum.frontend.gui.control.InstanceManageControls;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.inceptum.util.install.SetupStepInfo;
import io.gitlab.jfronny.inceptum.util.install.Steps;
import org.eclipse.jgit.api.errors.GitAPIException;
import java.io.IOException;
import java.net.URISyntaxException;
public class NewInstanceWindow extends Window {
private final InstanceManageControls imc = new InstanceManageControls(null, null);
private final ImString inceptumRepo = new ImString(GuiUtil.INPUT_FIELD_LENGTH);
private final ImString inceptumName = new ImString(GuiUtil.INPUT_FIELD_LENGTH);
private String inceptumNamePrev = "";
public NewInstanceWindow() {
super("New Instance");
}
@ -41,27 +33,10 @@ public class NewInstanceWindow extends Window {
});
ImGui.endTabItem();
}
if (ImGui.beginTabItem("Clone")) {
if (ImGui.inputTextWithHint("URL", "Repo to download", inceptumRepo)
&& (inceptumName.isEmpty() || inceptumName.get().isEmpty() || inceptumName.get().equals(inceptumNamePrev))) {
try {
inceptumName.set(GitCommand.CloneCommand.getName(inceptumRepo.get()));
inceptumNamePrev = inceptumName.get();
} catch (URISyntaxException ignored) {
}
}
ImGui.inputTextWithHint("Name", "Name of the new instance", inceptumName);
if (ImGui.button("OK")) {
close();
try {
GitCommand.CloneCommand.clone(inceptumRepo.get(), inceptumName.get());
} catch (GitAPIException | IOException e) {
Inceptum.showError("Could not clone instance", e);
}
}
ImGui.endTabItem();
}
if (ImGui.beginTabItem("Import")) {
ImGui.text("You can also just add an instance directory and it'll be loaded automatically");
ImGui.text("Using git to manage it is recommended if you do so");
ImGui.spacing();
ImGui.text("Importing CurseForge or Modrinth packs is not yet implemented");
//TODO generic importer based on zip contents and file name ("mrpack")
ImGui.endTabItem();

View File

@ -5,7 +5,7 @@ import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.frontend.gui.window.Window;
import io.gitlab.jfronny.inceptum.util.ProcessState;
import org.eclipse.jgit.annotations.Nullable;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.atomic.AtomicBoolean;
@ -22,7 +22,7 @@ public class ProcessStateWatcherWindow extends Window {
new Thread(() -> {
try {
executor.accept(canceled);
if (canceled.get()) cancel.run();
if (canceled.get() && cancel != null) cancel.run();
finished = true;
} catch (Throwable e) {
Inceptum.showError(errorMessage, e);

View File

@ -6,6 +6,7 @@ import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.InceptumGui;
import io.gitlab.jfronny.inceptum.frontend.gui.control.Tab;
import io.gitlab.jfronny.inceptum.frontend.gui.window.dialog.ProcessStateWatcherWindow;
import io.gitlab.jfronny.inceptum.frontend.gui.window.dialog.TextBoxWindow;
import io.gitlab.jfronny.inceptum.util.InstanceExporter;
import io.gitlab.jfronny.inceptum.util.ProcessState;
import org.lwjgl.PointerBuffer;
@ -14,6 +15,8 @@ import org.lwjgl.util.tinyfd.TinyFileDialogs;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
public class ExportTab extends Tab {
private final InstanceEditWindow window;
@ -26,11 +29,17 @@ public class ExportTab extends Tab {
@Override
protected void renderInner() {
if (window.mds.isComplete()) {
exportVariant("CurseForge", "zip", (state, exportPath) -> {
InstanceExporter.exportCurseZip(state, window.path, window.instance, window.mds, window.git, exportPath);
AtomicReference<String> cfVersion = new AtomicReference<>();
exportVariant("CurseForge", "zip", it -> {
InceptumGui.WINDOWS.add(new TextBoxWindow("Version", "Please enter the version to list in the modpack file", "1.0", version -> {
cfVersion.set(version);
it.run();
}, () -> {}));
}, (state, exportPath) -> {
InstanceExporter.exportCurseZip(state, window.path, window.instance, window.mds, exportPath, cfVersion.get());
});
exportVariant("MultiMC", "zip", (state, exportPath) -> {
InstanceExporter.exportMultiMCZip(state, window.path, window.instance, window.mds, window.git, exportPath);
exportVariant("MultiMC", "zip", Runnable::run, (state, exportPath) -> {
InstanceExporter.exportMultiMCZip(state, window.path, window.instance, window.mds, exportPath);
});
}
else {
@ -38,22 +47,24 @@ public class ExportTab extends Tab {
}
}
private <T extends Throwable> void exportVariant(String name, String packExt, ThrowingBiConsumer<ProcessState, Path, T> export) {
private <T extends Throwable> void exportVariant(String name, String packExt, Consumer<Runnable> beforeSave, ThrowingBiConsumer<ProcessState, Path, T> export) {
if (ImGui.button(name)) {
try (MemoryStack stack = MemoryStack.stackPush()) {
PointerBuffer aFilterPatterns = stack.mallocPointer(2);
aFilterPatterns.put(stack.UTF8("*." + packExt));
aFilterPatterns.flip();
String p = TinyFileDialogs.tinyfd_saveFileDialog("Export Pack", "", aFilterPatterns, name + " packs (*." + packExt + ")");
if (p != null) {
ProcessState state = new ProcessState(InstanceExporter.STEP_COUNT, "Initializing...");
InceptumGui.open(new ProcessStateWatcherWindow("Exporting", "Could not export pack", state, cToken -> {
Path exportPath = Paths.get(p);
export.accept(state, exportPath);
Inceptum.showInfo(window.path.getFileName().toString() + " has been successfully exported to " + exportPath, "Successfully exported");
}, null));
beforeSave.accept(() -> {
try (MemoryStack stack = MemoryStack.stackPush()) {
PointerBuffer aFilterPatterns = stack.mallocPointer(2);
aFilterPatterns.put(stack.UTF8("*." + packExt));
aFilterPatterns.flip();
String p = TinyFileDialogs.tinyfd_saveFileDialog("Export Pack", "", aFilterPatterns, name + " packs (*." + packExt + ")");
if (p != null) {
ProcessState state = new ProcessState(InstanceExporter.STEP_COUNT, "Initializing...");
InceptumGui.open(new ProcessStateWatcherWindow("Exporting", "Could not export pack", state, cToken -> {
Path exportPath = Paths.get(p);
export.accept(state, exportPath);
Inceptum.showInfo(window.path.getFileName().toString() + " has been successfully exported to " + exportPath, "Successfully exported");
}, null));
}
}
}
});
}
}
}

View File

@ -1,131 +0,0 @@
package io.gitlab.jfronny.inceptum.frontend.gui.window.edit;
import imgui.ImGui;
import imgui.flag.ImGuiInputTextFlags;
import imgui.type.ImString;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.frontend.gui.control.Tab;
import io.gitlab.jfronny.inceptum.frontend.gui.window.GuiUtil;
import io.gitlab.jfronny.inceptum.model.inceptum.Config;
import io.gitlab.jfronny.inceptum.util.ConfigHolder;
import io.gitlab.jfronny.inceptum.util.Utils;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import java.io.IOException;
public class GitTab extends Tab {
private final InstanceEditWindow window;
private final ImString gitUsername = new ImString(GuiUtil.INPUT_FIELD_LENGTH);
private final ImString gitPassword = new ImString(GuiUtil.INPUT_FIELD_LENGTH);
protected CredentialsProvider credentialsProvider;
public GitTab(InstanceEditWindow window) {
super("Git");
this.window = window;
if (ConfigHolder.CONFIG.git.instanceAuths.containsKey(window.name)) {
Config.GitConfig.GitAuth config = ConfigHolder.CONFIG.git.instanceAuths.get(window.name);
gitUsername.set(config.username);
gitPassword.set(config.password);
credentialsProvider = new UsernamePasswordCredentialsProvider(config.username, config.password);
}
else credentialsProvider = CredentialsProvider.getDefault();
}
@Override
protected void renderInner() {
Status status = null;
try {
status = window.git.status().call();
} catch (GitAPIException e) {
Utils.LOGGER.error("Could not check repo status", e);
}
if (status != null && status.isClean()) {
ImGui.text("There are un-committed changes");
for (String change : status.getUncommittedChanges()) {
ImGui.bulletText(change);
}
for (String s : status.getUntracked()) {
ImGui.bulletText(s);
}
if (ImGui.button("Commit")) {
try {
Inceptum.getInput("Commit message", "Commit at t" + System.currentTimeMillis(), message -> {
try {
window.git.commit()
.setAll(true)
.setMessage(message)
.setSign(ConfigHolder.CONFIG.git.signCommits)
.setAuthor(ConfigHolder.CONFIG.git.commitUsername, ConfigHolder.CONFIG.git.commitMail)
.setCredentialsProvider(credentialsProvider)
.call();
} catch (GitAPIException e) {
Inceptum.showError("Could not commit", e);
}
},() -> {});
} catch (IOException e) {
Inceptum.showError("Could not show dialog", e);
}
}
ImGui.sameLine();
}
if (ImGui.button("Push")) {
try {
window.git.push()
.setCredentialsProvider(credentialsProvider)
.call();
} catch (GitAPIException e) {
Inceptum.showError("Could not push", e);
}
}
ImGui.sameLine();
if (ImGui.button("Pull")) {
try {
window.git.pull()
.setRebase(true)
.setCredentialsProvider(credentialsProvider)
.call();
} catch (GitAPIException e) {
Inceptum.showError("Could not pull", e);
}
}
ImGui.sameLine();
if (ImGui.button("Reset")) {
Inceptum.showOkCancel("This will erase all changes since the last commit.\nAre you sure?", "Are you sure?", () -> {
try {
window.git.checkout()
.setAllPaths(true)
.call();
window.git.clean()
.setForce(true)
.setCleanDirectories(true)
.call();
} catch (GitAPIException e) {
Inceptum.showError("Could not reset", e);
}
}, () -> {});
}
ImGui.text("Authentication");
boolean update = ImGui.inputText("Username", gitUsername);
update |= ImGui.inputText("Password", gitPassword, ImGuiInputTextFlags.Password);
if (update) {
if (!ConfigHolder.CONFIG.git.instanceAuths.containsKey(window.name))
ConfigHolder.CONFIG.git.instanceAuths.put(window.name, new Config.GitConfig.GitAuth());
Config.GitConfig.GitAuth config = ConfigHolder.CONFIG.git.instanceAuths.get(window.name);
config.username = gitUsername.get();
config.password = gitPassword.get();
ConfigHolder.saveConfig();
credentialsProvider = new UsernamePasswordCredentialsProvider(config.username, config.password);
}
ImGui.text("Please note that Inceptum's git integration intentionally does not cover advanced usage");
ImGui.text("Using software intended to work with git (such as the git CLI or GitHub Desktop) is recommended");
}
@Override
protected boolean canShow() {
return window.git != null;
}
}

View File

@ -8,7 +8,6 @@ import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.InstanceLock;
import io.gitlab.jfronny.inceptum.util.Utils;
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
import org.eclipse.jgit.api.Git;
import java.io.IOException;
import java.nio.file.Path;
@ -19,7 +18,6 @@ public class InstanceEditWindow extends Window {
protected final String name;
protected final InstanceMeta instance;
protected final ModsDirScanner mds;
protected final Git git;
private final List<Tab> tabs;
protected boolean reDownload = false;
protected boolean lastTabWasMods = false;
@ -31,12 +29,10 @@ public class InstanceEditWindow extends Window {
this.instance = instance;
this.mds = ModsDirScanner.get(path.resolve("mods"), instance);
this.mds.start();
this.git = Git.open(path.toFile());
this.tabs = List.of(
new GeneralTab(this),
new ArgumentsTab(this),
new ModsTab(this),
new GitTab(this),
new ExportTab(this)
);
}
@ -73,7 +69,6 @@ public class InstanceEditWindow extends Window {
@Override
public void close() {
super.close();
if (git != null) git.close();
if (reDownload) {
GuiUtil.reload(path);
}

View File

@ -5,7 +5,7 @@ import io.gitlab.jfronny.inceptum.model.curseforge.response.*;
import io.gitlab.jfronny.inceptum.util.*;
import io.gitlab.jfronny.inceptum.util.api.*;
import io.gitlab.jfronny.inceptum.util.source.*;
import org.eclipse.jgit.annotations.*;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.net.*;

View File

@ -1,20 +1,18 @@
package io.gitlab.jfronny.inceptum.util;
import io.gitlab.jfronny.inceptum.model.curseforge.*;
import io.gitlab.jfronny.inceptum.model.inceptum.*;
import io.gitlab.jfronny.inceptum.model.multimc.*;
import io.gitlab.jfronny.inceptum.util.mds.*;
import io.gitlab.jfronny.inceptum.util.source.*;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.*;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.*;
import io.gitlab.jfronny.inceptum.model.curseforge.CurseforgeModpackManifest;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.model.multimc.MMCPackMeta;
import io.gitlab.jfronny.inceptum.util.ignore.IgnoringWalk;
import io.gitlab.jfronny.inceptum.util.mds.IWModDescription;
import io.gitlab.jfronny.inceptum.util.mds.ModsDirScanner;
import io.gitlab.jfronny.inceptum.util.source.CurseforgeModSource;
import io.gitlab.jfronny.inceptum.util.source.ModSource;
import java.io.*;
import java.net.*;
import java.nio.file.FileSystem;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.time.*;
import java.time.Instant;
import java.util.*;
//TODO deduplicate
@ -25,7 +23,7 @@ public class InstanceExporter {
private static final String OVERRIDES_DIR_DEFAULT = "overrides";
private static final String DOT_MINECRAFT = ".minecraft";
public static void exportCurseZip(ProcessState state, Path instanceDir, InstanceMeta meta, ModsDirScanner mds, Git git, Path exportPath) throws IOException, URISyntaxException, GitAPIException {
public static void exportCurseZip(ProcessState state, Path instanceDir, InstanceMeta meta, ModsDirScanner mds, Path exportPath, String version) throws IOException, URISyntaxException {
if (Files.exists(exportPath)) Files.delete(exportPath);
try (FileSystem fs = Utils.openZipFile(exportPath, true)) {
state.incrementStep("Preparing basic manifest");
@ -42,7 +40,7 @@ public class InstanceExporter {
manifest.manifestType = "minecraftModpack";
manifest.manifestVersion = 1;
manifest.name = instanceDir.getFileName().toString();
manifest.version = git.log().setMaxCount(1).call().iterator().next().getId().getName();
manifest.version = version;
manifest.author = ConfigHolder.CONFIG.git.commitUsername;
manifest.overrides = OVERRIDES_DIR_DEFAULT;
Path overrides = fs.getPath(OVERRIDES_DIR_DEFAULT);
@ -79,16 +77,7 @@ public class InstanceExporter {
}
}
state.incrementStep("Adding files");
List<Path> discovered = new ArrayList<>();
try (TreeWalk treeWalk = new TreeWalk(git.getRepository())) {
treeWalk.addTree(new FileTreeIterator(git.getRepository()));
treeWalk.setRecursive(false);
while (treeWalk.next()) {
if (treeWalk.getPathString().startsWith("mods")) continue;
discovered.add(instanceDir.resolve(treeWalk.getPathString()));
}
}
for (Path l : discovered) {
for (Path l : IgnoringWalk.walk(instanceDir).toList()) {
String fn = l.getFileName().toString();
if (fn.equals("mods") || fn.startsWith(".")) continue;
state.updateStep(fn);
@ -102,7 +91,7 @@ public class InstanceExporter {
}
}
public static void exportMultiMCZip(ProcessState state, Path instanceDir, InstanceMeta meta, ModsDirScanner mds, Git git, Path exportPath) throws IOException, URISyntaxException, GitAPIException {
public static void exportMultiMCZip(ProcessState state, Path instanceDir, InstanceMeta meta, ModsDirScanner mds, Path exportPath) throws IOException, URISyntaxException {
if (Files.exists(exportPath)) Files.delete(exportPath);
try (FileSystem fs = Utils.openZipFile(exportPath, true)) {
{
@ -183,16 +172,7 @@ public class InstanceExporter {
}
}
state.incrementStep("Adding files");
List<Path> discovered = new ArrayList<>();
try (TreeWalk treeWalk = new TreeWalk(git.getRepository())) {
treeWalk.addTree(new FileTreeIterator(git.getRepository()));
treeWalk.setRecursive(false);
while (treeWalk.next()) {
if (treeWalk.getPathString().startsWith("mods")) continue;
discovered.add(instanceDir.resolve(treeWalk.getPathString()));
}
}
for (Path l : discovered) {
for (Path l : IgnoringWalk.walk(instanceDir).toList()) {
String fn = l.getFileName().toString();
if (fn.equals("mods") || fn.startsWith(".")) continue;
state.updateStep(fn);

View File

@ -37,7 +37,7 @@ public class InstanceLauncher {
String sysUser = System.getProperty("user.name");
if (ConfigHolder.CONFIG.offlineAccountLastName == null)
ConfigHolder.CONFIG.offlineAccountLastName = sysUser;
Inceptum.getInput("User name", ConfigHolder.CONFIG.offlineAccountLastName, name -> {
Inceptum.getInput("User name", "Please enter the username to use for this session", ConfigHolder.CONFIG.offlineAccountLastName, name -> {
ConfigHolder.CONFIG.offlineAccountLastName = name.equals(sysUser) ? null : name;
ConfigHolder.saveConfig();
AuthInfo infoNew = new AuthInfo(name, authInfo.uuid(), authInfo.accessToken(), authInfo.userType());

View File

@ -0,0 +1,42 @@
package io.gitlab.jfronny.inceptum.util.ignore;
import java.util.*;
public class Ignore {
private final List<IgnoreRule> rules = new ArrayList<>();
private final List<String> originalRules = new ArrayList<>();
public Ignore add(String rule) {
originalRules.add(rule);
rules.add(new IgnoreRule(rule));
return this;
}
public Ignore add(Collection<String> patterns) {
originalRules.addAll(patterns);
patterns.forEach(s -> rules.add(new IgnoreRule(s)));
return this;
}
public Ignore add(String... patterns) {
originalRules.addAll(Arrays.asList(patterns));
for (String pattern : patterns) {
rules.add(new IgnoreRule(pattern));
}
return this;
}
public boolean isIgnored(String path) {
boolean ignore = false;
for (IgnoreRule rule : rules) {
if (rule.negate) {
if (ignore && rule.isMatch(path)) {
ignore = false;
}
} else if (!ignore && rule.isMatch(path)) {
ignore = true;
}
}
return ignore;
}
}

View File

@ -0,0 +1,66 @@
package io.gitlab.jfronny.inceptum.util.ignore;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;
public class IgnoreRule {
private static final List<Replacer> REPLACERS = List.of(
Replacers.TRAILING_SPACES,
Replacers.ESCAPED_SPACES,
Replacers.LITERAL_DOT,
Replacers.LITERAL_PLUS,
// probably not needed
// Replacers.METACHARACTERS,
Replacers.QUESTION_MARK,
Replacers.NON_LEADING_SINGLE_STAR,
Replacers.LEADING_SINGLE_STAR,
Replacers.LEADING_DOUBLE_STAR,
// probably not needed
// Replacers.METACHARACTER_SLASH_AFTER_LEADING_SLASH,
Replacers.MIDDLE_DOUBLE_STAR,
Replacers.LEADING_SLASH,
Replacers.TRAILING_DOUBLE_STAR,
Replacers.OTHER_DOUBLE_STAR,
Replacers.MIDDLE_SLASH,
Replacers.TRAILING_SLASH,
Replacers.NO_SLASH,
Replacers.NO_TRAILING_SLASH,
Replacers.ENDING
);
private final Predicate<String> parsedRegex;
public final boolean negate;
/**
* Initializes a new instance of the IgnoreRule class.
* Parses the given pattern as per .gitignore spec.
* @param pattern Pattern to parse
*/
public IgnoreRule(String pattern) {
if (pattern == null || pattern.isBlank() || pattern.startsWith("#")) { // Ignore comments and empty lines
parsedRegex = s -> false;
negate = false;
return;
}
boolean isNegate = false;
if (pattern.startsWith("\\!") || pattern.startsWith("\\#")) {
pattern = pattern.substring(1);
} else if (pattern.startsWith("!")) {
isNegate = true;
pattern = pattern.substring(1);
}
negate = isNegate;
for (Replacer replacer : REPLACERS) {
pattern = replacer.invoke(pattern);
}
parsedRegex = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE).asMatchPredicate();
}
public boolean isMatch(String input) {
return parsedRegex.test(input);
}
}

View File

@ -0,0 +1,79 @@
package io.gitlab.jfronny.inceptum.util.ignore;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class IgnoringWalk implements Iterator<Path> {
public static Stream<Path> walk(Path repositoryRoot) throws IOException {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new IgnoringWalk(repositoryRoot), 0), false);
}
private static final String GITIGNORE = ".gitignore";
private static final String GIT = ".git";
private final Path ref;
private final Queue<Path> toScan = new ArrayDeque<>(); // A queue of absolute Paths which must still be scanned/returned
private final Map<Path, Ignore> ignores = new HashMap<>();
private Path next;
private IgnoringWalk(Path repositoryRoot) throws IOException {
this.ref = repositoryRoot;
enqueueContent(repositoryRoot);
}
@Override
public Path next() {
if (!hasNext()) throw new NoSuchElementException();
Path ret = next;
next = null;
return ret;
}
@Override
public boolean hasNext() {
if (next != null) return true;
next = toScan.poll();
if (next == null) return false;
if (Files.isDirectory(next)) {
try {
enqueueContent(next);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
return true;
}
private void enqueueContent(Path directory) throws IOException {
Path ignorePath = directory.resolve(GITIGNORE);
if (Files.exists(ignorePath)) {
ignores.put(directory, new Ignore().add(Files.readAllLines(ignorePath)));
}
try (Stream<Path> files = Files.list(directory)) {
for (Path path : files.toList()) {
String fileName = path.getFileName().toString();
if (!fileName.equals(GITIGNORE)
&& !fileName.equals(GIT)
&& !isIgnored(ref.relativize(path))) {
toScan.add(path);
}
}
}
}
private boolean isIgnored(Path relativePath) {
Path constructed = ref;
for (Path segment : relativePath) {
Ignore ignore = ignores.get(constructed);
if (ignore != null && ignore.isIgnored(relativePath.toString()))
return true;
constructed = constructed.resolve(segment);
}
return false;
}
}

View File

@ -0,0 +1,26 @@
package io.gitlab.jfronny.inceptum.util.ignore;
import java.util.function.Function;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
public class Replacer {
private final String name;
private final Pattern regex;
private final Function<MatchResult, String> matchEvaluator;
public Replacer(String name, String regex, Function<MatchResult, String> matchEvaluator) {
this.name = name;
this.regex = Pattern.compile(regex);
this.matchEvaluator = matchEvaluator;
}
public String invoke(String pattern) {
return regex.matcher(pattern).replaceAll(matchEvaluator);
}
@Override
public String toString() {
return name;
}
}

View File

@ -0,0 +1,138 @@
package io.gitlab.jfronny.inceptum.util.ignore;
public class Replacers {
public static final Replacer TRAILING_SPACES = new Replacer(
"TrailingSpaces",
"\\\\?\\s+$",
match -> match.group().indexOf('\\') == 0 ? " " : ""
);
public static final Replacer ESCAPED_SPACES = new Replacer(
"EscapedSpaces",
"\\\\\\s",
match -> " "
);
public static final Replacer LITERAL_PLUS = new Replacer(
"LiteralPlus",
"\\+",
match -> "\\+"
);
// a ? matches any character other than a /
public static final Replacer QUESTION_MARK = new Replacer(
"QuestionMark",
"(?!\\\\)\\?",
match -> "[^/]"
);
// a leading / matches the beginning of the path
// eg. /fake.c only matches fake.c, not src/fake.c
public static final Replacer LEADING_SLASH = new Replacer(
"LeadingSlash",
"^\\/",
match -> "^"
);
public static final Replacer METACHARACTER_SLASH_AFTER_LEADING_SLASH = new Replacer(
"MetacharacterSlashAfterLeadingSlash",
"\\/",
match -> "\\/"
);
// A leading "**" followed by a slash means match in all directories.
// For example, "**/foo" matches file or directory "foo" anywhere, the same as pattern "foo".
// "**/foo/bar" matches file or directory "bar" anywhere that is directly under directory "foo".
public static final Replacer LEADING_DOUBLE_STAR = new Replacer(
"LeadingDoubleStar",
"^(\\*\\*/|\\*\\*)",
match -> ".*"
);
// A slash followed by two consecutive asterisks then a slash matches zero or more directories.
// For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on.
public static final Replacer MIDDLE_DOUBLE_STAR = new Replacer(
"MiddleDoubleStar",
"(?<=/)\\*\\*/",
match -> "(.*/)?"
);
// A trailing "/**" matches everything inside. For example,
// "abc/**" matches all files inside directory "abc",
// relative to the location of the .gitignore file, with infinite depth.
public static final Replacer TRAILING_DOUBLE_STAR = new Replacer(
"TrailingDoubleStar",
"\\*\\*$",
match -> ".*$"
);
// Undocumented cases like foo/**.ps
// Treat ** as match any character other than /.
public static final Replacer OTHER_DOUBLE_STAR = new Replacer(
"TrailingDoubleStar",
"\\*\\*",
match -> "[^/]*"
);
// If there is a separator at the beginning or middle (or both) of the pattern,
// then the pattern is relative to the directory level of the particular .gitignore file itself.
// Otherwise the pattern may also match at any level below the .gitignore level.
// The leading slash should be gone after using LeadingSlash.
// So put a ^ in the beginning of the match.
public static final Replacer MIDDLE_SLASH = new Replacer(
"MiddleSlash",
"^([^/\\^]+/[^/]+)",
match -> "^" + match.group(1)
);
// If there is a separator at the end of the pattern then the pattern will only match directories,
// otherwise the pattern can match both files and directories.
// This regex handles the paths with trailing slash present.
public static final Replacer TRAILING_SLASH = new Replacer(
"TrailingSlash",
"^([^/]+)/$",
match -> "(/|^)" + match.group(1) + "/"
);
// If there is a separator at the end of the pattern then the pattern will only match directories,
// otherwise the pattern can match both files and directories.
// This pattern handles the paths with no trailing slash.
public static final Replacer NO_TRAILING_SLASH = new Replacer(
"NoTrailingSlash",
"([^/$]+)$",
match -> match.group(1) + "(/.*)?$"
);
// An asterisk "*" matches anything except a slash.
// Replaces single * with anything other than a /.
// Unless the star is in the beginning of the pattern.
public static final Replacer NON_LEADING_SINGLE_STAR = new Replacer(
"NonLeadingSingleStar",
"(?<!^)(?<!\\*)\\*(?!\\*)",
match -> "[^/]*"
);
public static final Replacer LEADING_SINGLE_STAR = new Replacer(
"LeadingSingleStar",
"^(?<!\\*)\\*(?!\\*)",
match -> ".*"
);
public static final Replacer ENDING = new Replacer(
"Ending",
"([^/$]+)$",
match -> match.group(1) + "$"
);
public static final Replacer LITERAL_DOT = new Replacer(
"LiteralDot",
"\\.",
match -> "\\."
);
public static final Replacer NO_SLASH = new Replacer(
"NoSlash",
"(^[^/]*$)",
match -> "(^|/)" + match.group(1)
);
}

View File

@ -1,15 +1,9 @@
package io.gitlab.jfronny.inceptum.util.install.steps;
import io.gitlab.jfronny.inceptum.Inceptum;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.*;
import io.gitlab.jfronny.inceptum.util.install.SetupStepInfo;
import io.gitlab.jfronny.inceptum.util.install.Step;
import io.gitlab.jfronny.inceptum.model.inceptum.InstanceMeta;
import io.gitlab.jfronny.inceptum.util.ConfigHolder;
import io.gitlab.jfronny.inceptum.util.InstanceLock;
import io.gitlab.jfronny.inceptum.util.MetaHolder;
import io.gitlab.jfronny.inceptum.util.Utils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import java.io.IOException;
import java.nio.file.Files;
@ -40,21 +34,5 @@ public class WriteMetadataStep implements Step {
eula.txt
world/""");
}
Path gitDir = instance.resolve(".git");
if (!Files.exists(gitDir)) {
try (Git git = Git.init()
.setDirectory(instance.toFile())
.setGitDir(gitDir.toFile())
.call()) {
git.commit()
.setAll(true)
.setMessage("Initial commit")
.setSign(false)
.setAuthor(ConfigHolder.CONFIG.git.commitUsername, ConfigHolder.CONFIG.git.commitMail)
.call();
} catch (GitAPIException e) {
Inceptum.showError("Could not initialize Git", e);
}
}
}
}