mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-05-04 03:40:32 +00:00
Rework prompts
This commit is contained in:
@@ -53,7 +53,7 @@ public class MarkdownComp extends Comp<CompStructure<StackPane>> {
|
||||
|
||||
private Path getHtmlFile(String markdown) {
|
||||
if (TEMP == null) {
|
||||
TEMP = ShellTemp.getLocalTempDataDirectory("wv");
|
||||
TEMP = ShellTemp.getLocalTempDataDirectory("webview");
|
||||
}
|
||||
|
||||
if (markdown == null) {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.xpipe.app.terminal;
|
||||
|
||||
import io.xpipe.app.util.GithubReleaseDownloader;
|
||||
import io.xpipe.app.util.HttpHelper;
|
||||
import io.xpipe.app.util.ShellTemp;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ClinkHelper {
|
||||
|
||||
private static String downloadUrl = null;
|
||||
private static Path downloadFile = null;
|
||||
|
||||
public static FilePath getTargetDir(ShellControl sc) throws Exception {
|
||||
var targetDir = ShellTemp.createUserSpecificTempDataDirectory(sc, null).join("bin", "clink");
|
||||
return targetDir;
|
||||
}
|
||||
|
||||
public static boolean checkIfInstalled(ShellControl sc) throws Exception {
|
||||
if (sc.view().findProgram("clink").isPresent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var targetDir = getTargetDir(sc);
|
||||
return sc.view().fileExists(targetDir.join("clink_x64.exe"));
|
||||
}
|
||||
|
||||
public static void install(ShellControl sc) throws Exception {
|
||||
var targetDir = getTargetDir(sc);
|
||||
sc.view().mkdir(targetDir);
|
||||
var temp = GithubReleaseDownloader.getDownloadTempFile("chrisant996/clink", "clink.zip", name -> name.endsWith(".zip") && !name.endsWith("symbols.zip"));
|
||||
try (var fs = FileSystems.newFileSystem(temp)) {
|
||||
var exeFile = fs.getPath("clink_x64.exe");
|
||||
var exeBytes = Files.readAllBytes(exeFile);
|
||||
sc.view().writeStreamFile(targetDir.join("clink_x64.exe"), new ByteArrayInputStream(exeBytes), exeBytes.length);
|
||||
|
||||
var batFile = fs.getPath("clink.bat");
|
||||
var batBytes = Files.readAllBytes(batFile);
|
||||
sc.view().writeStreamFile(targetDir.join("clink.bat"), new ByteArrayInputStream(batBytes), batBytes.length);
|
||||
|
||||
var dllFile = fs.getPath("clink_dll_x64.dll");
|
||||
var dllBytes = Files.readAllBytes(dllFile);
|
||||
sc.view().writeStreamFile(targetDir.join("clink_dll_x64.dll"), new ByteArrayInputStream(dllBytes), dllBytes.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
package io.xpipe.app.terminal;
|
||||
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.IntegratedTextAreaComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.password.KeePassXcAssociationKey;
|
||||
import io.xpipe.app.password.KeePassXcManager;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellDialect;
|
||||
import io.xpipe.core.process.ShellScript;
|
||||
import io.xpipe.core.process.ShellTerminalInitCommand;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import javafx.beans.property.Property;
|
||||
@@ -15,6 +12,8 @@ import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
@SuperBuilder
|
||||
@@ -23,8 +22,9 @@ public abstract class ConfigFileTerminalPrompt implements TerminalPrompt {
|
||||
protected static <T extends ConfigFileTerminalPrompt> OptionsBuilder createOptions(Property<T> p, String extension, Function<String, T> creator) {
|
||||
var prop = new SimpleObjectProperty<>(p.getValue() != null ? p.getValue().configuration : null);
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("configuration")
|
||||
.addComp(new IntegratedTextAreaComp(prop, false, "config", new SimpleStringProperty(extension)), prop)
|
||||
.nameAndDescription("terminalPromptConfig")
|
||||
.addComp(new IntegratedTextAreaComp(prop, false, p.getValue() != null ? p.getValue().getId() : "config",
|
||||
new SimpleStringProperty(extension)).prefHeight(400), prop)
|
||||
.bind(
|
||||
() -> {
|
||||
return creator.apply(prop.getValue());
|
||||
@@ -39,15 +39,32 @@ public abstract class ConfigFileTerminalPrompt implements TerminalPrompt {
|
||||
protected abstract FilePath getDefaultConfigFile(ShellControl sc) throws Exception;
|
||||
|
||||
@Override
|
||||
public ShellTerminalInitCommand terminalCommand(ShellControl sc) throws Exception {
|
||||
FilePath configFile;
|
||||
if (configuration == null || configuration.isBlank()) {
|
||||
configFile = getDefaultConfigFile(sc);
|
||||
} else {
|
||||
configFile = prepareCustomConfigFile(sc);
|
||||
}
|
||||
return terminalCommand(sc, configFile);
|
||||
public ShellTerminalInitCommand terminalCommand() throws Exception {
|
||||
return new ShellTerminalInitCommand() {
|
||||
@Override
|
||||
public Optional<String> terminalContent(ShellControl shellControl) throws Exception {
|
||||
if (!installIfNeeded(shellControl)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
FilePath configFile;
|
||||
if (configuration == null || configuration.isBlank()) {
|
||||
configFile = getDefaultConfigFile(shellControl);
|
||||
} else {
|
||||
configFile = prepareCustomConfigFile(shellControl);
|
||||
shellControl.view().writeTextFile(configFile, configuration);
|
||||
}
|
||||
|
||||
var s = shellControl.getShellDialect().addToPathVariableCommand(List.of(getBinaryDirectory(shellControl).toString()), false);
|
||||
return Optional.of(s + "\n" + setupTerminalCommand(shellControl, configFile).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPotentiallyRunInDialect(ShellDialect dialect) {
|
||||
return getSupportedDialects().contains(dialect);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract ShellTerminalInitCommand terminalCommand(ShellControl shellControl, FilePath config) throws Exception;
|
||||
protected abstract ShellScript setupTerminalCommand(ShellControl shellControl, FilePath config) throws Exception;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package io.xpipe.app.terminal;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.util.CommandSupport;
|
||||
import io.xpipe.app.util.GithubReleaseDownloader;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
import io.xpipe.core.process.*;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import javafx.beans.property.Property;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@ToString
|
||||
@Jacksonized
|
||||
@JsonTypeName("starship")
|
||||
public class OhMyPoshTerminalPrompt extends ConfigFileTerminalPrompt {
|
||||
|
||||
public static OptionsBuilder createOptions(Property<OhMyPoshTerminalPrompt> p) {
|
||||
return createOptions(p, "toml", s -> OhMyPoshTerminalPrompt.builder().configuration(s).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDocsLink() {
|
||||
return "https://starship.rs/guide/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "starship";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCanInstall(ShellControl sc) throws Exception {
|
||||
if (sc.getOsType() != OsType.WINDOWS) {
|
||||
CommandSupport.isInPathOrThrow(sc, "curl");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkIfInstalled(ShellControl sc) throws Exception {
|
||||
if (sc.getShellDialect() == ShellDialects.CMD && !ClinkHelper.checkIfInstalled(sc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sc.view().findProgram("starship").isPresent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var extension = OsType.getLocal() == OsType.WINDOWS ? ".exe" : "";
|
||||
return sc.view().fileExists(getBinaryDirectory(sc).join("starship" + extension));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void install(ShellControl sc) throws Exception {
|
||||
if (sc.getShellDialect() == ShellDialects.CMD) {
|
||||
ClinkHelper.install(sc);
|
||||
var configDir = getConfigurationDirectory(sc);
|
||||
sc.view().mkdir(configDir);
|
||||
sc.view().writeTextFile(configDir.join("starship.lua"), "load(io.popen('starship init cmd'):read(\"*a\"))()");
|
||||
}
|
||||
|
||||
var dir = getBinaryDirectory(sc);
|
||||
sc.view().mkdir(dir);
|
||||
if (sc.getOsType() == OsType.WINDOWS) {
|
||||
var file = GithubReleaseDownloader.getDownloadTempFile(
|
||||
"JanDeDobbeleer/oh-my-posh",
|
||||
"posh-windows-amd64.exe",
|
||||
s -> s.equals("posh-windows-amd64.exe"));
|
||||
sc.view().transferLocalFile(file, dir.join("starship.exe"));
|
||||
} else {
|
||||
sc.command("curl -sS https://starship.rs/install.sh | sh /dev/stdin -y --bin-dir \"" + dir + "\" > /dev/null").execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath prepareCustomConfigFile(ShellControl sc) throws Exception {
|
||||
var file = getConfigurationDirectory(sc).join("starship.toml");
|
||||
sc.view().writeTextFile(file, configuration);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath getDefaultConfigFile(ShellControl sc) throws Exception {
|
||||
return sc.view().userHome().join(".config").join("starship.toml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShellScript setupTerminalCommand(ShellControl shellControl, FilePath config) throws Exception {
|
||||
var lines = new ArrayList<String>();
|
||||
if (shellControl.getShellDialect() == ShellDialects.CMD) {
|
||||
lines.add(shellControl.getShellDialect().addToPathVariableCommand(List.of(ClinkHelper.getTargetDir(shellControl).toString()), false));
|
||||
}
|
||||
lines.add(shellControl.getShellDialect().getSetEnvironmentVariableCommand("STARSHIP_CONFIG", config.toString()));
|
||||
if (shellControl.getShellDialect() == ShellDialects.CMD) {
|
||||
lines.add("clink inject --quiet --profile \"" + getConfigurationDirectory(shellControl) + "\"");
|
||||
} else if (ShellDialects.isPowershell(shellControl)) {
|
||||
lines.add("Invoke-Expression (&starship init powershell)");
|
||||
} else if (shellControl.getShellDialect() == ShellDialects.FISH) {
|
||||
lines.add("starship init fish | source");
|
||||
} else {
|
||||
lines.add("eval \"$(starship init " + shellControl.getShellDialect().getId() + ")\"");
|
||||
}
|
||||
return ShellScript.lines(lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShellDialect> getSupportedDialects() {
|
||||
return List.of(ShellDialects.BASH, ShellDialects.ZSH, ShellDialects.FISH, ShellDialects.CMD, ShellDialects.POWERSHELL, ShellDialects.POWERSHELL_CORE);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,21 @@
|
||||
package io.xpipe.app.terminal;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.password.KeePassXcAssociationKey;
|
||||
import io.xpipe.app.password.KeePassXcManager;
|
||||
import io.xpipe.app.util.CommandSupport;
|
||||
import io.xpipe.app.util.GithubReleaseDownloader;
|
||||
import io.xpipe.app.util.OptionsBuilder;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellDialect;
|
||||
import io.xpipe.core.process.ShellDialects;
|
||||
import io.xpipe.core.process.ShellTerminalInitCommand;
|
||||
import io.xpipe.core.process.*;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@@ -37,32 +30,62 @@ public class StarshipTerminalPrompt extends ConfigFileTerminalPrompt {
|
||||
|
||||
@Override
|
||||
public String getDocsLink() {
|
||||
return "";
|
||||
return "https://starship.rs/guide/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "starship";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCanInstall(ShellControl sc) throws Exception {
|
||||
CommandSupport.isInPathOrThrow(sc, "curl");
|
||||
if (sc.getOsType() != OsType.WINDOWS) {
|
||||
CommandSupport.isInPathOrThrow(sc, "curl");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkIfInstalled(ShellControl sc) throws Exception {
|
||||
if (sc.getShellDialect() == ShellDialects.CMD && !ClinkHelper.checkIfInstalled(sc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sc.view().findProgram("starship").isPresent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
var extension = OsType.getLocal() == OsType.WINDOWS ? ".exe" : "";
|
||||
return sc.view().fileExists(getBinaryDirectory(sc).join("starship" + extension));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void install(ShellControl sc) throws Exception {
|
||||
var dir = getBinaryDirectory(sc).join("starship");
|
||||
sc.command("curl -sS https://starship.rs/install.sh | sh /dev/stdin -y --bin-dir \"" + dir + "\" > /dev/null").execute();
|
||||
if (sc.getShellDialect() == ShellDialects.CMD) {
|
||||
ClinkHelper.install(sc);
|
||||
var configDir = getConfigurationDirectory(sc);
|
||||
sc.view().mkdir(configDir);
|
||||
sc.view().writeTextFile(configDir.join("starship.lua"), "load(io.popen('starship init cmd'):read(\"*a\"))()");
|
||||
}
|
||||
|
||||
var dir = getBinaryDirectory(sc);
|
||||
sc.view().mkdir(dir);
|
||||
if (sc.getOsType() == OsType.WINDOWS) {
|
||||
var file = GithubReleaseDownloader.getDownloadTempFile("starship/starship",
|
||||
"starship-x86_64-pc-windows-msvc.zip",
|
||||
s -> s.equals("starship-x86_64-pc-windows-msvc.zip"));
|
||||
try (var fs = FileSystems.newFileSystem(file)) {
|
||||
var exeFile = fs.getPath("starship.exe");
|
||||
sc.view().transferLocalFile(exeFile, dir.join("starship.exe"));
|
||||
}
|
||||
} else {
|
||||
sc.command("curl -sS https://starship.rs/install.sh | sh /dev/stdin -y --bin-dir \"" + dir + "\" > /dev/null").execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath prepareCustomConfigFile(ShellControl sc) throws Exception {
|
||||
var file = getConfigurationDirectory(sc).join("starship").join("starship.toml");
|
||||
var file = getConfigurationDirectory(sc).join("starship.toml");
|
||||
sc.view().writeTextFile(file, configuration);
|
||||
return file;
|
||||
}
|
||||
@@ -73,25 +96,26 @@ public class StarshipTerminalPrompt extends ConfigFileTerminalPrompt {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShellTerminalInitCommand terminalCommand(ShellControl shellControl, FilePath configFile) throws Exception {
|
||||
return new ShellTerminalInitCommand() {
|
||||
@Override
|
||||
public Optional<String> terminalContent(ShellControl shellControl) throws Exception {
|
||||
var s = shellControl.getShellDialect().getSetEnvironmentVariableCommand("STARSHIP_CONFIG", "") + "\n" + "eval \"$(starship init bash)\"";
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPotentiallyRunInDialect(ShellDialect dialect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
protected ShellScript setupTerminalCommand(ShellControl shellControl, FilePath config) throws Exception {
|
||||
var lines = new ArrayList<String>();
|
||||
if (shellControl.getShellDialect() == ShellDialects.CMD) {
|
||||
lines.add(shellControl.getShellDialect().addToPathVariableCommand(List.of(ClinkHelper.getTargetDir(shellControl).toString()), false));
|
||||
}
|
||||
lines.add(shellControl.getShellDialect().getSetEnvironmentVariableCommand("STARSHIP_CONFIG", config.toString()));
|
||||
if (shellControl.getShellDialect() == ShellDialects.CMD) {
|
||||
lines.add("clink inject --quiet --profile \"" + getConfigurationDirectory(shellControl) + "\"");
|
||||
} else if (ShellDialects.isPowershell(shellControl)) {
|
||||
lines.add("Invoke-Expression (&starship init powershell)");
|
||||
} else if (shellControl.getShellDialect() == ShellDialects.FISH) {
|
||||
lines.add("starship init fish | source");
|
||||
} else {
|
||||
lines.add("eval \"$(starship init " + shellControl.getShellDialect().getId() + ")\"");
|
||||
}
|
||||
return ShellScript.lines(lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ShellDialect> getSupportedDialects() {
|
||||
return List.of(ShellDialects.BASH);
|
||||
return List.of(ShellDialects.BASH, ShellDialects.ZSH, ShellDialects.FISH, ShellDialects.CMD, ShellDialects.POWERSHELL, ShellDialects.POWERSHELL_CORE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.xpipe.app.terminal;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.util.ShellTemp;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.process.ShellDialect;
|
||||
@@ -22,18 +23,31 @@ public interface TerminalPrompt {
|
||||
String getDocsLink();
|
||||
|
||||
default FilePath getConfigurationDirectory(ShellControl sc) throws Exception {
|
||||
return ShellTemp.createUserSpecificTempDataDirectory(sc, "prompt");
|
||||
var d = ShellTemp.createUserSpecificTempDataDirectory(sc, "prompt").join(getId());
|
||||
sc.view().mkdir(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
default FilePath getBinaryDirectory(ShellControl sc) throws Exception {
|
||||
return ShellTemp.createUserSpecificTempDataDirectory(sc, "bin");
|
||||
var d = ShellTemp.createUserSpecificTempDataDirectory(sc, "bin").join(getId());
|
||||
sc.view().mkdir(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
default void installIfNeeded(ShellControl sc) throws Exception {
|
||||
if (checkIfInstalled(sc)) {
|
||||
checkCanInstall(sc);
|
||||
install(sc);
|
||||
String getId();
|
||||
|
||||
default boolean installIfNeeded(ShellControl sc) throws Exception {
|
||||
if (!checkIfInstalled(sc)) {
|
||||
try {
|
||||
checkCanInstall(sc);
|
||||
install(sc);
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).omit().handle();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void checkCanInstall(ShellControl sc) throws Exception;
|
||||
@@ -42,7 +56,7 @@ public interface TerminalPrompt {
|
||||
|
||||
void install(ShellControl sc) throws Exception;
|
||||
|
||||
ShellTerminalInitCommand terminalCommand(ShellControl shellControl) throws Exception;
|
||||
ShellTerminalInitCommand terminalCommand() throws Exception;
|
||||
|
||||
List<ShellDialect> getSupportedDialects();
|
||||
}
|
||||
|
||||
@@ -12,14 +12,8 @@ public class TerminalPromptManager {
|
||||
return;
|
||||
}
|
||||
|
||||
var d = p.getSupportedDialects();
|
||||
if (!d.contains(sc.getShellDialect())) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
p.installIfNeeded(sc);
|
||||
sc.withInitSnippet(p.terminalCommand(sc));
|
||||
sc.withInitSnippet(p.terminalCommand());
|
||||
} catch (Exception e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
import io.xpipe.core.util.JacksonMapper;
|
||||
import lombok.Value;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class GithubReleaseDownloader {
|
||||
|
||||
public static Path getDownloadTempFile(String repository, String id, Predicate<String> filter) throws Exception {
|
||||
var tempDir = ShellTemp.getLocalTempDataDirectory("github");
|
||||
var temp = tempDir.resolve(id);
|
||||
if (Files.exists(temp)) {
|
||||
return temp;
|
||||
}
|
||||
|
||||
var request = HttpRequest.newBuilder().GET().uri(URI.create(getDownloadUrl(repository, filter))).build();
|
||||
var r = HttpHelper.client().send(request,
|
||||
HttpResponse.BodyHandlers.ofByteArray());
|
||||
if (r.statusCode() >= 400) {
|
||||
throw new IOException(new String(r.body(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
Files.createDirectories(tempDir);
|
||||
Files.write(temp, r.body());
|
||||
return temp;
|
||||
}
|
||||
|
||||
private static String getDownloadUrl(String repository, Predicate<String> filter) throws Exception {
|
||||
var request = HttpRequest.newBuilder().GET().uri(URI.create("https://api.github.com/repos/" + repository + "/releases")).build();
|
||||
var r = HttpHelper.client().send(request,
|
||||
HttpResponse.BodyHandlers.ofString());
|
||||
if (r.statusCode() >= 400) {
|
||||
throw new IOException(r.body());
|
||||
}
|
||||
|
||||
var json = JacksonMapper.getDefault().readTree(r.body());
|
||||
var latest = json.get(0);
|
||||
var assets = latest.required("assets");
|
||||
for (var asset : assets) {
|
||||
var name = asset.required("name").asText();
|
||||
if (filter.test(name)) {
|
||||
var url = asset.required("browser_download_url").asText();
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unable to find download url for " + repository);
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class ShellTemp {
|
||||
proc.command("chmod 777 " + proc.getShellDialect().fileArgument(base))
|
||||
.executeAndCheck();
|
||||
var user = proc.getShellDialect().printUsernameCommand(proc).readStdoutOrThrow();
|
||||
base = temp.join(user);
|
||||
base = base.join(user);
|
||||
} else {
|
||||
var temp = proc.getSystemTemporaryDirectory();
|
||||
base = temp.join("xpipe");
|
||||
|
||||
@@ -18,12 +18,12 @@ public abstract class SimpleFilterInputStream extends FilterInputStream {
|
||||
@Override
|
||||
public int read(byte @NonNull [] b, int off, int len) throws IOException {
|
||||
for (int i = off; i < off + len; i++) {
|
||||
var r = (byte) read();
|
||||
var r = read();
|
||||
if (r == -1) {
|
||||
return i - off == 0 ? -1 : i - off;
|
||||
}
|
||||
|
||||
b[i] = r;
|
||||
b[i] = (byte) r;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.xpipe.core.process;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Value
|
||||
@@ -12,6 +13,10 @@ public class ShellScript {
|
||||
return new ShellScript(Arrays.stream(lines).collect(Collectors.joining("\n")));
|
||||
}
|
||||
|
||||
public static ShellScript lines(List<String> lines) {
|
||||
return new ShellScript(lines.stream().collect(Collectors.joining("\n")));
|
||||
}
|
||||
|
||||
String value;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,8 @@ package io.xpipe.core.process;
|
||||
import io.xpipe.core.store.FilePath;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ShellView {
|
||||
@@ -66,6 +68,12 @@ public class ShellView {
|
||||
.executeAndCheck();
|
||||
}
|
||||
|
||||
public void mkdir(FilePath path) throws Exception {
|
||||
shellControl.command(getDialect()
|
||||
.getMkdirsCommand(path.toString()))
|
||||
.execute();
|
||||
}
|
||||
|
||||
public boolean directoryExists(FilePath path) throws Exception {
|
||||
return getDialect().directoryExists(shellControl, path.toString()).executeAndCheck();
|
||||
}
|
||||
@@ -104,6 +112,12 @@ public class ShellView {
|
||||
return out.flatMap(s -> s.lines().findFirst()).map(String::trim).map(s -> FilePath.of(s));
|
||||
}
|
||||
|
||||
public void transferLocalFile(Path localPath, FilePath target) throws Exception {
|
||||
try (var in = Files.newInputStream(localPath)) {
|
||||
writeStreamFile(target, in, in.available());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInPath(String executable) throws Exception {
|
||||
return shellControl.executeSimpleBooleanCommand(
|
||||
shellControl.getShellDialect().getWhichCommand(executable));
|
||||
|
||||
@@ -15,16 +15,16 @@ import java.util.*;
|
||||
|
||||
public class ScriptStoreSetup {
|
||||
|
||||
public static ShellControl controlWithDefaultScripts(ShellControl pc) {
|
||||
return controlWithScripts(pc, getEnabledScripts());
|
||||
public static void controlWithDefaultScripts(ShellControl pc) {
|
||||
controlWithScripts(pc, getEnabledScripts());
|
||||
}
|
||||
|
||||
public static ShellControl controlWithScripts(
|
||||
public static void controlWithScripts(
|
||||
ShellControl pc, List<DataStoreEntryRef<ScriptStore>> enabledScripts) {
|
||||
try {
|
||||
// Don't copy scripts if we don't want to modify the file system
|
||||
if (!pc.getEffectiveSecurityPolicy().permitTempScriptCreation()) {
|
||||
return pc;
|
||||
return;
|
||||
}
|
||||
|
||||
var initFlattened = flatten(enabledScripts).stream()
|
||||
@@ -36,7 +36,7 @@ public class ScriptStoreSetup {
|
||||
|
||||
// Optimize if we have nothing to do
|
||||
if (initFlattened.isEmpty() && bringFlattened.isEmpty()) {
|
||||
return pc;
|
||||
return;
|
||||
}
|
||||
|
||||
initFlattened.forEach(s -> {
|
||||
@@ -77,7 +77,6 @@ public class ScriptStoreSetup {
|
||||
}
|
||||
});
|
||||
}
|
||||
return pc;
|
||||
} catch (StackOverflowError t) {
|
||||
throw ErrorEvent.expected(
|
||||
new RuntimeException("Unable to set up scripts. Is there a circular script dependency?", t));
|
||||
|
||||
@@ -10,6 +10,8 @@ import io.xpipe.app.ext.*;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.terminal.TerminalLauncher;
|
||||
import io.xpipe.app.terminal.TerminalPromptManager;
|
||||
import io.xpipe.app.terminal.TerminalProxyManager;
|
||||
import io.xpipe.app.util.ShellStoreFormat;
|
||||
import io.xpipe.ext.base.script.ScriptStoreSetup;
|
||||
|
||||
@@ -25,7 +27,9 @@ public interface ShellStoreProvider extends DataStoreProvider {
|
||||
public void execute() throws Exception {
|
||||
var replacement = ProcessControlProvider.get().replace(entry.ref());
|
||||
ShellStore store = replacement.getStore().asNeeded();
|
||||
var control = ScriptStoreSetup.controlWithDefaultScripts(store.standaloneControl());
|
||||
var control = store.standaloneControl();
|
||||
ScriptStoreSetup.controlWithDefaultScripts(control);
|
||||
TerminalPromptManager.configurePromptScript(control);
|
||||
TerminalLauncher.open(
|
||||
replacement.get(),
|
||||
DataStorage.get().getStoreEntryDisplayName(replacement.get()),
|
||||
|
||||
Generated
+3
@@ -1381,4 +1381,7 @@ refreshOpenpubkey=Refresh openpubkey identity
|
||||
refreshOpenpubkeyDescription=Run opkssh refresh to make the openpubkey identity valid again
|
||||
all=All
|
||||
terminalPrompt=Terminal prompt
|
||||
terminalPromptDescription=The terminal prompt tool to use in your remote terminals.\n\nEnabling a terminal prompt will automatically set up and configure the prompt tool on the target system when opening a terminal session. This will increase the terminal loading time for the first time while the prompt is being set up on the remote system.
|
||||
terminalPromptConfiguration=Terminal prompt configuration
|
||||
terminalPromptConfig=Config file
|
||||
terminalPromptConfigDescription=The custom config file to apply to the prompt. This config will be automatically set up on the target system when the terminal is initialized and used as the default prompt config.\n\nIf you want to use the existing default config file on each system, you can leave this field empty.
|
||||
|
||||
Reference in New Issue
Block a user