mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-10-01 09:40:35 +13:00
Improvements and refactor for shell connections
This commit is contained in:
parent
d5a7e2fb64
commit
d879c13aa4
38 changed files with 700 additions and 191 deletions
|
@ -132,7 +132,7 @@ application {
|
||||||
|
|
||||||
run {
|
run {
|
||||||
systemProperty 'io.xpipe.app.mode', 'gui'
|
systemProperty 'io.xpipe.app.mode', 'gui'
|
||||||
systemProperty 'io.xpipe.app.dataDir', "$projectDir/local_stage/"
|
systemProperty 'io.xpipe.app.dataDir', "$projectDir/local7/"
|
||||||
systemProperty 'io.xpipe.app.writeLogs', "true"
|
systemProperty 'io.xpipe.app.writeLogs', "true"
|
||||||
systemProperty 'io.xpipe.app.writeSysOut', "true"
|
systemProperty 'io.xpipe.app.writeSysOut', "true"
|
||||||
systemProperty 'io.xpipe.app.developerMode', "true"
|
systemProperty 'io.xpipe.app.developerMode', "true"
|
||||||
|
|
|
@ -7,7 +7,7 @@ import io.xpipe.app.ext.DataSourceProvider;
|
||||||
import io.xpipe.app.util.*;
|
import io.xpipe.app.util.*;
|
||||||
import io.xpipe.core.impl.FileStore;
|
import io.xpipe.core.impl.FileStore;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.store.FileSystem;
|
import io.xpipe.core.store.FileSystem;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
|
@ -73,7 +73,7 @@ final class FileContextMenu extends ContextMenu {
|
||||||
var execute = new MenuItem("Run in terminal");
|
var execute = new MenuItem("Run in terminal");
|
||||||
execute.setOnAction(event -> {
|
execute.setOnAction(event -> {
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
ThreadHelper.runFailableAsync(() -> {
|
||||||
ShellProcessControl pc =
|
ShellControl pc =
|
||||||
model.getFileSystem().getShell().orElseThrow();
|
model.getFileSystem().getShell().orElseThrow();
|
||||||
pc.executeBooleanSimpleCommand(pc.getShellDialect().getMakeExecutableCommand(entry.getPath()));
|
pc.executeBooleanSimpleCommand(pc.getShellDialect().getMakeExecutableCommand(entry.getPath()));
|
||||||
var cmd = pc.command("\"" + entry.getPath() + "\"").prepareTerminalOpen();
|
var cmd = pc.command("\"" + entry.getPath() + "\"").prepareTerminalOpen();
|
||||||
|
@ -86,7 +86,7 @@ final class FileContextMenu extends ContextMenu {
|
||||||
var executeInBackground = new MenuItem("Run in background");
|
var executeInBackground = new MenuItem("Run in background");
|
||||||
executeInBackground.setOnAction(event -> {
|
executeInBackground.setOnAction(event -> {
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
ThreadHelper.runFailableAsync(() -> {
|
||||||
ShellProcessControl pc =
|
ShellControl pc =
|
||||||
model.getFileSystem().getShell().orElseThrow();
|
model.getFileSystem().getShell().orElseThrow();
|
||||||
pc.executeBooleanSimpleCommand(pc.getShellDialect().getMakeExecutableCommand(entry.getPath()));
|
pc.executeBooleanSimpleCommand(pc.getShellDialect().getMakeExecutableCommand(entry.getPath()));
|
||||||
var cmd = ScriptHelper.createDetachCommand(pc, "\"" + entry.getPath() + "\"");
|
var cmd = ScriptHelper.createDetachCommand(pc, "\"" + entry.getPath() + "\"");
|
||||||
|
|
|
@ -178,9 +178,9 @@ final class OpenFileSystemModel {
|
||||||
|
|
||||||
var current = !(fileSystem instanceof LocalStore) && fs instanceof ConnectionFileSystem connectionFileSystem
|
var current = !(fileSystem instanceof LocalStore) && fs instanceof ConnectionFileSystem connectionFileSystem
|
||||||
? connectionFileSystem
|
? connectionFileSystem
|
||||||
.getShellProcessControl()
|
.getShellControl()
|
||||||
.executeStringSimpleCommand(connectionFileSystem
|
.executeStringSimpleCommand(connectionFileSystem
|
||||||
.getShellProcessControl()
|
.getShellControl()
|
||||||
.getShellDialect()
|
.getShellDialect()
|
||||||
.getPrintWorkingDirectoryCommand())
|
.getPrintWorkingDirectoryCommand())
|
||||||
: null;
|
: null;
|
||||||
|
@ -199,7 +199,7 @@ final class OpenFileSystemModel {
|
||||||
ThreadHelper.runFailableAsync(() -> {
|
ThreadHelper.runFailableAsync(() -> {
|
||||||
BusyProperty.execute(busy, () -> {
|
BusyProperty.execute(busy, () -> {
|
||||||
if (store.getValue() instanceof ShellStore s) {
|
if (store.getValue() instanceof ShellStore s) {
|
||||||
var connection = ((ConnectionFileSystem) fileSystem).getShellProcessControl();
|
var connection = ((ConnectionFileSystem) fileSystem).getShellControl();
|
||||||
var command = s.create()
|
var command = s.create()
|
||||||
.initWith(List.of(connection.getShellDialect().getCdCommand(directory)))
|
.initWith(List.of(connection.getShellDialect().getCdCommand(directory)))
|
||||||
.prepareTerminalOpen();
|
.prepareTerminalOpen();
|
||||||
|
|
|
@ -8,6 +8,7 @@ import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class ListBoxViewComp<T> extends Comp<CompStructure<VBox>> {
|
public class ListBoxViewComp<T> extends Comp<CompStructure<ScrollPane>> {
|
||||||
|
|
||||||
private final ObservableList<T> shown;
|
private final ObservableList<T> shown;
|
||||||
private final ObservableList<T> all;
|
private final ObservableList<T> all;
|
||||||
|
@ -29,7 +30,7 @@ public class ListBoxViewComp<T> extends Comp<CompStructure<VBox>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompStructure<VBox> createBase() {
|
public CompStructure<ScrollPane> createBase() {
|
||||||
Map<T, Region> cache = new HashMap<>();
|
Map<T, Region> cache = new HashMap<>();
|
||||||
|
|
||||||
VBox listView = new VBox();
|
VBox listView = new VBox();
|
||||||
|
@ -46,7 +47,11 @@ public class ListBoxViewComp<T> extends Comp<CompStructure<VBox>> {
|
||||||
cache.keySet().retainAll(c.getList());
|
cache.keySet().retainAll(c.getList());
|
||||||
});
|
});
|
||||||
|
|
||||||
return new SimpleCompStructure<>(listView);
|
var scroll = new ScrollPane(listView);
|
||||||
|
scroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||||
|
scroll.setFitToWidth(true);
|
||||||
|
|
||||||
|
return new SimpleCompStructure<>(scroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refresh(VBox listView, List<? extends T> c, Map<T, Region> cache, boolean asynchronous) {
|
private void refresh(VBox listView, List<? extends T> c, Map<T, Region> cache, boolean asynchronous) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ public class ListSelectorComp<T> extends SimpleComp {
|
||||||
@Override
|
@Override
|
||||||
protected Region createSimple() {
|
protected Region createSimple() {
|
||||||
var vbox = new VBox();
|
var vbox = new VBox();
|
||||||
|
vbox.setSpacing(8);
|
||||||
vbox.getStyleClass().add("content");
|
vbox.getStyleClass().add("content");
|
||||||
for (var v : values) {
|
for (var v : values) {
|
||||||
var cb = new CheckBox(null);
|
var cb = new CheckBox(null);
|
||||||
|
|
|
@ -25,7 +25,6 @@ import io.xpipe.app.util.*;
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.DataStore;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.Separator;
|
import javafx.scene.control.Separator;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
|
@ -160,10 +159,11 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Region createStoreProperties(Comp<?> comp, Validator propVal) {
|
private Region createStoreProperties(Comp<?> comp, Validator propVal) {
|
||||||
return new DynamicOptionsBuilder(false)
|
return new OptionsBuilder()
|
||||||
.addComp((ObservableValue<String>) null, comp, input)
|
.addComp(comp, input)
|
||||||
.addTitle(AppI18n.observable("properties"))
|
.name("connectionName")
|
||||||
.addString(AppI18n.observable("name"), name, false)
|
.description("connectionNameDescription")
|
||||||
|
.addString(name, false)
|
||||||
.nonNull(propVal)
|
.nonNull(propVal)
|
||||||
.bind(
|
.bind(
|
||||||
() -> {
|
() -> {
|
||||||
|
@ -218,6 +218,11 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
|
||||||
return new LoadingOverlayComp(Comp.of(() -> layout), busy).createStructure();
|
return new LoadingOverlayComp(Comp.of(() -> layout), busy).createStructure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContinue() {
|
||||||
|
ScanAlert.showIfNeeded(entry.getValue().getStore());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canContinue() {
|
public boolean canContinue() {
|
||||||
if (provider.getValue() != null) {
|
if (provider.getValue() != null) {
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class AppGreetings {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showIfNeeded() {
|
public static void showIfNeeded() {
|
||||||
//TODO
|
// TODO
|
||||||
if (!AppProperties.get().isImage() || true) {
|
if (!AppProperties.get().isImage() || true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,7 @@ public class AppGreetings {
|
||||||
var button = alert.getDialogPane().lookupButton(buttonType);
|
var button = alert.getDialogPane().lookupButton(buttonType);
|
||||||
button.disableProperty().bind(accepted.not());
|
button.disableProperty().bind(accepted.not());
|
||||||
},
|
},
|
||||||
|
null,
|
||||||
r -> r.filter(b -> b.getButtonData().isDefaultButton() && accepted.get())
|
r -> r.filter(b -> b.getButtonData().isDefaultButton() && accepted.get())
|
||||||
.ifPresentOrElse(
|
.ifPresentOrElse(
|
||||||
t -> {
|
t -> {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import io.xpipe.app.issue.TrackEvent;
|
||||||
import io.xpipe.app.prefs.AppPrefs;
|
import io.xpipe.app.prefs.AppPrefs;
|
||||||
import io.xpipe.app.prefs.SupportedLocale;
|
import io.xpipe.app.prefs.SupportedLocale;
|
||||||
import io.xpipe.app.util.DynamicOptionsBuilder;
|
import io.xpipe.app.util.DynamicOptionsBuilder;
|
||||||
|
import io.xpipe.app.util.OptionsBuilder;
|
||||||
import io.xpipe.app.util.Translatable;
|
import io.xpipe.app.util.Translatable;
|
||||||
import io.xpipe.core.util.ModuleHelper;
|
import io.xpipe.core.util.ModuleHelper;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
|
@ -131,6 +132,7 @@ private static AppI18n INSTANCE = new AppI18n();
|
||||||
|| caller.equals(FancyTooltipAugment.class)
|
|| caller.equals(FancyTooltipAugment.class)
|
||||||
|| caller.equals(PrefsChoiceValue.class)
|
|| caller.equals(PrefsChoiceValue.class)
|
||||||
|| caller.equals(Translatable.class)
|
|| caller.equals(Translatable.class)
|
||||||
|
|| caller.equals(OptionsBuilder.class)
|
||||||
|| caller.equals(DynamicOptionsBuilder.class)) {
|
|| caller.equals(DynamicOptionsBuilder.class)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class AppWindowHelper {
|
||||||
childStage.setY(stage.getY() + stage.getHeight() / 2 - childStage.getHeight() / 2);
|
childStage.setY(stage.getY() + stage.getHeight() / 2 - childStage.getHeight() / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showAlert(Consumer<Alert> c, Consumer<Optional<ButtonType>> bt) {
|
public static void showAlert(Consumer<Alert> c, ObservableValue<Boolean> loading, Consumer<Optional<ButtonType>> bt) {
|
||||||
ThreadHelper.runAsync(() -> {
|
ThreadHelper.runAsync(() -> {
|
||||||
var r = showBlockingAlert(c);
|
var r = showBlockingAlert(c);
|
||||||
if (bt != null) {
|
if (bt != null) {
|
||||||
|
|
49
app/src/main/java/io/xpipe/app/ext/ScanProvider.java
Normal file
49
app/src/main/java/io/xpipe/app/ext/ScanProvider.java
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package io.xpipe.app.ext;
|
||||||
|
|
||||||
|
import io.xpipe.core.store.DataStore;
|
||||||
|
import io.xpipe.core.util.ModuleLayerLoader;
|
||||||
|
import lombok.Value;
|
||||||
|
import org.apache.commons.lang3.function.FailableRunnable;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public abstract class ScanProvider {
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public static class ScanOperation {
|
||||||
|
String nameKey;
|
||||||
|
FailableRunnable<Exception> scanner;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ScanProvider> ALL;
|
||||||
|
|
||||||
|
public static class Loader implements ModuleLayerLoader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(ModuleLayer layer) {
|
||||||
|
ALL = ServiceLoader.load(layer, ScanProvider.class).stream()
|
||||||
|
.map(ServiceLoader.Provider::get)
|
||||||
|
.sorted(Comparator.comparing(scanProvider -> scanProvider.getClass().getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresFullDaemon() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean prioritizeLoading() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ScanProvider> getAll() {
|
||||||
|
return ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract ScanOperation create(DataStore store);
|
||||||
|
}
|
163
app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java
Normal file
163
app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
package io.xpipe.app.fxcomps.impl;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Popover;
|
||||||
|
import atlantafx.base.controls.Spacer;
|
||||||
|
import io.xpipe.app.core.AppFont;
|
||||||
|
import io.xpipe.app.fxcomps.Comp;
|
||||||
|
import io.xpipe.app.fxcomps.CompStructure;
|
||||||
|
import io.xpipe.app.fxcomps.SimpleCompStructure;
|
||||||
|
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.geometry.Orientation;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class OptionsComp extends Comp<CompStructure<Pane>> {
|
||||||
|
|
||||||
|
private final List<OptionsComp.Entry> entries;
|
||||||
|
|
||||||
|
public OptionsComp(List<OptionsComp.Entry> entries) {
|
||||||
|
this.entries = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsComp.Entry queryEntry(String key) {
|
||||||
|
return entries.stream()
|
||||||
|
.filter(entry -> entry.key != null && entry.key.equals(key))
|
||||||
|
.findAny()
|
||||||
|
.orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompStructure<Pane> createBase() {
|
||||||
|
Pane pane;
|
||||||
|
var content = new VBox();
|
||||||
|
content.setSpacing(7);
|
||||||
|
pane = content;
|
||||||
|
pane.getStyleClass().add("options-comp");
|
||||||
|
|
||||||
|
var nameRegions = new ArrayList<Region>();
|
||||||
|
var compRegions = new ArrayList<Region>();
|
||||||
|
|
||||||
|
for (var entry : getEntries()) {
|
||||||
|
Region compRegion = null;
|
||||||
|
if (entry.comp() != null) {
|
||||||
|
compRegion = entry.comp().createRegion();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.name() != null && entry.description() != null) {
|
||||||
|
var line = new VBox();
|
||||||
|
line.prefWidthProperty().bind(pane.widthProperty());
|
||||||
|
line.setSpacing(5);
|
||||||
|
|
||||||
|
var name = new Label();
|
||||||
|
name.getStyleClass().add("name");
|
||||||
|
name.textProperty().bind(entry.name());
|
||||||
|
name.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
name.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
if (compRegion != null) {
|
||||||
|
name.visibleProperty().bind(PlatformThread.sync(compRegion.visibleProperty()));
|
||||||
|
name.managedProperty().bind(PlatformThread.sync(compRegion.managedProperty()));
|
||||||
|
}
|
||||||
|
line.getChildren().add(name);
|
||||||
|
|
||||||
|
var description = new Label();
|
||||||
|
description.setWrapText(true);
|
||||||
|
description.getStyleClass().add("description");
|
||||||
|
description.textProperty().bind(entry.description());
|
||||||
|
description.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
if (compRegion != null) {
|
||||||
|
description.visibleProperty().bind(PlatformThread.sync(compRegion.visibleProperty()));
|
||||||
|
description.managedProperty().bind(PlatformThread.sync(compRegion.managedProperty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.longDescription() != null) {
|
||||||
|
var popover = new Popover(new Label(entry.longDescription().getValue()));
|
||||||
|
popover.setCloseButtonEnabled(false);
|
||||||
|
popover.setHeaderAlwaysVisible(false);
|
||||||
|
popover.setDetachable(true);
|
||||||
|
AppFont.small(popover.getContentNode());
|
||||||
|
|
||||||
|
var descriptionHover = new Label("?");
|
||||||
|
AppFont.header(descriptionHover);
|
||||||
|
descriptionHover.setOnMouseClicked(e -> popover.show(descriptionHover));
|
||||||
|
|
||||||
|
var descriptionBox = new HBox(description, new Spacer(Orientation.HORIZONTAL), descriptionHover);
|
||||||
|
HBox.setHgrow(descriptionBox, Priority.ALWAYS);
|
||||||
|
descriptionBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
line.getChildren().add(descriptionBox);
|
||||||
|
}else {
|
||||||
|
line.getChildren().add(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compRegion != null) {
|
||||||
|
line.getChildren().add(compRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
pane.getChildren().add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (entry.name() != null) {
|
||||||
|
var line = new HBox();
|
||||||
|
line.setFillHeight(true);
|
||||||
|
line.prefWidthProperty().bind(pane.widthProperty());
|
||||||
|
line.setSpacing(8);
|
||||||
|
|
||||||
|
var name = new Label();
|
||||||
|
name.textProperty().bind(entry.name());
|
||||||
|
name.prefHeightProperty().bind(line.heightProperty());
|
||||||
|
name.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
name.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
if (compRegion != null) {
|
||||||
|
name.visibleProperty().bind(PlatformThread.sync(compRegion.visibleProperty()));
|
||||||
|
name.managedProperty().bind(PlatformThread.sync(compRegion.managedProperty()));
|
||||||
|
}
|
||||||
|
nameRegions.add(name);
|
||||||
|
line.getChildren().add(name);
|
||||||
|
|
||||||
|
if (compRegion != null) {
|
||||||
|
compRegions.add(compRegion);
|
||||||
|
line.getChildren().add(compRegion);
|
||||||
|
HBox.setHgrow(compRegion, Priority.ALWAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
pane.getChildren().add(line);
|
||||||
|
} else {
|
||||||
|
if (compRegion != null) {
|
||||||
|
compRegions.add(compRegion);
|
||||||
|
pane.getChildren().add(compRegion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entries.stream().anyMatch(entry -> entry.name() != null && entry.description() == null)) {
|
||||||
|
var nameWidthBinding = Bindings.createDoubleBinding(
|
||||||
|
() -> {
|
||||||
|
if (nameRegions.stream().anyMatch(r -> r.getWidth() == 0)) {
|
||||||
|
return Region.USE_COMPUTED_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
var m = nameRegions.stream()
|
||||||
|
.map(Region::getWidth)
|
||||||
|
.max(Double::compareTo)
|
||||||
|
.orElse(0.0);
|
||||||
|
return m;
|
||||||
|
},
|
||||||
|
nameRegions.stream().map(Region::widthProperty).toList().toArray(new Observable[0]));
|
||||||
|
nameRegions.forEach(r -> r.prefWidthProperty().bind(nameWidthBinding));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleCompStructure<>(pane);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OptionsComp.Entry> getEntries() {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Entry(String key, ObservableValue<String> description, ObservableValue<String> longDescription, ObservableValue<String> name, Comp<?> comp) {}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import io.xpipe.app.ext.PrefsChoiceValue;
|
||||||
import io.xpipe.app.issue.ErrorEvent;
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
import io.xpipe.core.impl.LocalStore;
|
import io.xpipe.core.impl.LocalStore;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -37,7 +37,7 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Optional<Path> getApplicationPath() {
|
protected Optional<Path> getApplicationPath() {
|
||||||
try (ShellProcessControl pc = LocalStore.getShell().start()) {
|
try (ShellControl pc = LocalStore.getShell().start()) {
|
||||||
try (var c = pc.command(String.format(
|
try (var c = pc.command(String.format(
|
||||||
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister "
|
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister "
|
||||||
+ "-dump | grep -o \"/.*%s.app\" | grep -v -E \"Caches|TimeMachine|Temporary|/Volumes/%s\" | uniq",
|
+ "-dump | grep -o \"/.*%s.app\" | grep -v -E \"Caches|TimeMachine|Temporary|/Volumes/%s\" | uniq",
|
||||||
|
@ -76,7 +76,7 @@ public abstract class ExternalApplicationType implements PrefsChoiceValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
try (ShellProcessControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalStore.getShell()) {
|
||||||
return pc.executeBooleanSimpleCommand(pc.getShellDialect().getWhichCommand(executable));
|
return pc.executeBooleanSimpleCommand(pc.getShellDialect().getWhichCommand(executable));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorEvent.fromThrowable(e).omit().handle();
|
ErrorEvent.fromThrowable(e).omit().handle();
|
||||||
|
|
|
@ -6,7 +6,7 @@ import io.xpipe.app.util.ApplicationHelper;
|
||||||
import io.xpipe.app.util.MacOsPermissions;
|
import io.xpipe.app.util.MacOsPermissions;
|
||||||
import io.xpipe.core.impl.LocalStore;
|
import io.xpipe.core.impl.LocalStore;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(String name, String command) throws Exception {
|
public void launch(String name, String command) throws Exception {
|
||||||
try (ShellProcessControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalStore.getShell()) {
|
||||||
var suffix = command.equals(pc.getShellDialect().getNormalOpenCommand())
|
var suffix = command.equals(pc.getShellDialect().getNormalOpenCommand())
|
||||||
? "\"\""
|
? "\"\""
|
||||||
: "\"" + command.replaceAll("\"", "\\\\\"") + "\"";
|
: "\"" + command.replaceAll("\"", "\\\\\"") + "\"";
|
||||||
|
@ -187,7 +187,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(String name, String command) throws Exception {
|
public void launch(String name, String command) throws Exception {
|
||||||
try (ShellProcessControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalStore.getShell()) {
|
||||||
var cmd = String.format(
|
var cmd = String.format(
|
||||||
"""
|
"""
|
||||||
osascript - "$@" <<EOF
|
osascript - "$@" <<EOF
|
||||||
|
@ -225,7 +225,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (ShellProcessControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalStore.getShell()) {
|
||||||
var cmd = String.format(
|
var cmd = String.format(
|
||||||
"""
|
"""
|
||||||
osascript - "$@" <<EOF
|
osascript - "$@" <<EOF
|
||||||
|
@ -258,7 +258,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void launch(String name, String command) throws Exception {
|
public void launch(String name, String command) throws Exception {
|
||||||
try (ShellProcessControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalStore.getShell()) {
|
||||||
ApplicationHelper.checkSupport(pc, executable, displayName);
|
ApplicationHelper.checkSupport(pc, executable, displayName);
|
||||||
|
|
||||||
var toExecute = executable + " " + toCommand(name, command);
|
var toExecute = executable + " " + toCommand(name, command);
|
||||||
|
@ -274,7 +274,7 @@ public interface ExternalTerminalType extends PrefsChoiceValue {
|
||||||
protected abstract String toCommand(String name, String command);
|
protected abstract String toCommand(String name, String command);
|
||||||
|
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
try (ShellProcessControl pc = LocalStore.getShell()) {
|
try (ShellControl pc = LocalStore.getShell()) {
|
||||||
return pc.executeBooleanSimpleCommand(pc.getShellDialect().getWhichCommand(executable));
|
return pc.executeBooleanSimpleCommand(pc.getShellDialect().getWhichCommand(executable));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorEvent.fromThrowable(e).omit().handle();
|
ErrorEvent.fromThrowable(e).omit().handle();
|
||||||
|
|
|
@ -53,6 +53,8 @@ public abstract class DataStorage {
|
||||||
INSTANCE = shouldPersist() ? new StandardStorage() : new ImpersistentStorage();
|
INSTANCE = shouldPersist() ? new StandardStorage() : new ImpersistentStorage();
|
||||||
INSTANCE.load();
|
INSTANCE.load();
|
||||||
|
|
||||||
|
INSTANCE.storeEntries.forEach(entry -> entry.simpleRefresh());
|
||||||
|
|
||||||
DataStoreProviders.getAll().forEach(dataStoreProvider -> {
|
DataStoreProviders.getAll().forEach(dataStoreProvider -> {
|
||||||
try {
|
try {
|
||||||
dataStoreProvider.storageInit();
|
dataStoreProvider.storageInit();
|
||||||
|
@ -115,6 +117,21 @@ public abstract class DataStorage {
|
||||||
return createUniqueSourceEntryName(col, typeName);
|
return createUniqueSourceEntryName(col, typeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String createUniqueStoreEntryName(String base) {
|
||||||
|
if (DataStorage.get().getStoreIfPresent(base).isEmpty()) {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
int counter = 1;
|
||||||
|
while (true) {
|
||||||
|
var name = base + counter;
|
||||||
|
if (DataStorage.get().getStoreIfPresent(name).isEmpty()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String createUniqueSourceEntryName(DataSourceCollection col, String base) {
|
private String createUniqueSourceEntryName(DataSourceCollection col, String base) {
|
||||||
base = DataSourceId.cleanString(base);
|
base = DataSourceId.cleanString(base);
|
||||||
var id = DataSourceId.create(col != null ? col.getName() : null, base);
|
var id = DataSourceId.create(col != null ? col.getName() : null, base);
|
||||||
|
@ -345,7 +362,7 @@ public abstract class DataStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataStoreEntry addStore(@NonNull String name, DataStore store) {
|
public DataStoreEntry addStore(@NonNull String name, DataStore store) {
|
||||||
var e = DataStoreEntry.createNew(UUID.randomUUID(), name, store);
|
var e = DataStoreEntry.createNew(UUID.randomUUID(), createUniqueStoreEntryName(name), store);
|
||||||
addStore(e);
|
addStore(e);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
import io.xpipe.app.util.ScriptHelper;
|
import io.xpipe.app.util.ScriptHelper;
|
||||||
import io.xpipe.app.util.TerminalHelper;
|
import io.xpipe.app.util.TerminalHelper;
|
||||||
import io.xpipe.core.impl.FileNames;
|
import io.xpipe.core.impl.FileNames;
|
||||||
import io.xpipe.core.process.CommandProcessControl;
|
import io.xpipe.core.process.CommandControl;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
import io.xpipe.core.process.ShellDialects;
|
||||||
import io.xpipe.core.store.ShellStore;
|
import io.xpipe.core.store.ShellStore;
|
||||||
import io.xpipe.core.util.XPipeInstallation;
|
import io.xpipe.core.util.XPipeInstallation;
|
||||||
|
@ -22,7 +22,7 @@ import java.util.List;
|
||||||
|
|
||||||
public class AppInstaller {
|
public class AppInstaller {
|
||||||
|
|
||||||
public static void installOnRemoteMachine(ShellProcessControl s, String version) throws Exception {
|
public static void installOnRemoteMachine(ShellControl s, String version) throws Exception {
|
||||||
var asset = getSuitablePlatformAsset(s);
|
var asset = getSuitablePlatformAsset(s);
|
||||||
var file = AppDownloads.downloadInstaller(asset, version);
|
var file = AppDownloads.downloadInstaller(asset, version);
|
||||||
installFile(s, asset, file);
|
installFile(s, asset, file);
|
||||||
|
@ -32,14 +32,14 @@ public class AppInstaller {
|
||||||
asset.installLocal(localFile.toString());
|
asset.installLocal(localFile.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void installFile(ShellProcessControl s, InstallerAssetType asset, Path localFile) throws Exception {
|
public static void installFile(ShellControl s, InstallerAssetType asset, Path localFile) throws Exception {
|
||||||
String targetFile = null;
|
String targetFile = null;
|
||||||
if (s.isLocal()) {
|
if (s.isLocal()) {
|
||||||
targetFile = localFile.toString();
|
targetFile = localFile.toString();
|
||||||
} else {
|
} else {
|
||||||
targetFile = FileNames.join(
|
targetFile = FileNames.join(
|
||||||
s.getTemporaryDirectory(), localFile.getFileName().toString());
|
s.getTemporaryDirectory(), localFile.getFileName().toString());
|
||||||
try (CommandProcessControl c = s.getShellDialect().getStreamFileWriteCommand(s, targetFile)
|
try (CommandControl c = s.getShellDialect().getStreamFileWriteCommand(s, targetFile)
|
||||||
.start()) {
|
.start()) {
|
||||||
c.discardOut();
|
c.discardOut();
|
||||||
c.discardErr();
|
c.discardErr();
|
||||||
|
@ -73,13 +73,13 @@ public class AppInstaller {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstallerAssetType getSuitablePlatformAsset(ShellProcessControl p) throws Exception {
|
public static InstallerAssetType getSuitablePlatformAsset(ShellControl p) throws Exception {
|
||||||
if (p.getOsType().equals(OsType.WINDOWS)) {
|
if (p.getOsType().equals(OsType.WINDOWS)) {
|
||||||
return new InstallerAssetType.Msi();
|
return new InstallerAssetType.Msi();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.getOsType().equals(OsType.LINUX)) {
|
if (p.getOsType().equals(OsType.LINUX)) {
|
||||||
try (CommandProcessControl c = p.command(p.getShellDialect().getFileExistsCommand("/etc/debian_version"))
|
try (CommandControl c = p.command(p.getShellDialect().getFileExistsCommand("/etc/debian_version"))
|
||||||
.start()) {
|
.start()) {
|
||||||
return c.discardAndCheckExit() ? new InstallerAssetType.Debian() : new InstallerAssetType.Rpm();
|
return c.discardAndCheckExit() ? new InstallerAssetType.Debian() : new InstallerAssetType.Rpm();
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ public class AppInstaller {
|
||||||
})
|
})
|
||||||
public abstract static class InstallerAssetType {
|
public abstract static class InstallerAssetType {
|
||||||
|
|
||||||
public abstract void installRemote(ShellProcessControl pc, String file) throws Exception;
|
public abstract void installRemote(ShellControl pc, String file) throws Exception;
|
||||||
|
|
||||||
public abstract void installLocal(String file) throws Exception;
|
public abstract void installLocal(String file) throws Exception;
|
||||||
|
|
||||||
|
@ -121,11 +121,11 @@ public class AppInstaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installRemote(ShellProcessControl shellProcessControl, String file) throws Exception {
|
public void installRemote(ShellControl shellControl, String file) throws Exception {
|
||||||
var exec = XPipeInstallation.getInstallationExecutable(
|
var exec = XPipeInstallation.getInstallationExecutable(
|
||||||
shellProcessControl,
|
shellControl,
|
||||||
XPipeInstallation.getDefaultInstallationBasePath(shellProcessControl, false));
|
XPipeInstallation.getDefaultInstallationBasePath(shellControl, false));
|
||||||
var logsDir = FileNames.join(XPipeInstallation.getDataBasePath(shellProcessControl), "logs");
|
var logsDir = FileNames.join(XPipeInstallation.getDataBasePath(shellControl), "logs");
|
||||||
var cmd = new ArrayList<>(java.util.List.of(
|
var cmd = new ArrayList<>(java.util.List.of(
|
||||||
"start",
|
"start",
|
||||||
"/wait",
|
"/wait",
|
||||||
|
@ -139,7 +139,7 @@ public class AppInstaller {
|
||||||
exec
|
exec
|
||||||
// "/qf"
|
// "/qf"
|
||||||
));
|
));
|
||||||
try (CommandProcessControl c = shellProcessControl.command(cmd).start()) {
|
try (CommandControl c = shellControl.command(cmd).start()) {
|
||||||
c.discardOrThrow();
|
c.discardOrThrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,14 +177,14 @@ public class AppInstaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installRemote(ShellProcessControl shellProcessControl, String file) throws Exception {
|
public void installRemote(ShellControl shellControl, String file) throws Exception {
|
||||||
try (var pc = shellProcessControl.subShell(ShellDialects.BASH).start()) {
|
try (var pc = shellControl.subShell(ShellDialects.BASH).start()) {
|
||||||
try (CommandProcessControl c = pc.command("DEBIAN_FRONTEND=noninteractive apt-get remove -qy xpipe")
|
try (CommandControl c = pc.command("DEBIAN_FRONTEND=noninteractive apt-get remove -qy xpipe")
|
||||||
.elevated()
|
.elevated()
|
||||||
.start()) {
|
.start()) {
|
||||||
c.discardOrThrow();
|
c.discardOrThrow();
|
||||||
}
|
}
|
||||||
try (CommandProcessControl c = pc.command(
|
try (CommandControl c = pc.command(
|
||||||
"DEBIAN_FRONTEND=noninteractive apt-get install -qy \"" + file + "\"")
|
"DEBIAN_FRONTEND=noninteractive apt-get install -qy \"" + file + "\"")
|
||||||
.elevated()
|
.elevated()
|
||||||
.start()) {
|
.start()) {
|
||||||
|
@ -214,9 +214,9 @@ public class AppInstaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installRemote(ShellProcessControl shellProcessControl, String file) throws Exception {
|
public void installRemote(ShellControl shellControl, String file) throws Exception {
|
||||||
try (var pc = shellProcessControl.subShell(ShellDialects.BASH).start()) {
|
try (var pc = shellControl.subShell(ShellDialects.BASH).start()) {
|
||||||
try (CommandProcessControl c = pc.command("rpm -U -v --force \"" + file + "\"")
|
try (CommandControl c = pc.command("rpm -U -v --force \"" + file + "\"")
|
||||||
.elevated()
|
.elevated()
|
||||||
.start()) {
|
.start()) {
|
||||||
c.discardOrThrow();
|
c.discardOrThrow();
|
||||||
|
@ -244,9 +244,9 @@ public class AppInstaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installRemote(ShellProcessControl shellProcessControl, String file) throws Exception {
|
public void installRemote(ShellControl shellControl, String file) throws Exception {
|
||||||
try (var pc = shellProcessControl.subShell(ShellDialects.BASH).start()) {
|
try (var pc = shellControl.subShell(ShellDialects.BASH).start()) {
|
||||||
try (CommandProcessControl c = pc.command(
|
try (CommandControl c = pc.command(
|
||||||
"installer -verboseR -allowUntrusted -pkg \"" + file + "\" -target /")
|
"installer -verboseR -allowUntrusted -pkg \"" + file + "\" -target /")
|
||||||
.elevated()
|
.elevated()
|
||||||
.start()) {
|
.start()) {
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class UpdateChangelogAlert {
|
||||||
alert.getDialogPane().setContent(markdown);
|
alert.getDialogPane().setContent(markdown);
|
||||||
|
|
||||||
alert.getButtonTypes().add(new ButtonType(AppI18n.get("gotIt"), ButtonBar.ButtonData.OK_DONE));
|
alert.getButtonTypes().add(new ButtonType(AppI18n.get("gotIt"), ButtonBar.ButtonData.OK_DONE));
|
||||||
},
|
},null,
|
||||||
r -> r.filter(b -> b.getButtonData().isDefaultButton()).ifPresent(t -> {}));
|
r -> r.filter(b -> b.getButtonData().isDefaultButton()).ifPresent(t -> {}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package io.xpipe.app.util;
|
||||||
import io.xpipe.app.issue.TrackEvent;
|
import io.xpipe.app.issue.TrackEvent;
|
||||||
import io.xpipe.core.impl.LocalStore;
|
import io.xpipe.core.impl.LocalStore;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
import io.xpipe.core.process.ShellDialects;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -30,12 +30,12 @@ public class ApplicationHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isInPath(ShellProcessControl processControl, String executable) throws Exception {
|
public static boolean isInPath(ShellControl processControl, String executable) throws Exception {
|
||||||
return processControl.executeBooleanSimpleCommand(
|
return processControl.executeBooleanSimpleCommand(
|
||||||
processControl.getShellDialect().getWhichCommand(executable));
|
processControl.getShellDialect().getWhichCommand(executable));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkSupport(ShellProcessControl processControl, String executable, String displayName)
|
public static void checkSupport(ShellControl processControl, String executable, String displayName)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
if (!isInPath(processControl, executable)) {
|
if (!isInPath(processControl, executable)) {
|
||||||
throw new IOException(displayName + " executable " + executable + " not found in PATH");
|
throw new IOException(displayName + " executable " + executable + " not found in PATH");
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class MacOsPermissions {
|
||||||
a.getDialogPane().setContent(AppWindowHelper.alertContentText(AppI18n.get("permissionsAlertTitleContent")));
|
a.getDialogPane().setContent(AppWindowHelper.alertContentText(AppI18n.get("permissionsAlertTitleContent")));
|
||||||
a.getButtonTypes().clear();
|
a.getButtonTypes().clear();
|
||||||
alert.set(a);
|
alert.set(a);
|
||||||
}, buttonType -> {
|
}, null, buttonType -> {
|
||||||
alert.get().close();
|
alert.get().close();
|
||||||
if (buttonType.isEmpty() || !buttonType.get().getButtonData().isDefaultButton()) {
|
if (buttonType.isEmpty() || !buttonType.get().getButtonData().isDefaultButton()) {
|
||||||
state.set(false);
|
state.set(false);
|
||||||
|
|
166
app/src/main/java/io/xpipe/app/util/OptionsBuilder.java
Normal file
166
app/src/main/java/io/xpipe/app/util/OptionsBuilder.java
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
package io.xpipe.app.util;
|
||||||
|
|
||||||
|
import io.xpipe.app.core.AppI18n;
|
||||||
|
import io.xpipe.app.fxcomps.Comp;
|
||||||
|
import io.xpipe.app.fxcomps.impl.*;
|
||||||
|
import io.xpipe.core.util.SecretValue;
|
||||||
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import net.synedra.validatorfx.Check;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class OptionsBuilder {
|
||||||
|
|
||||||
|
private final List<OptionsComp.Entry> entries = new ArrayList<>();
|
||||||
|
private final List<Property<?>> props = new ArrayList<>();
|
||||||
|
|
||||||
|
private ObservableValue<String> name;
|
||||||
|
private ObservableValue<String> description;
|
||||||
|
private ObservableValue<String> longDescription;
|
||||||
|
private Comp<?> comp;
|
||||||
|
|
||||||
|
private void finishCurrent() {
|
||||||
|
if (comp == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry = new OptionsComp.Entry(null, description, longDescription, name, comp);
|
||||||
|
description = null;
|
||||||
|
longDescription = null;
|
||||||
|
name = null;
|
||||||
|
comp = null;
|
||||||
|
entries.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder addTitle(String titleKey) {
|
||||||
|
finishCurrent();
|
||||||
|
entries.add(new OptionsComp.Entry(
|
||||||
|
titleKey, null, null, null, new LabelComp(AppI18n.observable(titleKey)).styleClass("title-header")));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder addTitle(ObservableValue<String> title) {
|
||||||
|
finishCurrent();
|
||||||
|
entries.add(new OptionsComp.Entry(
|
||||||
|
null, null, null, null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header")));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder decorate(Check c) {
|
||||||
|
comp.apply(s -> c.decorates(s.get()));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder nonNull(Validator v) {
|
||||||
|
var e = name;
|
||||||
|
var p = props.get(props.size() - 1);
|
||||||
|
return decorate(Validator.nonNull(v, e, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pushComp(Comp<?> comp) {
|
||||||
|
finishCurrent();
|
||||||
|
this.comp = comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder stringArea(Property<String> prop, boolean lazy) {
|
||||||
|
var comp = new TextAreaComp(prop, lazy);
|
||||||
|
pushComp(comp);
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder addInteger(Property<Integer> prop) {
|
||||||
|
var comp = new IntFieldComp(prop);
|
||||||
|
pushComp(comp);
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder addString(Property<String> prop) {
|
||||||
|
return addString(prop, false);
|
||||||
|
}
|
||||||
|
public OptionsBuilder addString(Property<String> prop, boolean lazy) {
|
||||||
|
var comp = new TextFieldComp(prop, lazy);
|
||||||
|
pushComp(comp);
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public OptionsBuilder name(String nameKey) {
|
||||||
|
finishCurrent();
|
||||||
|
name = AppI18n.observable(nameKey);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder description(String descriptionKey) {
|
||||||
|
finishCurrent();
|
||||||
|
description = AppI18n.observable(descriptionKey);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder longDescription(String descriptionKey) {
|
||||||
|
finishCurrent();
|
||||||
|
longDescription = AppI18n.observable(descriptionKey);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder addComp(Comp<?> comp) {
|
||||||
|
pushComp(comp);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder addComp(Comp<?> comp, Property<?> prop) {
|
||||||
|
pushComp(comp);
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsBuilder addSecret(Property<SecretValue> prop) {
|
||||||
|
var comp = new SecretFieldComp(prop);
|
||||||
|
pushComp(comp);
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public final <T, V extends T> OptionsBuilder bind(Supplier<V> creator, Property<T>... toSet) {
|
||||||
|
props.forEach(prop -> {
|
||||||
|
prop.addListener((c, o, n) -> {
|
||||||
|
for (Property<T> p : toSet) {
|
||||||
|
p.setValue(creator.get());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
for (Property<T> p : toSet) {
|
||||||
|
p.setValue(creator.get());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final <T, V extends T> OptionsBuilder bindChoice(
|
||||||
|
Supplier<Property<? extends V>> creator, Property<T> toSet) {
|
||||||
|
props.forEach(prop -> {
|
||||||
|
prop.addListener((c, o, n) -> {
|
||||||
|
toSet.unbind();
|
||||||
|
toSet.bind(creator.get());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
toSet.bind(creator.get());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionsComp buildComp() {
|
||||||
|
finishCurrent();
|
||||||
|
return new OptionsComp(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Region build() {
|
||||||
|
return buildComp().createRegion();
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ import io.xpipe.app.prefs.AppPrefs;
|
||||||
import io.xpipe.app.update.AppDownloads;
|
import io.xpipe.app.update.AppDownloads;
|
||||||
import io.xpipe.app.update.AppInstaller;
|
import io.xpipe.app.update.AppInstaller;
|
||||||
import io.xpipe.core.impl.FileNames;
|
import io.xpipe.core.impl.FileNames;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.util.ModuleHelper;
|
import io.xpipe.core.util.ModuleHelper;
|
||||||
import io.xpipe.core.util.ProxyManagerProvider;
|
import io.xpipe.core.util.ProxyManagerProvider;
|
||||||
import io.xpipe.core.util.XPipeInstallation;
|
import io.xpipe.core.util.XPipeInstallation;
|
||||||
|
@ -31,7 +31,7 @@ public class ProxyManagerProviderImpl extends ProxyManagerProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> checkCompatibility(ShellProcessControl s) throws Exception {
|
public Optional<String> checkCompatibility(ShellControl s) throws Exception {
|
||||||
var version = ModuleHelper.isImage() ? AppProperties.get().getVersion() : AppDownloads.getLatestVersion();
|
var version = ModuleHelper.isImage() ? AppProperties.get().getVersion() : AppDownloads.getLatestVersion();
|
||||||
|
|
||||||
if (AppPrefs.get().developerDisableConnectorInstallationVersionCheck().get()) {
|
if (AppPrefs.get().developerDisableConnectorInstallationVersionCheck().get()) {
|
||||||
|
@ -54,7 +54,7 @@ public class ProxyManagerProviderImpl extends ProxyManagerProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setup(ShellProcessControl s) throws Exception {
|
public boolean setup(ShellControl s) throws Exception {
|
||||||
var message = checkCompatibility(s);
|
var message = checkCompatibility(s);
|
||||||
if (message.isPresent()) {
|
if (message.isPresent()) {
|
||||||
if (showAlert()) {
|
if (showAlert()) {
|
||||||
|
|
64
app/src/main/java/io/xpipe/app/util/ScanAlert.java
Normal file
64
app/src/main/java/io/xpipe/app/util/ScanAlert.java
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package io.xpipe.app.util;
|
||||||
|
|
||||||
|
import io.xpipe.app.comp.base.ListSelectorComp;
|
||||||
|
import io.xpipe.app.core.AppI18n;
|
||||||
|
import io.xpipe.app.core.AppWindowHelper;
|
||||||
|
import io.xpipe.app.ext.ScanProvider;
|
||||||
|
import io.xpipe.app.fxcomps.impl.LabelComp;
|
||||||
|
import io.xpipe.app.fxcomps.impl.VerticalComp;
|
||||||
|
import io.xpipe.app.issue.ErrorEvent;
|
||||||
|
import io.xpipe.core.store.DataStore;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleListProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ScanAlert {
|
||||||
|
|
||||||
|
public static void showIfNeeded(DataStore store) {
|
||||||
|
var providers = ScanProvider.getAll();
|
||||||
|
var applicable = providers.stream()
|
||||||
|
.map(scanProvider -> scanProvider.create(store))
|
||||||
|
.filter(scanOperation -> scanOperation != null)
|
||||||
|
.toList();
|
||||||
|
if (applicable.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected = new SimpleListProperty<ScanProvider.ScanOperation>(
|
||||||
|
FXCollections.observableList(new ArrayList<>(applicable)));
|
||||||
|
var busy = new SimpleBooleanProperty();
|
||||||
|
AppWindowHelper.showAlert(
|
||||||
|
alert -> {
|
||||||
|
alert.setAlertType(Alert.AlertType.NONE);
|
||||||
|
alert.setTitle(AppI18n.get("scanAlertTitle"));
|
||||||
|
alert.setWidth(300);
|
||||||
|
var content = new VerticalComp(List.of(
|
||||||
|
new LabelComp(AppI18n.get("scanAlertHeader")).apply(struc -> struc.get().setWrapText(true)),
|
||||||
|
new ListSelectorComp<>(
|
||||||
|
applicable, scanOperation -> AppI18n.get(scanOperation.getNameKey()), selected)))
|
||||||
|
.apply(struc -> struc.get().setSpacing(15))
|
||||||
|
.styleClass("window-content")
|
||||||
|
.createRegion();
|
||||||
|
alert.getButtonTypes().add(ButtonType.OK);
|
||||||
|
alert.getDialogPane().setContent(content);
|
||||||
|
},
|
||||||
|
busy,
|
||||||
|
buttonType -> {
|
||||||
|
if (buttonType.isPresent()
|
||||||
|
&& buttonType.get().getButtonData().isDefaultButton()) {
|
||||||
|
try (var ignored = new BusyProperty(busy)) {
|
||||||
|
for (var a : applicable) {
|
||||||
|
a.getScanner().run();
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ErrorEvent.fromThrowable(ex).handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import io.xpipe.core.impl.LocalStore;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellDialect;
|
import io.xpipe.core.process.ShellDialect;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
import io.xpipe.core.process.ShellDialects;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.util.SecretValue;
|
import io.xpipe.core.util.SecretValue;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import java.util.Random;
|
||||||
|
|
||||||
public class ScriptHelper {
|
public class ScriptHelper {
|
||||||
|
|
||||||
public static String createDefaultOpenCommand(ShellProcessControl pc, String file) {
|
public static String createDefaultOpenCommand(ShellControl pc, String file) {
|
||||||
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
||||||
return "\"" + file + "\"";
|
return "\"" + file + "\"";
|
||||||
} else if (pc.getOsType().equals(OsType.LINUX)){
|
} else if (pc.getOsType().equals(OsType.LINUX)){
|
||||||
|
@ -25,7 +25,7 @@ public class ScriptHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String createDetachCommand(ShellProcessControl pc, String command) {
|
public static String createDetachCommand(ShellControl pc, String command) {
|
||||||
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
if (pc.getOsType().equals(OsType.WINDOWS)) {
|
||||||
return "start \"\" " + command;
|
return "start \"\" " + command;
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,7 +123,7 @@ public class ScriptHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String constructOpenWithInitScriptCommand(
|
public static String constructOpenWithInitScriptCommand(
|
||||||
ShellProcessControl processControl, List<String> init, String toExecuteInShell) {
|
ShellControl processControl, List<String> init, String toExecuteInShell) {
|
||||||
ShellDialect t = processControl.getShellDialect();
|
ShellDialect t = processControl.getShellDialect();
|
||||||
if (init.size() == 0 && toExecuteInShell == null) {
|
if (init.size() == 0 && toExecuteInShell == null) {
|
||||||
return t.getNormalOpenCommand();
|
return t.getNormalOpenCommand();
|
||||||
|
@ -168,12 +168,12 @@ public class ScriptHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static String getExecScriptFile(ShellProcessControl processControl) {
|
public static String getExecScriptFile(ShellControl processControl) {
|
||||||
return getExecScriptFile(processControl, processControl.getShellDialect().getScriptFileEnding());
|
return getExecScriptFile(processControl, processControl.getShellDialect().getScriptFileEnding());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static String getExecScriptFile(ShellProcessControl processControl, String fileEnding) {
|
public static String getExecScriptFile(ShellControl processControl, String fileEnding) {
|
||||||
var fileName = "exec-" + getScriptId();
|
var fileName = "exec-" + getScriptId();
|
||||||
var temp = processControl.getTemporaryDirectory();
|
var temp = processControl.getTemporaryDirectory();
|
||||||
var file = FileNames.join(temp, fileName + "." + fileEnding);
|
var file = FileNames.join(temp, fileName + "." + fileEnding);
|
||||||
|
@ -181,7 +181,7 @@ public class ScriptHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static String createExecScript(ShellProcessControl processControl, String content) {
|
public static String createExecScript(ShellControl processControl, String content) {
|
||||||
var fileName = "exec-" + getScriptId();
|
var fileName = "exec-" + getScriptId();
|
||||||
ShellDialect type = processControl.getShellDialect();
|
ShellDialect type = processControl.getShellDialect();
|
||||||
var temp = processControl.getTemporaryDirectory();
|
var temp = processControl.getTemporaryDirectory();
|
||||||
|
@ -190,7 +190,7 @@ public class ScriptHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
private static String createExecScript(ShellProcessControl processControl, String file, String content) {
|
private static String createExecScript(ShellControl processControl, String file, String content) {
|
||||||
ShellDialect type = processControl.getShellDialect();
|
ShellDialect type = processControl.getShellDialect();
|
||||||
content = type.prepareScriptContent(content);
|
content = type.prepareScriptContent(content);
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ public class ScriptHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static String createAskPassScript(SecretValue pass, ShellProcessControl parent, ShellDialect type) {
|
public static String createAskPassScript(SecretValue pass, ShellControl parent, ShellDialect type) {
|
||||||
var content = type.getScriptEchoCommand(pass.getSecretValue());
|
var content = type.getScriptEchoCommand(pass.getSecretValue());
|
||||||
var temp = parent.getTemporaryDirectory();
|
var temp = parent.getTemporaryDirectory();
|
||||||
var file = FileNames.join(temp, "askpass-" + getScriptId() + "." + type.getScriptFileEnding());
|
var file = FileNames.join(temp, "askpass-" + getScriptId() + "." + type.getScriptFileEnding());
|
||||||
|
|
|
@ -117,11 +117,13 @@ open module io.xpipe.app {
|
||||||
uses XPipeDaemon;
|
uses XPipeDaemon;
|
||||||
uses ProxyFunction;
|
uses ProxyFunction;
|
||||||
uses ModuleLayerLoader;
|
uses ModuleLayerLoader;
|
||||||
|
uses ScanProvider;
|
||||||
|
|
||||||
provides ModuleLayerLoader with
|
provides ModuleLayerLoader with
|
||||||
DataSourceTarget.Loader,
|
DataSourceTarget.Loader,
|
||||||
ActionProvider.Loader,
|
ActionProvider.Loader,
|
||||||
PrefsProvider.Loader;
|
PrefsProvider.Loader,
|
||||||
|
ScanProvider.Loader;
|
||||||
provides DataStateProvider with
|
provides DataStateProvider with
|
||||||
DataStateProviderImpl;
|
DataStateProviderImpl;
|
||||||
provides ProxyManagerProvider with
|
provides ProxyManagerProvider with
|
||||||
|
|
|
@ -10,7 +10,11 @@ mustNotBeEmpty=$NAME$ must not be empty
|
||||||
null=$VALUE$ must be not null
|
null=$VALUE$ must be not null
|
||||||
hostFeatureUnsupported=Host does not support the feature $FEATURE$
|
hostFeatureUnsupported=Host does not support the feature $FEATURE$
|
||||||
missingStore=$NAME$ does not exist
|
missingStore=$NAME$ does not exist
|
||||||
|
connectionName=Connection name
|
||||||
|
connectionNameDescription=Give this connection a custom name
|
||||||
unknown=Unknown
|
unknown=Unknown
|
||||||
|
scanAlertTitle=Connection detection
|
||||||
|
scanAlertHeader=Select types of connections you want to automatically detect on the host system:
|
||||||
namedHostFeatureUnsupported=$HOST$ does not support this feature
|
namedHostFeatureUnsupported=$HOST$ does not support this feature
|
||||||
namedHostNotActive=$HOST$ is not active
|
namedHostNotActive=$HOST$ is not active
|
||||||
noInformationAvailable=No information available
|
noInformationAvailable=No information available
|
||||||
|
|
|
@ -11,3 +11,12 @@
|
||||||
.choice-pane-comp {
|
.choice-pane-comp {
|
||||||
-fx-spacing: 7px;
|
-fx-spacing: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.options-comp .name {
|
||||||
|
-fx-padding: 9px 0 0 0;
|
||||||
|
-fx-font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-comp .description {
|
||||||
|
-fx-opacity: 0.75;
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ package io.xpipe.core.impl;
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||||
import io.xpipe.core.process.ProcessControlProvider;
|
import io.xpipe.core.process.ProcessControlProvider;
|
||||||
import io.xpipe.core.process.ShellDialects;
|
import io.xpipe.core.process.ShellDialects;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import io.xpipe.core.store.ConnectionFileSystem;
|
import io.xpipe.core.store.ConnectionFileSystem;
|
||||||
import io.xpipe.core.store.FileSystem;
|
import io.xpipe.core.store.FileSystem;
|
||||||
import io.xpipe.core.store.FileSystemStore;
|
import io.xpipe.core.store.FileSystemStore;
|
||||||
|
@ -15,12 +15,12 @@ import java.nio.file.Path;
|
||||||
@JsonTypeName("local")
|
@JsonTypeName("local")
|
||||||
public class LocalStore extends JacksonizedValue implements ShellStore {
|
public class LocalStore extends JacksonizedValue implements ShellStore {
|
||||||
|
|
||||||
private static ShellProcessControl local;
|
private static ShellControl local;
|
||||||
private static FileSystem localFileSystem;
|
private static FileSystem localFileSystem;
|
||||||
|
|
||||||
public static ShellProcessControl getShell() throws Exception {
|
public static ShellControl getShell() throws Exception {
|
||||||
if (local == null) {
|
if (local == null) {
|
||||||
local = new LocalStore().create().start();
|
local = ProcessControlProvider.createLocal(false).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
return local;
|
return local;
|
||||||
|
@ -128,7 +128,7 @@ public class LocalStore extends JacksonizedValue implements ShellStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ShellProcessControl createControl() {
|
public ShellControl createControl() {
|
||||||
return ProcessControlProvider.createLocal();
|
return ProcessControlProvider.createLocal(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package io.xpipe.core.process;
|
package io.xpipe.core.process;
|
||||||
|
|
||||||
import io.xpipe.core.charsetter.Charsetter;
|
import io.xpipe.core.charsetter.Charsetter;
|
||||||
|
import io.xpipe.core.util.FailableFunction;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
@ -8,18 +9,18 @@ import java.io.OutputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public interface CommandProcessControl extends ProcessControl {
|
public interface CommandControl extends ProcessControl {
|
||||||
|
|
||||||
public CommandProcessControl doesNotObeyReturnValueConvention();
|
public CommandControl doesNotObeyReturnValueConvention();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessControl sensitive();
|
public CommandControl sensitive();
|
||||||
|
|
||||||
CommandProcessControl complex();
|
CommandControl complex();
|
||||||
|
|
||||||
CommandProcessControl workingDirectory(String directory);
|
CommandControl workingDirectory(String directory);
|
||||||
|
|
||||||
ShellProcessControl getParent();
|
ShellControl getParent();
|
||||||
|
|
||||||
InputStream startExternalStdout() throws Exception;
|
InputStream startExternalStdout() throws Exception;
|
||||||
|
|
||||||
|
@ -27,16 +28,20 @@ public interface CommandProcessControl extends ProcessControl {
|
||||||
|
|
||||||
public boolean waitFor();
|
public boolean waitFor();
|
||||||
|
|
||||||
CommandProcessControl customCharset(Charset charset);
|
CommandControl customCharset(Charset charset);
|
||||||
|
|
||||||
int getExitCode();
|
int getExitCode();
|
||||||
|
|
||||||
CommandProcessControl elevated();
|
default CommandControl elevated() {
|
||||||
|
return elevated((v) -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandControl elevated(FailableFunction<ShellControl, Boolean, Exception> elevationFunction);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
CommandProcessControl start() throws Exception;
|
CommandControl start() throws Exception;
|
||||||
|
|
||||||
CommandProcessControl exitTimeout(Integer timeout);
|
CommandControl exitTimeout(Integer timeout);
|
||||||
|
|
||||||
public void withStdoutOrThrow(Charsetter.FailableConsumer<InputStreamReader, Exception> c) throws Exception;
|
public void withStdoutOrThrow(Charsetter.FailableConsumer<InputStreamReader, Exception> c) throws Exception;
|
||||||
String readOnlyStdout() throws Exception;
|
String readOnlyStdout() throws Exception;
|
|
@ -22,25 +22,32 @@ public interface OsType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getHomeDirectory(ShellControl pc) throws Exception;
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
String getTempDirectory(ShellProcessControl pc) throws Exception;
|
String getTempDirectory(ShellControl pc) throws Exception;
|
||||||
|
|
||||||
String normalizeFileName(String file);
|
String normalizeFileName(String file);
|
||||||
|
|
||||||
Map<String, String> getProperties(ShellProcessControl pc) throws Exception;
|
Map<String, String> getProperties(ShellControl pc) throws Exception;
|
||||||
|
|
||||||
String determineOperatingSystemName(ShellProcessControl pc) throws Exception;
|
String determineOperatingSystemName(ShellControl pc) throws Exception;
|
||||||
|
|
||||||
static class Windows implements OsType {
|
static class Windows implements OsType {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHomeDirectory(ShellControl pc) throws Exception {
|
||||||
|
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("USERPROFILE"));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Windows";
|
return "Windows";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTempDirectory(ShellProcessControl pc) throws Exception {
|
public String getTempDirectory(ShellControl pc) throws Exception {
|
||||||
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("TEMP"));
|
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("TEMP"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,15 +57,15 @@ public interface OsType {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getProperties(ShellProcessControl pc) throws Exception {
|
public Map<String, String> getProperties(ShellControl pc) throws Exception {
|
||||||
try (CommandProcessControl c = pc.command("systeminfo").start()) {
|
try (CommandControl c = pc.command("systeminfo").start()) {
|
||||||
var text = c.readOrThrow();
|
var text = c.readOrThrow();
|
||||||
return PropertiesFormatsParser.parse(text, ":");
|
return PropertiesFormatsParser.parse(text, ":");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String determineOperatingSystemName(ShellProcessControl pc) throws Exception {
|
public String determineOperatingSystemName(ShellControl pc) throws Exception {
|
||||||
var properties = getProperties(pc);
|
var properties = getProperties(pc);
|
||||||
return properties.get("OS Name") + " "
|
return properties.get("OS Name") + " "
|
||||||
+ properties.get("OS Version").split(" ")[0];
|
+ properties.get("OS Version").split(" ")[0];
|
||||||
|
@ -68,7 +75,12 @@ public interface OsType {
|
||||||
static class Linux implements OsType {
|
static class Linux implements OsType {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTempDirectory(ShellProcessControl pc) throws Exception {
|
public String getHomeDirectory(ShellControl pc) throws Exception {
|
||||||
|
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTempDirectory(ShellControl pc) throws Exception {
|
||||||
return "/tmp/";
|
return "/tmp/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,20 +95,20 @@ public interface OsType {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getProperties(ShellProcessControl pc) throws Exception {
|
public Map<String, String> getProperties(ShellControl pc) throws Exception {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String determineOperatingSystemName(ShellProcessControl pc) throws Exception {
|
public String determineOperatingSystemName(ShellControl pc) throws Exception {
|
||||||
try (CommandProcessControl c = pc.command("lsb_release -a").start()) {
|
try (CommandControl c = pc.command("lsb_release -a").start()) {
|
||||||
var text = c.readOnlyStdout();
|
var text = c.readOnlyStdout();
|
||||||
if (c.getExitCode() == 0) {
|
if (c.getExitCode() == 0) {
|
||||||
return PropertiesFormatsParser.parse(text, ":").getOrDefault("Description", null);
|
return PropertiesFormatsParser.parse(text, ":").getOrDefault("Description", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try (CommandProcessControl c = pc.command("cat /etc/*release").start()) {
|
try (CommandControl c = pc.command("cat /etc/*release").start()) {
|
||||||
var text = c.readOnlyStdout();
|
var text = c.readOnlyStdout();
|
||||||
if (c.getExitCode() == 0) {
|
if (c.getExitCode() == 0) {
|
||||||
return PropertiesFormatsParser.parse(text, "=").getOrDefault("PRETTY_NAME", null);
|
return PropertiesFormatsParser.parse(text, "=").getOrDefault("PRETTY_NAME", null);
|
||||||
|
@ -104,7 +116,7 @@ public interface OsType {
|
||||||
}
|
}
|
||||||
|
|
||||||
String type = "Unknown";
|
String type = "Unknown";
|
||||||
try (CommandProcessControl c = pc.command("uname -o").start()) {
|
try (CommandControl c = pc.command("uname -o").start()) {
|
||||||
var text = c.readOnlyStdout();
|
var text = c.readOnlyStdout();
|
||||||
if (c.getExitCode() == 0) {
|
if (c.getExitCode() == 0) {
|
||||||
type = text.strip();
|
type = text.strip();
|
||||||
|
@ -112,7 +124,7 @@ public interface OsType {
|
||||||
}
|
}
|
||||||
|
|
||||||
String version = "?";
|
String version = "?";
|
||||||
try (CommandProcessControl c = pc.command("uname -r").start()) {
|
try (CommandControl c = pc.command("uname -r").start()) {
|
||||||
var text = c.readOnlyStdout();
|
var text = c.readOnlyStdout();
|
||||||
if (c.getExitCode() == 0) {
|
if (c.getExitCode() == 0) {
|
||||||
version = text.strip();
|
version = text.strip();
|
||||||
|
@ -126,7 +138,12 @@ public interface OsType {
|
||||||
static class MacOs implements OsType {
|
static class MacOs implements OsType {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTempDirectory(ShellProcessControl pc) throws Exception {
|
public String getHomeDirectory(ShellControl pc) throws Exception {
|
||||||
|
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintEnvironmentVariableCommand("HOME"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTempDirectory(ShellControl pc) throws Exception {
|
||||||
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintVariableCommand("TMPDIR"));
|
return pc.executeStringSimpleCommand(pc.getShellDialect().getPrintVariableCommand("TMPDIR"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,8 +158,8 @@ public interface OsType {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getProperties(ShellProcessControl pc) throws Exception {
|
public Map<String, String> getProperties(ShellControl pc) throws Exception {
|
||||||
try (CommandProcessControl c =
|
try (CommandControl c =
|
||||||
pc.subShell(ShellDialects.BASH).command("sw_vers").start()) {
|
pc.subShell(ShellDialects.BASH).command("sw_vers").start()) {
|
||||||
var text = c.readOrThrow();
|
var text = c.readOrThrow();
|
||||||
return PropertiesFormatsParser.parse(text, ":");
|
return PropertiesFormatsParser.parse(text, ":");
|
||||||
|
@ -150,7 +167,7 @@ public interface OsType {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String determineOperatingSystemName(ShellProcessControl pc) throws Exception {
|
public String determineOperatingSystemName(ShellControl pc) throws Exception {
|
||||||
var properties = getProperties(pc);
|
var properties = getProperties(pc);
|
||||||
var name = pc.executeStringSimpleCommand(
|
var name = pc.executeStringSimpleCommand(
|
||||||
"awk '/SOFTWARE LICENSE AGREEMENT FOR macOS/' '/System/Library/CoreServices/Setup "
|
"awk '/SOFTWARE LICENSE AGREEMENT FOR macOS/' '/System/Library/CoreServices/Setup "
|
||||||
|
|
|
@ -18,18 +18,18 @@ public abstract class ProcessControlProvider {
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShellProcessControl createLocal() {
|
public static ShellControl createLocal(boolean stoppable) {
|
||||||
return INSTANCES.stream()
|
return INSTANCES.stream()
|
||||||
.map(localProcessControlProvider -> localProcessControlProvider.createLocalProcessControl())
|
.map(localProcessControlProvider -> localProcessControlProvider.createLocalProcessControl(stoppable))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShellProcessControl createSub(
|
public static ShellControl createSub(
|
||||||
ShellProcessControl parent,
|
ShellControl parent,
|
||||||
@NonNull FailableFunction<ShellProcessControl, String, Exception> commandFunction,
|
@NonNull FailableFunction<ShellControl, String, Exception> commandFunction,
|
||||||
FailableBiFunction<ShellProcessControl, String, String, Exception> terminalCommand) {
|
FailableBiFunction<ShellControl, String, String, Exception> terminalCommand) {
|
||||||
return INSTANCES.stream()
|
return INSTANCES.stream()
|
||||||
.map(localProcessControlProvider ->
|
.map(localProcessControlProvider ->
|
||||||
localProcessControlProvider.sub(parent, commandFunction, terminalCommand))
|
localProcessControlProvider.sub(parent, commandFunction, terminalCommand))
|
||||||
|
@ -38,10 +38,10 @@ public abstract class ProcessControlProvider {
|
||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CommandProcessControl createCommand(
|
public static CommandControl createCommand(
|
||||||
ShellProcessControl parent,
|
ShellControl parent,
|
||||||
@NonNull FailableFunction<ShellProcessControl, String, Exception> command,
|
@NonNull FailableFunction<ShellControl, String, Exception> command,
|
||||||
FailableFunction<ShellProcessControl, String, Exception> terminalCommand) {
|
FailableFunction<ShellControl, String, Exception> terminalCommand) {
|
||||||
return INSTANCES.stream()
|
return INSTANCES.stream()
|
||||||
.map(localProcessControlProvider ->
|
.map(localProcessControlProvider ->
|
||||||
localProcessControlProvider.command(parent, command, terminalCommand))
|
localProcessControlProvider.command(parent, command, terminalCommand))
|
||||||
|
@ -50,7 +50,7 @@ public abstract class ProcessControlProvider {
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ShellProcessControl createSsh(Object sshStore) {
|
public static ShellControl createSsh(Object sshStore) {
|
||||||
return INSTANCES.stream()
|
return INSTANCES.stream()
|
||||||
.map(localProcessControlProvider -> localProcessControlProvider.createSshControl(sshStore))
|
.map(localProcessControlProvider -> localProcessControlProvider.createSshControl(sshStore))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
|
@ -58,17 +58,17 @@ public abstract class ProcessControlProvider {
|
||||||
.orElseThrow();
|
.orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract ShellProcessControl sub(
|
public abstract ShellControl sub(
|
||||||
ShellProcessControl parent,
|
ShellControl parent,
|
||||||
@NonNull FailableFunction<ShellProcessControl, String, Exception> commandFunction,
|
@NonNull FailableFunction<ShellControl, String, Exception> commandFunction,
|
||||||
FailableBiFunction<ShellProcessControl, String, String, Exception> terminalCommand);
|
FailableBiFunction<ShellControl, String, String, Exception> terminalCommand);
|
||||||
|
|
||||||
public abstract CommandProcessControl command(
|
public abstract CommandControl command(
|
||||||
ShellProcessControl parent,
|
ShellControl parent,
|
||||||
@NonNull FailableFunction<ShellProcessControl, String, Exception> command,
|
@NonNull FailableFunction<ShellControl, String, Exception> command,
|
||||||
FailableFunction<ShellProcessControl, String, Exception> terminalCommand);
|
FailableFunction<ShellControl, String, Exception> terminalCommand);
|
||||||
|
|
||||||
public abstract ShellProcessControl createLocalProcessControl();
|
public abstract ShellControl createLocalProcessControl(boolean stoppable);
|
||||||
|
|
||||||
public abstract ShellProcessControl createSshControl(Object sshStore);
|
public abstract ShellControl createSshControl(Object sshStore);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,12 @@ import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public interface ShellProcessControl extends ProcessControl {
|
public interface ShellControl extends ProcessControl {
|
||||||
|
|
||||||
Semaphore getCommandLock();
|
Semaphore getCommandLock();
|
||||||
|
|
||||||
void onInit(Consumer<ShellProcessControl> pc);
|
void onInit(Consumer<ShellControl> pc);
|
||||||
|
|
||||||
String prepareTerminalOpen() throws Exception;
|
String prepareTerminalOpen() throws Exception;
|
||||||
|
|
||||||
|
@ -26,25 +25,25 @@ public interface ShellProcessControl extends ProcessControl {
|
||||||
public void checkRunning() throws Exception;
|
public void checkRunning() throws Exception;
|
||||||
|
|
||||||
default String executeStringSimpleCommand(String command) throws Exception {
|
default String executeStringSimpleCommand(String command) throws Exception {
|
||||||
try (CommandProcessControl c = command(command).start()) {
|
try (CommandControl c = command(command).start()) {
|
||||||
return c.readOrThrow();
|
return c.readOrThrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean executeBooleanSimpleCommand(String command) throws Exception {
|
default boolean executeBooleanSimpleCommand(String command) throws Exception {
|
||||||
try (CommandProcessControl c = command(command).start()) {
|
try (CommandControl c = command(command).start()) {
|
||||||
return c.discardAndCheckExit();
|
return c.discardAndCheckExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default void executeSimpleCommand(String command) throws Exception {
|
default void executeSimpleCommand(String command) throws Exception {
|
||||||
try (CommandProcessControl c = command(command).start()) {
|
try (CommandControl c = command(command).start()) {
|
||||||
c.discardOrThrow();
|
c.discardOrThrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default void executeSimpleCommand(String command, String failMessage) throws Exception {
|
default void executeSimpleCommand(String command, String failMessage) throws Exception {
|
||||||
try (CommandProcessControl c = command(command).start()) {
|
try (CommandControl c = command(command).start()) {
|
||||||
c.discardOrThrow();
|
c.discardOrThrow();
|
||||||
} catch (ProcessOutputException out) {
|
} catch (ProcessOutputException out) {
|
||||||
throw ProcessOutputException.of(failMessage, out);
|
throw ProcessOutputException.of(failMessage, out);
|
||||||
|
@ -63,52 +62,52 @@ public interface ShellProcessControl extends ProcessControl {
|
||||||
|
|
||||||
OsType getOsType();
|
OsType getOsType();
|
||||||
|
|
||||||
ShellProcessControl elevated(Predicate<ShellProcessControl> elevationFunction);
|
ShellControl elevated(FailableFunction<ShellControl, Boolean, Exception> elevationFunction);
|
||||||
|
|
||||||
ShellProcessControl elevation(SecretValue value);
|
ShellControl elevationPassword(SecretValue value);
|
||||||
|
|
||||||
ShellProcessControl initWith(List<String> cmds);
|
ShellControl initWith(List<String> cmds);
|
||||||
|
|
||||||
SecretValue getElevationPassword();
|
SecretValue getElevationPassword();
|
||||||
|
|
||||||
default ShellProcessControl subShell(@NonNull ShellDialect type) {
|
default ShellControl subShell(@NonNull ShellDialect type) {
|
||||||
return subShell(p -> type.getNormalOpenCommand(), (shellProcessControl, s) -> {
|
return subShell(p -> type.getNormalOpenCommand(), (shellProcessControl, s) -> {
|
||||||
return s == null ? type.getNormalOpenCommand() : type.executeCommandWithShell(s);
|
return s == null ? type.getNormalOpenCommand() : type.executeCommandWithShell(s);
|
||||||
})
|
})
|
||||||
.elevation(getElevationPassword());
|
.elevationPassword(getElevationPassword());
|
||||||
}
|
}
|
||||||
|
|
||||||
default ShellProcessControl subShell(@NonNull List<String> command) {
|
default ShellControl subShell(@NonNull List<String> command) {
|
||||||
return subShell(
|
return subShell(
|
||||||
shellProcessControl -> shellProcessControl.getShellDialect().flatten(command), null);
|
shellProcessControl -> shellProcessControl.getShellDialect().flatten(command), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
default ShellProcessControl subShell(@NonNull String command) {
|
default ShellControl subShell(@NonNull String command) {
|
||||||
return subShell(processControl -> command, null);
|
return subShell(processControl -> command, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShellProcessControl subShell(
|
ShellControl subShell(
|
||||||
FailableFunction<ShellProcessControl, String, Exception> command,
|
FailableFunction<ShellControl, String, Exception> command,
|
||||||
FailableBiFunction<ShellProcessControl, String, String, Exception> terminalCommand);
|
FailableBiFunction<ShellControl, String, String, Exception> terminalCommand);
|
||||||
|
|
||||||
void executeLine(String command) throws Exception;
|
void executeLine(String command) throws Exception;
|
||||||
|
|
||||||
void cd(String directory) throws Exception;
|
void cd(String directory) throws Exception;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
ShellProcessControl start() throws Exception;
|
ShellControl start() throws Exception;
|
||||||
|
|
||||||
CommandProcessControl command(FailableFunction<ShellProcessControl, String, Exception> command);
|
CommandControl command(FailableFunction<ShellControl, String, Exception> command);
|
||||||
|
|
||||||
CommandProcessControl command(
|
CommandControl command(
|
||||||
FailableFunction<ShellProcessControl, String, Exception> command,
|
FailableFunction<ShellControl, String, Exception> command,
|
||||||
FailableFunction<ShellProcessControl, String, Exception> terminalCommand);
|
FailableFunction<ShellControl, String, Exception> terminalCommand);
|
||||||
|
|
||||||
default CommandProcessControl command(String command) {
|
default CommandControl command(String command) {
|
||||||
return command(shellProcessControl -> command);
|
return command(shellProcessControl -> command);
|
||||||
}
|
}
|
||||||
|
|
||||||
default CommandProcessControl command(List<String> command) {
|
default CommandControl command(List<String> command) {
|
||||||
return command(shellProcessControl -> shellProcessControl.getShellDialect().flatten(command));
|
return command(shellProcessControl -> shellProcessControl.getShellDialect().flatten(command));
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,9 @@ public interface ShellDialect {
|
||||||
|
|
||||||
String addInlineVariablesToCommand(Map<String, String> variables, String command);
|
String addInlineVariablesToCommand(Map<String, String> variables, String command);
|
||||||
|
|
||||||
Stream<FileSystem.FileEntry> listFiles(FileSystem fs, ShellProcessControl control, String dir) throws Exception;
|
Stream<FileSystem.FileEntry> listFiles(FileSystem fs, ShellControl control, String dir) throws Exception;
|
||||||
|
|
||||||
Stream<String> listRoots(ShellProcessControl control) throws Exception;
|
Stream<String> listRoots(ShellControl control) throws Exception;
|
||||||
|
|
||||||
String getPauseCommand();
|
String getPauseCommand();
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ public interface ShellDialect {
|
||||||
|
|
||||||
String getFileMoveCommand(String oldFile, String newFile);
|
String getFileMoveCommand(String oldFile, String newFile);
|
||||||
|
|
||||||
CommandProcessControl getStreamFileWriteCommand(ShellProcessControl processControl, String file);
|
CommandControl getStreamFileWriteCommand(ShellControl processControl, String file);
|
||||||
|
|
||||||
String getTextFileWriteCommand(String content, String file);
|
String getTextFileWriteCommand(String content, String file);
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ public interface ShellDialect {
|
||||||
|
|
||||||
String getWhichCommand(String executable);
|
String getWhichCommand(String executable);
|
||||||
|
|
||||||
Charset determineCharset(ShellProcessControl control) throws Exception;
|
Charset determineCharset(ShellControl control) throws Exception;
|
||||||
|
|
||||||
NewLine getNewLine();
|
NewLine getNewLine();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package io.xpipe.core.store;
|
package io.xpipe.core.store;
|
||||||
|
|
||||||
import io.xpipe.core.process.CommandProcessControl;
|
import io.xpipe.core.process.CommandControl;
|
||||||
|
|
||||||
public interface CommandExecutionStore extends DataStore, LaunchableStore {
|
public interface CommandExecutionStore extends DataStore, LaunchableStore {
|
||||||
|
|
||||||
|
@ -9,5 +9,5 @@ public interface CommandExecutionStore extends DataStore, LaunchableStore {
|
||||||
return create().prepareTerminalOpen();
|
return create().prepareTerminalOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandProcessControl create() throws Exception;
|
CommandControl create() throws Exception;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package io.xpipe.core.store;
|
package io.xpipe.core.store;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -15,19 +15,19 @@ import java.util.stream.Stream;
|
||||||
public class ConnectionFileSystem implements FileSystem {
|
public class ConnectionFileSystem implements FileSystem {
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private final ShellProcessControl shellProcessControl;
|
private final ShellControl shellControl;
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private final ShellStore store;
|
private final ShellStore store;
|
||||||
|
|
||||||
public ConnectionFileSystem(ShellProcessControl shellProcessControl, ShellStore store) {
|
public ConnectionFileSystem(ShellControl shellControl, ShellStore store) {
|
||||||
this.shellProcessControl = shellProcessControl;
|
this.shellControl = shellControl;
|
||||||
this.store = store;
|
this.store = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> listRoots() throws Exception {
|
public List<String> listRoots() throws Exception {
|
||||||
return shellProcessControl.getShellDialect().listRoots(shellProcessControl).toList();
|
return shellControl.getShellDialect().listRoots(shellControl).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -35,7 +35,7 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<FileEntry> listFiles(String file) throws Exception {
|
public Stream<FileEntry> listFiles(String file) throws Exception {
|
||||||
return shellProcessControl.getShellDialect().listFiles(this, shellProcessControl, file);
|
return shellControl.getShellDialect().listFiles(this, shellControl, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -44,33 +44,33 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<ShellProcessControl> getShell() {
|
public Optional<ShellControl> getShell() {
|
||||||
return Optional.of(shellProcessControl);
|
return Optional.of(shellControl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileSystem open() throws Exception {
|
public FileSystem open() throws Exception {
|
||||||
shellProcessControl.start();
|
shellControl.start();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInput(String file) throws Exception {
|
public InputStream openInput(String file) throws Exception {
|
||||||
return shellProcessControl.command(proc ->
|
return shellControl.command(proc ->
|
||||||
proc.getShellDialect().getFileReadCommand(proc.getOsType().normalizeFileName(file)))
|
proc.getShellDialect().getFileReadCommand(proc.getOsType().normalizeFileName(file)))
|
||||||
.startExternalStdout();
|
.startExternalStdout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OutputStream openOutput(String file) throws Exception {
|
public OutputStream openOutput(String file) throws Exception {
|
||||||
return shellProcessControl.getShellDialect()
|
return shellControl.getShellDialect()
|
||||||
.getStreamFileWriteCommand(shellProcessControl, shellProcessControl.getOsType().normalizeFileName(file))
|
.getStreamFileWriteCommand(shellControl, shellControl.getOsType().normalizeFileName(file))
|
||||||
.startExternalStdin();
|
.startExternalStdin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String file) throws Exception {
|
public boolean exists(String file) throws Exception {
|
||||||
try (var pc = shellProcessControl.command(proc -> proc.getShellDialect()
|
try (var pc = shellControl.command(proc -> proc.getShellDialect()
|
||||||
.getFileExistsCommand(proc.getOsType().normalizeFileName(file)))
|
.getFileExistsCommand(proc.getOsType().normalizeFileName(file)))
|
||||||
.start()) {
|
.start()) {
|
||||||
return pc.discardAndCheckExit();
|
return pc.discardAndCheckExit();
|
||||||
|
@ -79,7 +79,7 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(String file) throws Exception {
|
public void delete(String file) throws Exception {
|
||||||
try (var pc = shellProcessControl.command(proc -> proc.getShellDialect()
|
try (var pc = shellControl.command(proc -> proc.getShellDialect()
|
||||||
.getFileDeleteCommand(proc.getOsType().normalizeFileName(file)))
|
.getFileDeleteCommand(proc.getOsType().normalizeFileName(file)))
|
||||||
.start()) {
|
.start()) {
|
||||||
pc.discardOrThrow();
|
pc.discardOrThrow();
|
||||||
|
@ -88,7 +88,7 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copy(String file, String newFile) throws Exception {
|
public void copy(String file, String newFile) throws Exception {
|
||||||
try (var pc = shellProcessControl.command(proc -> proc.getShellDialect()
|
try (var pc = shellControl.command(proc -> proc.getShellDialect()
|
||||||
.getFileCopyCommand(proc.getOsType().normalizeFileName(file), proc.getOsType().normalizeFileName(newFile))).complex()
|
.getFileCopyCommand(proc.getOsType().normalizeFileName(file), proc.getOsType().normalizeFileName(newFile))).complex()
|
||||||
.start()) {
|
.start()) {
|
||||||
pc.discardOrThrow();
|
pc.discardOrThrow();
|
||||||
|
@ -97,7 +97,7 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void move(String file, String newFile) throws Exception {
|
public void move(String file, String newFile) throws Exception {
|
||||||
try (var pc = shellProcessControl.command(proc -> proc.getShellDialect()
|
try (var pc = shellControl.command(proc -> proc.getShellDialect()
|
||||||
.getFileMoveCommand(proc.getOsType().normalizeFileName(file), proc.getOsType().normalizeFileName(newFile))).complex()
|
.getFileMoveCommand(proc.getOsType().normalizeFileName(file), proc.getOsType().normalizeFileName(newFile))).complex()
|
||||||
.start()) {
|
.start()) {
|
||||||
pc.discardOrThrow();
|
pc.discardOrThrow();
|
||||||
|
@ -106,7 +106,7 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mkdirs(String file) throws Exception {
|
public boolean mkdirs(String file) throws Exception {
|
||||||
try (var pc = shellProcessControl.command(proc -> proc.getShellDialect()
|
try (var pc = shellControl.command(proc -> proc.getShellDialect()
|
||||||
.flatten(proc.getShellDialect()
|
.flatten(proc.getShellDialect()
|
||||||
.getMkdirsCommand(proc.getOsType().normalizeFileName(file))))
|
.getMkdirsCommand(proc.getOsType().normalizeFileName(file))))
|
||||||
.start()) {
|
.start()) {
|
||||||
|
@ -116,7 +116,7 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void touch(String file) throws Exception {
|
public void touch(String file) throws Exception {
|
||||||
try (var pc = shellProcessControl.command(proc -> proc.getShellDialect()
|
try (var pc = shellControl.command(proc -> proc.getShellDialect()
|
||||||
.getFileTouchCommand(proc.getOsType().normalizeFileName(file))).complex()
|
.getFileTouchCommand(proc.getOsType().normalizeFileName(file))).complex()
|
||||||
.start()) {
|
.start()) {
|
||||||
pc.discardOrThrow();
|
pc.discardOrThrow();
|
||||||
|
@ -125,6 +125,6 @@ public class ConnectionFileSystem implements FileSystem {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
shellProcessControl.close();
|
shellControl.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package io.xpipe.core.store;
|
package io.xpipe.core.store;
|
||||||
|
|
||||||
import io.xpipe.core.impl.FileNames;
|
import io.xpipe.core.impl.FileNames;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ public interface FileSystem extends Closeable, AutoCloseable {
|
||||||
|
|
||||||
FileSystemStore getStore();
|
FileSystemStore getStore();
|
||||||
|
|
||||||
Optional<ShellProcessControl> getShell();
|
Optional<ShellControl> getShell();
|
||||||
|
|
||||||
FileSystem open() throws Exception;
|
FileSystem open() throws Exception;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package io.xpipe.core.store;
|
||||||
import io.xpipe.core.impl.LocalStore;
|
import io.xpipe.core.impl.LocalStore;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellDialect;
|
import io.xpipe.core.process.ShellDialect;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStor
|
||||||
return create().prepareTerminalOpen();
|
return create().prepareTerminalOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
default ShellProcessControl create() {
|
default ShellControl create() {
|
||||||
var pc = createControl();
|
var pc = createControl();
|
||||||
pc.onInit(processControl -> {
|
pc.onInit(processControl -> {
|
||||||
setState("type", processControl.getShellDialect());
|
setState("type", processControl.getShellDialect());
|
||||||
|
@ -49,7 +49,7 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStor
|
||||||
return getState("charset", Charset.class, null);
|
return getState("charset", Charset.class, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShellProcessControl createControl();
|
ShellControl createControl();
|
||||||
|
|
||||||
public default ShellDialect determineType() throws Exception {
|
public default ShellDialect determineType() throws Exception {
|
||||||
try (var pc = create().start()) {
|
try (var pc = create().start()) {
|
||||||
|
@ -59,7 +59,7 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStor
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default void validate() throws Exception {
|
default void validate() throws Exception {
|
||||||
try (ShellProcessControl pc = create().start()) {}
|
try (ShellControl pc = create().start()) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public default String queryMachineName() throws Exception {
|
public default String queryMachineName() throws Exception {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package io.xpipe.core.util;
|
package io.xpipe.core.util;
|
||||||
|
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
|
@ -19,7 +19,7 @@ public abstract class ProxyManagerProvider {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Optional<String> checkCompatibility(ShellProcessControl pc) throws Exception;
|
public abstract Optional<String> checkCompatibility(ShellControl pc) throws Exception;
|
||||||
|
|
||||||
public abstract boolean setup(ShellProcessControl pc) throws Exception;
|
public abstract boolean setup(ShellControl pc) throws Exception;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package io.xpipe.core.util;
|
package io.xpipe.core.util;
|
||||||
|
|
||||||
import io.xpipe.core.impl.FileNames;
|
import io.xpipe.core.impl.FileNames;
|
||||||
import io.xpipe.core.process.CommandProcessControl;
|
import io.xpipe.core.process.CommandControl;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ProcessOutputException;
|
import io.xpipe.core.process.ProcessOutputException;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -153,21 +153,21 @@ public class XPipeInstallation {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String queryInstallationVersion(ShellProcessControl p, String exec) throws Exception {
|
public static String queryInstallationVersion(ShellControl p, String exec) throws Exception {
|
||||||
try (CommandProcessControl c = p.command(List.of(exec, "version")).start()) {
|
try (CommandControl c = p.command(List.of(exec, "version")).start()) {
|
||||||
return c.readOrThrow();
|
return c.readOrThrow();
|
||||||
} catch (ProcessOutputException ex) {
|
} catch (ProcessOutputException ex) {
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getInstallationExecutable(ShellProcessControl p, String installation) throws Exception {
|
public static String getInstallationExecutable(ShellControl p, String installation) throws Exception {
|
||||||
var executable = getDaemonExecutablePath(p.getOsType());
|
var executable = getDaemonExecutablePath(p.getOsType());
|
||||||
var file = FileNames.join(installation, executable);
|
var file = FileNames.join(installation, executable);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getDataBasePath(ShellProcessControl p) throws Exception {
|
public static String getDataBasePath(ShellControl p) throws Exception {
|
||||||
if (p.getOsType().equals(OsType.WINDOWS)) {
|
if (p.getOsType().equals(OsType.WINDOWS)) {
|
||||||
var base = p.executeStringSimpleCommand(p.getShellDialect().getPrintVariableCommand("userprofile"));
|
var base = p.executeStringSimpleCommand(p.getShellDialect().getPrintVariableCommand("userprofile"));
|
||||||
return FileNames.join(base, ".xpipe");
|
return FileNames.join(base, ".xpipe");
|
||||||
|
@ -218,7 +218,7 @@ public class XPipeInstallation {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getDefaultInstallationBasePath(ShellProcessControl p, boolean acceptPortable)
|
public static String getDefaultInstallationBasePath(ShellControl p, boolean acceptPortable)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
if (acceptPortable) {
|
if (acceptPortable) {
|
||||||
var customHome = p.executeStringSimpleCommand(p.getShellDialect().getPrintVariableCommand("XPIPE_HOME"));
|
var customHome = p.executeStringSimpleCommand(p.getShellDialect().getPrintVariableCommand("XPIPE_HOME"));
|
||||||
|
|
|
@ -2,7 +2,7 @@ package io.xpipe.core.util;
|
||||||
|
|
||||||
import io.xpipe.core.impl.FileNames;
|
import io.xpipe.core.impl.FileNames;
|
||||||
import io.xpipe.core.process.OsType;
|
import io.xpipe.core.process.OsType;
|
||||||
import io.xpipe.core.process.ShellProcessControl;
|
import io.xpipe.core.process.ShellControl;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -19,7 +19,7 @@ public class XPipeTempDirectory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String get(ShellProcessControl proc) throws Exception {
|
public static String get(ShellControl proc) throws Exception {
|
||||||
var base = proc.getOsType().getTempDirectory(proc);
|
var base = proc.getOsType().getTempDirectory(proc);
|
||||||
var dir = FileNames.join(base, "xpipe");
|
var dir = FileNames.join(base, "xpipe");
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ public class XPipeTempDirectory {
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clear(ShellProcessControl proc) throws Exception {
|
public static void clear(ShellControl proc) throws Exception {
|
||||||
var dir = get(proc);
|
var dir = get(proc);
|
||||||
if (!proc.executeBooleanSimpleCommand(proc.getShellDialect().getFileDeleteCommand(dir))) {
|
if (!proc.executeBooleanSimpleCommand(proc.getShellDialect().getFileDeleteCommand(dir))) {
|
||||||
throw new IOException("Unable to delete temporary directory " + dir);
|
throw new IOException("Unable to delete temporary directory " + dir);
|
||||||
|
|
Loading…
Reference in a new issue