This commit is contained in:
crschnick 2023-05-02 18:55:35 +00:00
parent e0678aaf74
commit 78a51f5f52
29 changed files with 179 additions and 59 deletions

View file

@ -81,7 +81,7 @@ final class FileContextMenu extends ContextMenu {
ShellControl pc = model.getFileSystem().getShell().orElseThrow();
var e = pc.getShellDialect().getMakeExecutableCommand(entry.getPath());
if (e != null) {
pc.executeBooleanSimpleCommand(e);
pc.executeSimpleBooleanCommand(e);
}
var cmd = pc.command("\"" + entry.getPath() + "\"").prepareTerminalOpen();
TerminalHelper.open(FilenameUtils.getBaseName(entry.getPath()), cmd);
@ -96,10 +96,10 @@ final class FileContextMenu extends ContextMenu {
ShellControl pc = model.getFileSystem().getShell().orElseThrow();
var e = pc.getShellDialect().getMakeExecutableCommand(entry.getPath());
if (e != null) {
pc.executeBooleanSimpleCommand(e);
pc.executeSimpleBooleanCommand(e);
}
var cmd = ScriptHelper.createDetachCommand(pc, "\"" + entry.getPath() + "\"");
pc.executeBooleanSimpleCommand(cmd);
pc.executeSimpleBooleanCommand(cmd);
});
event.consume();
});

View file

@ -24,7 +24,7 @@ public class FileSystemHelper {
var current = !(model.getStore().getValue() instanceof LocalStore)
? fileSystem
.getShellControl()
.executeStringSimpleCommand(
.executeSimpleStringCommand(
fileSystem.getShellControl().getShellDialect().getPrintWorkingDirectoryCommand())
: fileSystem
.getShell()

View file

@ -275,7 +275,7 @@ final class OpenFileSystemModel {
BusyProperty.execute(busy, () -> {
if (store.getValue() instanceof ShellStore s) {
var connection = ((ConnectionFileSystem) fileSystem).getShellControl();
var command = s.create()
var command = s.control()
.initWith(connection.getShellDialect().getCdCommand(directory))
.prepareTerminalOpen();
TerminalHelper.open(directory, command);

View file

@ -44,7 +44,7 @@ public class BrowseDirectoryComp extends SimpleComp {
"launchDebugMode",
new ButtonComp(AppI18n.observable("launchDebugMode"), () -> {
OperationMode.executeAfterShutdown(() -> {
try (var sc = ShellStore.createLocal().create().start()) {
try (var sc = ShellStore.createLocal().control().start()) {
var script = FileNames.join(
XPipeInstallation.getCurrentInstallationBasePath()
.toString(),

View file

@ -45,7 +45,7 @@ public interface ActionProvider {
interface Action {
boolean requiresPlatform();
boolean requiresJavaFXPlatform();
void execute() throws Exception;
}

View file

@ -31,14 +31,14 @@ public abstract class LauncherInput {
}
});
var requiresPlatform = all.stream().anyMatch(launcherInput -> launcherInput.requiresPlatform());
var requiresPlatform = all.stream().anyMatch(launcherInput -> launcherInput.requiresJavaFXPlatform());
if (requiresPlatform) {
OperationMode.switchTo(OperationMode.GUI);
}
var hasGui = OperationMode.get() == OperationMode.GUI;
all.forEach(launcherInput -> {
if (!hasGui && launcherInput.requiresPlatform()) {
if (!hasGui && launcherInput.requiresJavaFXPlatform()) {
return;
}
@ -118,7 +118,7 @@ public abstract class LauncherInput {
}
@Override
public boolean requiresPlatform() {
public boolean requiresJavaFXPlatform() {
return true;
}
}

View file

@ -77,7 +77,7 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
public boolean isAvailable() {
try (ShellControl pc = LocalStore.getShell()) {
return pc.executeBooleanSimpleCommand(pc.getShellDialect().getWhichCommand(executable));
return pc.executeSimpleBooleanCommand(pc.getShellDialect().getWhichCommand(executable));
} catch (Exception e) {
ErrorEvent.fromThrowable(e).omit().handle();
return false;

View file

@ -332,7 +332,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
public boolean isAvailable() {
try (ShellControl pc = LocalStore.getShell()) {
return pc.executeBooleanSimpleCommand(pc.getShellDialect().getWhichCommand(executable));
return pc.executeSimpleBooleanCommand(pc.getShellDialect().getWhichCommand(executable));
} catch (Exception e) {
ErrorEvent.fromThrowable(e).omit().handle();
return false;

View file

@ -141,7 +141,7 @@ public class AppInstaller {
@Override
public void installLocal(String file) throws Exception {
var shellProcessControl = ShellStore.createLocal().create().start();
var shellProcessControl = ShellStore.createLocal().control().start();
var exec = XPipeInstallation.getInstallationExecutable(
shellProcessControl,
XPipeInstallation.getDefaultInstallationBasePath(shellProcessControl, false));
@ -187,7 +187,7 @@ public class AppInstaller {
@Override
public void installLocal(String file) throws Exception {
var command = ShellStore.createLocal()
.create()
.control()
.subShell(ShellDialects.BASH)
.command(String.format(
"""
@ -228,7 +228,7 @@ public class AppInstaller {
@Override
public void installLocal(String file) throws Exception {
var command = ShellStore.createLocal().create().subShell(ShellDialects.BASH).command(String.format(
var command = ShellStore.createLocal().control().subShell(ShellDialects.BASH).command(String.format(
"""
function exec {
echo "+ sudo rpm -U -v --force \\"%s\\""
@ -266,7 +266,7 @@ public class AppInstaller {
@Override
public void installLocal(String file) throws Exception {
var command = ShellStore.createLocal().create().subShell(ShellDialects.BASH).command(String.format(
var command = ShellStore.createLocal().control().subShell(ShellDialects.BASH).command(String.format(
"""
function exec {
echo "+ sudo installer -verboseR -allowUntrusted -pkg \\"%s\\" -target /"

View file

@ -30,8 +30,8 @@ public class ChocoUpdater extends UpdateHandler {
}
public AvailableRelease refreshUpdateCheckImpl() throws Exception {
try (var sc = ShellStore.createLocal().create().start()) {
var latest = sc.executeStringSimpleCommand(
try (var sc = ShellStore.createLocal().control().start()) {
var latest = sc.executeSimpleStringCommand(
"choco outdated -r --nocolor").lines().filter(s -> s.startsWith("xpipe")).findAny().orElseThrow().split("\\|")[2];
var isUpdate = isUpdate(latest);
var rel = new AvailableRelease(

View file

@ -35,8 +35,8 @@ public class XPipeInstanceHelper {
}
public static boolean isSupported(ShellStore host) {
try (var pc = host.create().start();
var cmd = pc.command(List.of("xpipe"))) {
try (var pc = host.control().start();
var cmd = pc.command(List.of("xpipe"))) {
cmd.discardOrThrow();
return true;
} catch (Exception e) {

View file

@ -23,7 +23,7 @@ public class ApplicationHelper {
}
public static boolean isInPath(ShellControl processControl, String executable) throws Exception {
return processControl.executeBooleanSimpleCommand(
return processControl.executeSimpleBooleanCommand(
processControl.getShellDialect().getWhichCommand(executable));
}

View file

@ -17,7 +17,7 @@ public class MacOsPermissions {
var state = new SimpleBooleanProperty(true);
try (var pc = LocalStore.getShell().start()) {
while (state.get()) {
var success = pc.executeBooleanSimpleCommand(
var success = pc.executeSimpleBooleanCommand(
"osascript -e 'tell application \"System Events\" to keystroke \"t\"'");
if (success) {
Platform.runLater(() -> {

View file

@ -53,7 +53,7 @@ public class BeaconClient implements AutoCloseable {
}
public static BeaconClient connectProxy(ShellStore proxy) throws Exception {
var control = proxy.create().start();
var control = proxy.control().start();
if (!ProxyManagerProvider.get().setup(control)) {
throw new IOException("X-Pipe connector required to perform operation");
}

View file

@ -36,7 +36,7 @@ public class LocalStore extends JacksonizedValue implements ShellStore {
@Override
public FileSystem createFileSystem() {
return new ConnectionFileSystem(ShellStore.createLocal().create(), LocalStore.this) {
return new ConnectionFileSystem(ShellStore.createLocal().control(), LocalStore.this) {
@Override
public FileSystemStore getStore() {
@ -128,7 +128,7 @@ public class LocalStore extends JacksonizedValue implements ShellStore {
}
@Override
public ShellControl createControl() {
public ShellControl createBasicControl() {
return ProcessControlProvider.createLocal(true);
}
}

View file

@ -37,7 +37,7 @@ public interface OsType {
@Override
public String getHomeDirectory(ShellControl pc) throws Exception {
return pc.executeStringSimpleCommand(
return pc.executeSimpleStringCommand(
pc.getShellDialect().getPrintEnvironmentVariableCommand("USERPROFILE"));
}
@ -48,7 +48,7 @@ public interface OsType {
@Override
public String getTempDirectory(ShellControl pc) throws Exception {
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("TEMP"));
return pc.executeSimpleStringCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("TEMP"));
}
@Override
@ -62,13 +62,13 @@ public interface OsType {
@Override
public String determineOperatingSystemName(ShellControl pc) throws Exception {
try {
return pc.executeStringSimpleCommand("wmic os get Caption")
return pc.executeSimpleStringCommand("wmic os get Caption")
.lines()
.skip(1)
.collect(Collectors.joining())
.trim()
+ " "
+ pc.executeStringSimpleCommand("wmic os get Version")
+ pc.executeSimpleStringCommand("wmic os get Version")
.lines()
.skip(1)
.collect(Collectors.joining())
@ -84,7 +84,7 @@ public interface OsType {
@Override
public String getHomeDirectory(ShellControl pc) throws Exception {
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
return pc.executeSimpleStringCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
}
@Override
@ -142,12 +142,12 @@ public interface OsType {
@Override
public String getHomeDirectory(ShellControl pc) throws Exception {
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
return pc.executeSimpleStringCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
}
@Override
public String getTempDirectory(ShellControl pc) throws Exception {
var found = pc.executeStringSimpleCommand(pc.getShellDialect().getPrintVariableCommand("TMPDIR"));
var found = pc.executeSimpleStringCommand(pc.getShellDialect().getPrintVariableCommand("TMPDIR"));
// This variable is not defined for root users, so manually fix it. Why? ...
if (found.isBlank()) {
@ -174,7 +174,7 @@ public interface OsType {
@Override
public String determineOperatingSystemName(ShellControl pc) throws Exception {
var properties = getProperties(pc);
var name = pc.executeStringSimpleCommand(
var name = pc.executeSimpleStringCommand(
"awk '/SOFTWARE LICENSE AGREEMENT FOR macOS/' '/System/Library/CoreServices/Setup "
+ "Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf' | "
+ "awk -F 'macOS ' '{print $NF}' | awk '{print substr($0, 0, length($0)-1)}'");

View file

@ -29,13 +29,13 @@ public interface ShellControl extends ProcessControl {
public void checkRunning() throws Exception;
default String executeStringSimpleCommand(String command) throws Exception {
default String executeSimpleStringCommand(String command) throws Exception {
try (CommandControl c = command(command).start()) {
return c.readOrThrow();
}
}
default boolean executeBooleanSimpleCommand(String command) throws Exception {
default boolean executeSimpleBooleanCommand(String command) throws Exception {
try (CommandControl c = command(command).start()) {
return c.discardAndCheckExit();
}
@ -55,9 +55,9 @@ public interface ShellControl extends ProcessControl {
}
}
default String executeStringSimpleCommand(ShellDialect type, String command) throws Exception {
default String executeSimpleStringCommand(ShellDialect type, String command) throws Exception {
try (var sub = subShell(type).start()) {
return sub.executeStringSimpleCommand(command);
return sub.executeSimpleStringCommand(command);
}
}

View file

@ -5,8 +5,8 @@ import io.xpipe.core.process.ShellControl;
public interface DelegateShellStore extends ShellStore {
@Override
default ShellControl createControl() {
return getDelegateHost().create();
default ShellControl createBasicControl() {
return getDelegateHost().control();
}
ShellStore getDelegateHost();

View file

@ -19,16 +19,16 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStor
@Override
default FileSystem createFileSystem() {
return new ConnectionFileSystem(create(), this);
return new ConnectionFileSystem(control(), this);
}
@Override
default String prepareLaunchCommand() throws Exception {
return create().prepareTerminalOpen();
return control().prepareTerminalOpen();
}
default ShellControl create() {
var pc = createControl();
default ShellControl control() {
var pc = createBasicControl();
pc.onInit(processControl -> {
setState("type", processControl.getShellDialect());
setState("os", processControl.getOsType());
@ -49,21 +49,21 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStor
return getState("charset", Charset.class, null);
}
ShellControl createControl();
ShellControl createBasicControl();
public default ShellDialect determineType() throws Exception {
try (var pc = create().start()) {
try (var pc = control().start()) {
return pc.getShellDialect();
}
}
@Override
default void validate() throws Exception {
try (ShellControl pc = create().start()) {}
try (ShellControl pc = control().start()) {}
}
public default String queryMachineName() throws Exception {
try (var pc = create().start()) {
try (var pc = control().start()) {
var operatingSystem = pc.getOsType();
return operatingSystem.determineOperatingSystemName(pc);
}

View file

@ -170,7 +170,7 @@ public class XPipeInstallation {
public static String getDataBasePath(ShellControl p) throws Exception {
if (p.getOsType().equals(OsType.WINDOWS)) {
var base = p.executeStringSimpleCommand(p.getShellDialect().getPrintVariableCommand("userprofile"));
var base = p.executeSimpleStringCommand(p.getShellDialect().getPrintVariableCommand("userprofile"));
return FileNames.join(base, ".xpipe");
} else {
return FileNames.join("~", ".xpipe");
@ -222,7 +222,7 @@ public class XPipeInstallation {
public static String getDefaultInstallationBasePath(ShellControl p, boolean acceptPortable)
throws Exception {
if (acceptPortable) {
var customHome = p.executeStringSimpleCommand(p.getShellDialect().getPrintVariableCommand("XPIPE_HOME"));
var customHome = p.executeSimpleStringCommand(p.getShellDialect().getPrintVariableCommand("XPIPE_HOME"));
if (!customHome.isEmpty()) {
return customHome;
}
@ -230,7 +230,7 @@ public class XPipeInstallation {
String path = null;
if (p.getOsType().equals(OsType.WINDOWS)) {
var base = p.executeStringSimpleCommand(p.getShellDialect().getPrintVariableCommand("LOCALAPPDATA"));
var base = p.executeSimpleStringCommand(p.getShellDialect().getPrintVariableCommand("LOCALAPPDATA"));
path = FileNames.join(base, "X-Pipe");
} else if (p.getOsType().equals(OsType.LINUX)) {
path = "/opt/xpipe";

View file

@ -22,7 +22,7 @@ public class XPipeTempDirectory {
"Unable to access or create temporary directory " + dir);
if (proc.getOsType().equals(OsType.LINUX) || proc.getOsType().equals(OsType.MACOS)) {
proc.executeBooleanSimpleCommand("chmod 777 \"" + dir + "\"");
proc.executeSimpleBooleanCommand("chmod 777 \"" + dir + "\"");
}
}
@ -31,7 +31,7 @@ public class XPipeTempDirectory {
public static void clearSubDirectory(ShellControl proc) throws Exception {
var dir = getSubDirectory(proc);
if (!proc.executeBooleanSimpleCommand(proc.getShellDialect().getFileDeleteCommand(dir))) {
if (!proc.executeSimpleBooleanCommand(proc.getShellDialect().getFileDeleteCommand(dir))) {
throw new IOException("Unable to delete temporary directory " + dir);
}
}

View file

@ -18,7 +18,7 @@ public class AddStoreAction implements ActionProvider {
DataStore store;
@Override
public boolean requiresPlatform() {
public boolean requiresJavaFXPlatform() {
return true;
}

View file

@ -16,7 +16,7 @@ public class DeleteStoreChildrenAction implements ActionProvider {
DataStoreEntry store;
@Override
public boolean requiresPlatform() {
public boolean requiresJavaFXPlatform() {
return false;
}

View file

@ -17,7 +17,7 @@ public class EditStoreAction implements ActionProvider {
DataStoreEntry store;
@Override
public boolean requiresPlatform() {
public boolean requiresJavaFXPlatform() {
return true;
}

View file

@ -19,7 +19,7 @@ public class FileBrowseAction implements ActionProvider {
FileStore store;
@Override
public boolean requiresPlatform() {
public boolean requiresJavaFXPlatform() {
return false;
}

View file

@ -17,7 +17,7 @@ public class FileEditAction implements ActionProvider {
FileStore store;
@Override
public boolean requiresPlatform() {
public boolean requiresJavaFXPlatform() {
return false;
}

View file

@ -0,0 +1,120 @@
package io.xpipe.ext.base.actions;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.process.CommandControl;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.store.ShellStore;
import javafx.beans.value.ObservableValue;
import lombok.Value;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class SampleAction implements ActionProvider {
@Value
static class Action implements ActionProvider.Action {
DataStoreEntry entry;
@Override
public boolean requiresJavaFXPlatform() {
// Do we require the JavaFX platform to be running?
return false;
}
@Override
public void execute() throws Exception {
// Start a shell control from the shell connection store
try (ShellControl sc = ((ShellStore) entry.getStore()).control().start()) {
// Simple commands can be executed in one line
// The shell dialects also provide the proper command syntax for common commands like echo
String echoOut =
sc.executeSimpleStringCommand(sc.getShellDialect().getEchoCommand("hello!", false));
// You can also implement custom handling for more complex commands
try (CommandControl cc = sc.command("ls").start()) {
// Discard stderr
cc.discardErr();
// Read the stdout lines as a stream
BufferedReader reader = new BufferedReader(new InputStreamReader(cc.getStdout(), cc.getCharset()));
reader.lines().filter(s -> s != null).forEach(s -> {
System.out.println(s);
});
// Waits for command completion and returns exit code
if (cc.getExitCode() != 0) {
// Handle failure
}
}
// Commands can also be more complex and span multiple lines.
// In this case, X-Pipe will internally write a command to a script file and then execute the script
try (CommandControl cc = sc.command(
"""
VAR = "value"
echo "$VAR"
"""
).start()) {
var output = cc.readOrThrow();
}
// More customization options
// If the command should be run as root, the command will be executed with
// sudo and the optional sudo password automatically provided by X-Pipe.
// You can also set a custom working directory
try (CommandControl cc = sc.command("kill <pid>").elevated().workingDirectory("/").start()) {
// Discard any output but throw an exception the exit code is not 0
cc.discardOrThrow();
}
// Start a bash sub shell. Useful if the login shell is different
try (ShellControl bash = sc.subShell("bash").start()) {
// ...
}
}
}
}
@Override
public DataStoreCallSite<?> getDataStoreCallSite() {
// Call sites represent different ways of invoking the action.
// In this case, this represents a button that is shown for all stored shell connections.
return new DataStoreCallSite<ShellStore>() {
@Override
public Action createAction(ShellStore store) {
return new Action(DataStorage.get().getStoreEntry(store));
}
@Override
public Class<ShellStore> getApplicableClass() {
// For which general type of connection store to make this action available.
return ShellStore.class;
}
@Override
public boolean isApplicable(ShellStore o) throws Exception {
// Allows you to individually check whether this action should be available for the specific store.
// In this case it should only be available for remote shell connections, not local ones.
return !ShellStore.isLocal(o);
}
@Override
public ObservableValue<String> getName(ShellStore store) {
// The displayed name of the action, allows you to use translation keys.
return AppI18n.observable("installConnector");
}
@Override
public String getIcon(ShellStore store) {
// The ikonli icon of the button.
return "mdi2c-code-greater-than";
}
};
}
}

View file

@ -21,7 +21,7 @@ public class ShareStoreAction implements ActionProvider {
DataStore store;
@Override
public boolean requiresPlatform() {
public boolean requiresJavaFXPlatform() {
return false;
}

View file

@ -22,7 +22,7 @@ public class StreamExportAction implements ActionProvider {
StreamDataStore store;
@Override
public boolean requiresPlatform() {
public boolean requiresJavaFXPlatform() {
return true;
}