mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-10-03 02:26:34 +13:00
Try to fix push
This commit is contained in:
parent
8478992798
commit
94c77ab1c6
51 changed files with 907 additions and 218 deletions
|
@ -132,7 +132,7 @@ application {
|
|||
|
||||
run {
|
||||
systemProperty 'io.xpipe.app.mode', 'gui'
|
||||
systemProperty 'io.xpipe.app.dataDir', "$projectDir/local3/"
|
||||
systemProperty 'io.xpipe.app.dataDir', "$projectDir/local_stage/"
|
||||
systemProperty 'io.xpipe.app.writeLogs', "true"
|
||||
systemProperty 'io.xpipe.app.writeSysOut', "true"
|
||||
systemProperty 'io.xpipe.app.developerMode', "true"
|
||||
|
|
|
@ -80,8 +80,8 @@ public class AppLayoutComp extends Comp<CompStructure<BorderPane>> {
|
|||
pane.setCenter(r);
|
||||
});
|
||||
pane.setCenter(selected.getValue().comp().createRegion());
|
||||
pane.setPrefWidth(1200);
|
||||
pane.setPrefHeight(700);
|
||||
pane.setPrefWidth(1280);
|
||||
pane.setPrefHeight(720);
|
||||
AppFont.normal(pane);
|
||||
return new SimpleCompStructure<>(pane);
|
||||
}
|
||||
|
|
|
@ -5,16 +5,12 @@ import io.xpipe.extension.fxcomps.Comp;
|
|||
import io.xpipe.extension.fxcomps.CompStructure;
|
||||
import io.xpipe.extension.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.extension.fxcomps.util.SimpleChangeListener;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.PauseTransition;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.util.Duration;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
|
||||
|
@ -80,17 +76,6 @@ public class LazyTextFieldComp extends Comp<LazyTextFieldComp.Structure> {
|
|||
currentValue.setValue(newValue);
|
||||
});
|
||||
|
||||
Animation delay = new PauseTransition(Duration.millis(800));
|
||||
delay.setOnFinished(e -> {
|
||||
r.setDisable(false);
|
||||
r.requestFocus();
|
||||
});
|
||||
sp.addEventFilter(MouseEvent.MOUSE_ENTERED, e -> {
|
||||
delay.playFromStart();
|
||||
});
|
||||
sp.addEventFilter(MouseEvent.MOUSE_EXITED, e -> {
|
||||
delay.stop();
|
||||
});
|
||||
r.focusedProperty().addListener((c, o, n) -> {
|
||||
if (!n) {
|
||||
r.setDisable(true);
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
package io.xpipe.app.comp.base;
|
||||
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.CompStructure;
|
||||
import io.xpipe.extension.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.extension.fxcomps.util.BindingsHelper;
|
||||
import io.xpipe.extension.fxcomps.util.PlatformThread;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ListBoxViewComp<T> extends Comp<CompStructure<VBox>> {
|
||||
|
||||
private final ObservableList<T> shown;
|
||||
private final ObservableList<T> all;
|
||||
private final Function<T, Comp<?>> compFunction;
|
||||
|
||||
public ListBoxViewComp(
|
||||
ObservableList<T> shown, ObservableList<T> all, Function<T, Comp<?>> compFunction) {
|
||||
this.shown = PlatformThread.sync(shown);
|
||||
this.all = PlatformThread.sync(all);
|
||||
this.compFunction = compFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<VBox> createBase() {
|
||||
Map<T, Region> cache = new HashMap<>();
|
||||
|
||||
VBox listView = new VBox();
|
||||
listView.setFocusTraversable(false);
|
||||
|
||||
refresh(listView, shown, cache, false);
|
||||
listView.requestLayout();
|
||||
|
||||
shown.addListener((ListChangeListener<? super T>) (c) -> {
|
||||
refresh(listView, c.getList(), cache, true);
|
||||
});
|
||||
|
||||
all.addListener((ListChangeListener<? super T>) c -> {
|
||||
cache.keySet().retainAll(c.getList());
|
||||
});
|
||||
|
||||
return new SimpleCompStructure<>(listView);
|
||||
}
|
||||
|
||||
private void refresh(VBox listView, List<? extends T> c, Map<T, Region> cache, boolean asynchronous) {
|
||||
Runnable update = () -> {
|
||||
var newShown = c.stream()
|
||||
.map(v -> {
|
||||
if (!cache.containsKey(v)) {
|
||||
cache.put(v, compFunction.apply(v).createRegion());
|
||||
}
|
||||
|
||||
return cache.get(v);
|
||||
})
|
||||
.toList();
|
||||
|
||||
if (!listView.getChildren().equals(newShown)) {
|
||||
BindingsHelper.setContent(listView.getChildren(), newShown);
|
||||
listView.layout();
|
||||
}
|
||||
};
|
||||
|
||||
if (asynchronous) {
|
||||
Platform.runLater(update);
|
||||
} else {
|
||||
PlatformThread.runLaterIfNeeded(update);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.app.comp.storage.store;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import io.xpipe.app.comp.base.LazyTextFieldComp;
|
||||
import io.xpipe.app.comp.base.LoadingOverlayComp;
|
||||
import io.xpipe.app.core.AppFont;
|
||||
|
@ -10,6 +11,7 @@ import io.xpipe.extension.I18n;
|
|||
import io.xpipe.extension.event.ErrorEvent;
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.SimpleComp;
|
||||
import io.xpipe.extension.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.extension.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.extension.fxcomps.augment.PopupMenuAugment;
|
||||
import io.xpipe.extension.fxcomps.impl.FancyTooltipAugment;
|
||||
|
@ -100,6 +102,9 @@ public class StoreEntryComp extends SimpleComp {
|
|||
var imageComp = new PrettyImageComp(new SimpleStringProperty(img), 55, 45);
|
||||
var storeIcon = imageComp.createRegion();
|
||||
storeIcon.getStyleClass().add("icon");
|
||||
if (entry.getState().getValue().isUsable()) {
|
||||
new FancyTooltipAugment<>(new SimpleStringProperty(entry.getEntry().getProvider().getDisplayName())).augment(storeIcon);
|
||||
}
|
||||
return storeIcon;
|
||||
}
|
||||
|
||||
|
@ -117,6 +122,7 @@ public class StoreEntryComp extends SimpleComp {
|
|||
|
||||
var storeIcon = createIcon();
|
||||
|
||||
|
||||
grid.getColumnConstraints()
|
||||
.addAll(
|
||||
createShareConstraint(grid, STORE_TYPE_WIDTH), createShareConstraint(grid, NAME_WIDTH),
|
||||
|
@ -133,7 +139,7 @@ public class StoreEntryComp extends SimpleComp {
|
|||
AppFont.small(size);
|
||||
AppFont.small(date);
|
||||
|
||||
grid.getStyleClass().add("store-entry-comp");
|
||||
grid.getStyleClass().add("store-entry-grid");
|
||||
|
||||
grid.setOnMouseClicked(event -> {
|
||||
if (entry.getEditable().get()) {
|
||||
|
@ -143,7 +149,12 @@ public class StoreEntryComp extends SimpleComp {
|
|||
|
||||
applyState(grid);
|
||||
|
||||
return grid;
|
||||
var button = new JFXButton();
|
||||
button.setGraphic(grid);
|
||||
GrowAugment.create(true, false).augment(new SimpleCompStructure<>(grid));
|
||||
button.getStyleClass().add("store-entry-comp");
|
||||
button.setMaxWidth(2000);
|
||||
return button;
|
||||
}
|
||||
|
||||
private Comp<?> createButtonBar() {
|
||||
|
|
|
@ -4,7 +4,6 @@ import io.xpipe.app.comp.base.ListViewComp;
|
|||
import io.xpipe.app.comp.base.MultiContentComp;
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.SimpleComp;
|
||||
import io.xpipe.extension.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.extension.fxcomps.util.BindingsHelper;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
|
@ -15,14 +14,18 @@ import java.util.Map;
|
|||
public class StoreEntryListComp extends SimpleComp {
|
||||
|
||||
private Comp<?> createList() {
|
||||
var topLevel = StoreEntrySection.createTopLevels();
|
||||
var filtered = BindingsHelper.filteredContentBinding(
|
||||
topLevel,
|
||||
StoreViewState.get().getFilterString().map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s))));
|
||||
var content = new ListViewComp<>(
|
||||
StoreViewState.get().getShownEntries(),
|
||||
StoreViewState.get().getAllEntries(),
|
||||
filtered,
|
||||
topLevel,
|
||||
null,
|
||||
(StoreEntryWrapper e) -> {
|
||||
return new StoreEntryComp(e).apply(GrowAugment.create(true, false));
|
||||
(StoreEntrySection e) -> {
|
||||
return e.comp(true);
|
||||
});
|
||||
return content;
|
||||
return content.styleClass("store-list-comp");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package io.xpipe.app.comp.storage.store;
|
||||
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
import io.xpipe.app.comp.storage.StorageFilter;
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.augment.GrowAugment;
|
||||
import io.xpipe.extension.fxcomps.impl.HorizontalComp;
|
||||
import io.xpipe.extension.fxcomps.impl.VerticalComp;
|
||||
import io.xpipe.extension.fxcomps.util.BindingsHelper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class StoreEntrySection implements StorageFilter.Filterable {
|
||||
|
||||
public StoreEntrySection(StoreEntryWrapper entry, ObservableList<StoreEntrySection> children) {
|
||||
this.entry = entry;
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public static ObservableList<StoreEntrySection> createTopLevels() {
|
||||
var topLevel = BindingsHelper.mappedContentBinding(
|
||||
StoreViewState.get()
|
||||
.getAllEntries()
|
||||
.filtered(storeEntryWrapper ->
|
||||
!storeEntryWrapper.getEntry().getState().isUsable()
|
||||
|| storeEntryWrapper
|
||||
.getEntry()
|
||||
.getProvider()
|
||||
.getParent(storeEntryWrapper
|
||||
.getEntry()
|
||||
.getStore())
|
||||
== null),
|
||||
storeEntryWrapper -> create(storeEntryWrapper));
|
||||
var ordered = BindingsHelper.orderedContentBinding(
|
||||
topLevel,
|
||||
Comparator.<StoreEntrySection, Instant>comparing(storeEntrySection ->
|
||||
storeEntrySection.entry.lastAccessProperty().getValue())
|
||||
.reversed());
|
||||
return ordered;
|
||||
}
|
||||
|
||||
public static StoreEntrySection create(StoreEntryWrapper e) {
|
||||
if (!e.getEntry().getState().isUsable()) {
|
||||
return new StoreEntrySection(e, FXCollections.observableArrayList());
|
||||
}
|
||||
|
||||
var children = BindingsHelper.mappedContentBinding(
|
||||
StoreViewState.get()
|
||||
.getAllEntries()
|
||||
.filtered(other -> other.getEntry().getState().isUsable()
|
||||
&& e.getEntry()
|
||||
.getStore()
|
||||
.equals(other.getEntry()
|
||||
.getProvider()
|
||||
.getParent(other.getEntry().getStore()))),
|
||||
entry1 -> create(entry1));
|
||||
var ordered = BindingsHelper.orderedContentBinding(
|
||||
children,
|
||||
Comparator.<StoreEntrySection, Instant>comparing(storeEntrySection ->
|
||||
storeEntrySection.entry.lastAccessProperty().getValue())
|
||||
.reversed());
|
||||
return new StoreEntrySection(e, ordered);
|
||||
}
|
||||
|
||||
private final StoreEntryWrapper entry;
|
||||
private final ObservableList<StoreEntrySection> children;
|
||||
|
||||
public Comp<?> comp(boolean top) {
|
||||
var root = new StoreEntryComp(entry).apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS));
|
||||
var icon = Comp.of(() -> {
|
||||
var padding = new FontIcon("mdal-arrow_forward_ios");
|
||||
padding.setIconSize(14);
|
||||
var pain = new StackPane(padding);
|
||||
pain.setMinWidth(20);
|
||||
pain.setMaxHeight(20);
|
||||
return pain;
|
||||
});
|
||||
List<Comp<?>> topEntryList = top ? List.of(root) : List.of(icon, root);
|
||||
|
||||
if (children.size() == 0) {
|
||||
return new HorizontalComp(topEntryList);
|
||||
}
|
||||
|
||||
var all = BindingsHelper.orderedContentBinding(
|
||||
children,
|
||||
Comparator.comparing(storeEntrySection ->
|
||||
storeEntrySection.entry.lastAccessProperty().getValue()));
|
||||
var shown = BindingsHelper.filteredContentBinding(
|
||||
all,
|
||||
StoreViewState.get().getFilterString().map(s -> (storeEntrySection -> storeEntrySection.shouldShow(s))));
|
||||
var content = new ListBoxViewComp<>(shown, all, (StoreEntrySection e) -> {
|
||||
return e.comp(false).apply(GrowAugment.create(true, false));
|
||||
})
|
||||
.apply(struc -> HBox.setHgrow(struc.get(), Priority.ALWAYS))
|
||||
.apply(struc -> struc.get().backgroundProperty().set(Background.fill(Color.color(0, 0, 0, 0.01))));
|
||||
var spacer = Comp.of(() -> {
|
||||
var padding = new Region();
|
||||
padding.setMinWidth(25);
|
||||
padding.setMaxWidth(25);
|
||||
return padding;
|
||||
});
|
||||
return new VerticalComp(List.of(
|
||||
new HorizontalComp(topEntryList),
|
||||
new HorizontalComp(List.of(spacer, content))
|
||||
.apply(struc -> struc.get().setFillHeight(true))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShow(String filter) {
|
||||
return entry.shouldShow(filter)
|
||||
|| children.stream().anyMatch(storeEntrySection -> storeEntrySection.shouldShow(filter));
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import javafx.application.Platform;
|
|||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
|
@ -95,6 +96,10 @@ public class StoreViewState {
|
|||
return filter;
|
||||
}
|
||||
|
||||
public ObservableValue<String> getFilterString() {
|
||||
return filter.filterProperty();
|
||||
}
|
||||
|
||||
public ObservableList<StoreEntryWrapper> getAllEntries() {
|
||||
return allEntries;
|
||||
}
|
||||
|
|
|
@ -78,6 +78,14 @@ public class App extends Application {
|
|||
focus();
|
||||
});
|
||||
appWindow.show();
|
||||
|
||||
// For demo purposes
|
||||
if (true) {
|
||||
stage.setX(310);
|
||||
stage.setY(178);
|
||||
stage.setWidth(1300);
|
||||
stage.setHeight(730);
|
||||
}
|
||||
}
|
||||
|
||||
public void focus() {
|
||||
|
|
|
@ -60,6 +60,7 @@ public class DataSourceCollection extends StorageElement {
|
|||
var json = mapper.readTree(dir.resolve("collection.json").toFile());
|
||||
var uuid = UUID.fromString(json.required("uuid").textValue());
|
||||
var name = json.required("name").textValue();
|
||||
Objects.requireNonNull(name);
|
||||
var lastModified = Instant.parse(json.required("lastModified").textValue());
|
||||
|
||||
JavaType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, UUID.class);
|
||||
|
|
|
@ -82,7 +82,7 @@ public abstract class DataStorage {
|
|||
}
|
||||
|
||||
public DataSourceCollection getInternalCollection() {
|
||||
var found = sourceCollections.stream().filter(o -> o.getName().equals("Internal")).findAny();
|
||||
var found = sourceCollections.stream().filter(o -> o.getName() != null && o.getName().equals("Internal")).findAny();
|
||||
if (found.isPresent()) {
|
||||
return found.get();
|
||||
}
|
||||
|
|
|
@ -17,16 +17,16 @@ public class DataStorageWriter {
|
|||
public static JsonNode storeToNode(DataStore store) {
|
||||
var mapper = JacksonMapper.newMapper();
|
||||
var tree = mapper.valueToTree(store);
|
||||
return replaceReferencesWithIds(store, tree);
|
||||
return replaceReferencesWithIds(tree, true);
|
||||
}
|
||||
|
||||
public static JsonNode sourceToNode(DataSource<?> source) {
|
||||
var mapper = JacksonMapper.newMapper();
|
||||
var tree = mapper.valueToTree(source);
|
||||
return replaceReferencesWithIds(source, tree);
|
||||
return replaceReferencesWithIds(tree, true);
|
||||
}
|
||||
|
||||
private static JsonNode replaceReferencesWithIds(Object root, JsonNode node) {
|
||||
private static JsonNode replaceReferencesWithIds(JsonNode node, boolean isRoot) {
|
||||
var mapper = JacksonMapper.newMapper();
|
||||
|
||||
node = replaceReferencesWithIds(
|
||||
|
@ -38,7 +38,7 @@ public class DataStorageWriter {
|
|||
|
||||
try {
|
||||
var store = mapper.treeToValue(possibleReference, DataStore.class);
|
||||
if (root == null || !root.equals(store)) {
|
||||
if (!isRoot) {
|
||||
var found = DataStorage.get().getEntryByStore(store);
|
||||
return found.map(dataSourceEntry -> dataSourceEntry.getUuid());
|
||||
}
|
||||
|
@ -46,14 +46,14 @@ public class DataStorageWriter {
|
|||
}
|
||||
return Optional.empty();
|
||||
},
|
||||
"storeId");
|
||||
"storeId", isRoot);
|
||||
|
||||
node = replaceReferencesWithIds(
|
||||
node,
|
||||
possibleReference -> {
|
||||
try {
|
||||
var source = mapper.treeToValue(possibleReference, DataSource.class);
|
||||
if (root == null || !root.equals(source)) {
|
||||
if (!isRoot) {
|
||||
var found = DataStorage.get().getEntryBySource(source);
|
||||
return found.map(dataSourceEntry -> dataSourceEntry.getUuid());
|
||||
}
|
||||
|
@ -61,13 +61,13 @@ public class DataStorageWriter {
|
|||
}
|
||||
return Optional.empty();
|
||||
},
|
||||
"sourceId");
|
||||
"sourceId", isRoot);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static JsonNode replaceReferencesWithIds(
|
||||
JsonNode node, Function<JsonNode, Optional<UUID>> function, String key) {
|
||||
JsonNode node, Function<JsonNode, Optional<UUID>> function, String key, boolean isRoot) {
|
||||
if (!node.isObject()) {
|
||||
return node;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public class DataStorageWriter {
|
|||
|
||||
var replacement = JsonNodeFactory.instance.objectNode();
|
||||
node.fields().forEachRemaining(stringJsonNodeEntry -> {
|
||||
var resolved = replaceReferencesWithIds(null, stringJsonNodeEntry.getValue());
|
||||
var resolved = replaceReferencesWithIds(stringJsonNodeEntry.getValue(), false);
|
||||
replacement.set(stringJsonNodeEntry.getKey(), resolved);
|
||||
});
|
||||
return replacement;
|
||||
|
|
|
@ -75,3 +75,7 @@
|
|||
-fx-padding: 0;
|
||||
-fx-focus-color: transparent;
|
||||
}
|
||||
|
||||
.store-list-comp .list-cell {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
|
|
@ -1,27 +1,30 @@
|
|||
|
||||
.store-entry-comp .date, .store-entry-comp .summary {
|
||||
.store-entry-grid .date, .store-entry-grid .summary {
|
||||
-fx-text-fill: -xp-text-light;
|
||||
}
|
||||
|
||||
.store-entry-comp:failed .jfx-text-field {
|
||||
.store-entry-grid:failed .jfx-text-field {
|
||||
-fx-text-fill: red;
|
||||
}
|
||||
|
||||
.store-entry-comp:incomplete .jfx-text-field {
|
||||
.store-entry-grid:incomplete .jfx-text-field {
|
||||
-fx-text-fill: gray;
|
||||
}
|
||||
.store-entry-comp:incomplete .summary {
|
||||
.store-entry-grid:incomplete .summary {
|
||||
-fx-text-fill: gray;
|
||||
}
|
||||
.store-entry-comp:incomplete .information {
|
||||
.store-entry-grid:incomplete .information {
|
||||
-fx-text-fill: gray;
|
||||
}
|
||||
.store-entry-comp:incomplete .date {
|
||||
.store-entry-grid:incomplete .date {
|
||||
-fx-text-fill: gray;
|
||||
}
|
||||
.store-entry-comp:incomplete .icon {
|
||||
.store-entry-grid:incomplete .icon {
|
||||
-fx-opacity: 0.5;
|
||||
}
|
||||
.store-entry-comp {
|
||||
-fx-padding: 6px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -248,6 +248,7 @@ productionTest {
|
|||
// + System.getProperty("java.vm.version") + ")")
|
||||
|
||||
graalvmNative {
|
||||
testSupport = false
|
||||
binaries {
|
||||
main {
|
||||
imageName = 'xpipe' // The name of the native image, defaults to the project name
|
||||
|
@ -261,8 +262,6 @@ graalvmNative {
|
|||
'-Dpicocli.converters.excludes=java.time.*,java.sql.*'
|
||||
)
|
||||
buildArgs.addAll(
|
||||
'--static',
|
||||
'--libc=musl',
|
||||
// '--debug-attach=8000',
|
||||
'--enable-https',
|
||||
'--install-exit-handlers',
|
||||
|
@ -279,6 +278,13 @@ graalvmNative {
|
|||
'org.ocpsoft.prettytime,' +
|
||||
'com.fasterxml.jackson')
|
||||
|
||||
if (org.gradle.internal.os.OperatingSystem.current().isLinux()) {
|
||||
buildArgs.addAll(
|
||||
'--static',
|
||||
'--libc=musl'
|
||||
)
|
||||
}
|
||||
|
||||
// Build Time
|
||||
systemProperties(prodProperties)
|
||||
}
|
||||
|
|
44
cli/musl-setup.sh
Executable file
44
cli/musl-setup.sh
Executable file
|
@ -0,0 +1,44 @@
|
|||
#!/bin/sh
|
||||
# This script builds the bundle inside the Docker container and then copies it to the host machine.
|
||||
|
||||
set -e
|
||||
# Set up the URL to the version of musl used by alpine at the time of writing.
|
||||
LATEST_MUSL_URL="http://more.musl.cc/10/x86_64-linux-musl/x86_64-linux-musl-native.tgz"
|
||||
# Set up the URL for the latest zlib version available at the time of writing.
|
||||
LATEST_ZLIB_URL="https://www.zlib.net/zlib-1.2.13.tar.gz"
|
||||
BUNDLE_DIR_NAME="bundle"
|
||||
DIR="$1"
|
||||
BUILD_DIR="$DIR/build/musl"
|
||||
|
||||
# Create the folder that will contain the finished bundle.
|
||||
mkdir -p $BUILD_DIR
|
||||
cd $BUILD_DIR
|
||||
|
||||
echo "Downloading musl library."
|
||||
wget "${LATEST_MUSL_URL}"
|
||||
echo "Downloading zlib library."
|
||||
wget "${LATEST_ZLIB_URL}"
|
||||
# Grab the names of the archives from the URL.
|
||||
MUSL_TAR="${LATEST_MUSL_URL##*/}"
|
||||
ZLIB_TAR="${LATEST_ZLIB_URL##*/}"
|
||||
|
||||
# Compile musl, compiling only the static musl libraries.
|
||||
echo "Extracting musl."
|
||||
tar xvzf "$MUSL_TAR"
|
||||
MUSL_DIR=$(tar tzf "${MUSL_TAR}" | cut -d'/' -f1 | uniq)
|
||||
TOOLCHAIN_DIR="${BUILD_DIR=}/${MUSL_DIR}"
|
||||
echo $TOOLCHAIN_DIR
|
||||
|
||||
# Compile zlib with the musl library we just built.
|
||||
export CC="${TOOLCHAIN_DIR}/bin/gcc"
|
||||
|
||||
echo "Extracting zlib."
|
||||
tar xvzf "${ZLIB_TAR}"
|
||||
ZLIB_DIR=$(tar tzf "${ZLIB_TAR}" | cut -d'/' -f1 | uniq)
|
||||
cd "${ZLIB_DIR}"
|
||||
echo "Configuring zlib."
|
||||
echo $TOOLCHAIN_DIR
|
||||
./configure --static --prefix="${TOOLCHAIN_DIR}"
|
||||
echo "Building zlib."
|
||||
make
|
||||
make install
|
7
cli/native-build-musl.sh
Executable file
7
cli/native-build-musl.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
DIR="$1"
|
||||
MUSL_DIR="$DIR/build/musl/x86_64-linux-musl-native"
|
||||
|
||||
export PATH="$PATH:$MUSL_DIR/bin"
|
||||
"$DIR/../gradlew" :cli:nativeCompile "-Dorg.gradle.jvmargs=-Xmx2048M"
|
|
@ -1,3 +1,3 @@
|
|||
CALL "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||
set JAVA_HOME=%GRAALVM_HOME%
|
||||
"%~dp0\..\gradlew.bat" :cli:nativeCompile
|
||||
"%~dp0\..\gradlew.bat" :cli:nativeCompile "-Dorg.gradle.jvmargs=-Xmx2048M"
|
|
@ -11,11 +11,9 @@ import java.util.function.Predicate;
|
|||
|
||||
public interface ShellProcessControl extends ProcessControl {
|
||||
|
||||
default String prepareTerminalOpen() throws Exception {
|
||||
return prepareTerminalOpen(null);
|
||||
}
|
||||
String prepareTerminalOpen() throws Exception;
|
||||
|
||||
String prepareTerminalOpen(String content) throws Exception;
|
||||
String prepareIntermediateTerminalOpen(String content) throws Exception;
|
||||
|
||||
default String executeStringSimpleCommand(String command) throws Exception {
|
||||
try (CommandProcessControl c = command(command).start()) {
|
||||
|
@ -35,7 +33,7 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
}
|
||||
}
|
||||
|
||||
default void executeSimpleCommand(String command,String failMessage) throws Exception {
|
||||
default void executeSimpleCommand(String command, String failMessage) throws Exception {
|
||||
try (CommandProcessControl c = command(command).start()) {
|
||||
c.discardOrThrow();
|
||||
} catch (ProcessOutputException out) {
|
||||
|
@ -60,12 +58,14 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
|
||||
ShellProcessControl elevation(SecretValue value);
|
||||
|
||||
ShellProcessControl initWith(List<String> cmds);
|
||||
|
||||
SecretValue getElevationPassword();
|
||||
|
||||
default ShellProcessControl subShell(@NonNull ShellType type) {
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -80,10 +80,9 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
|
||||
ShellProcessControl subShell(
|
||||
@NonNull Function<ShellProcessControl, String> command,
|
||||
BiFunction<ShellProcessControl, String, String> terminalCommand
|
||||
);
|
||||
BiFunction<ShellProcessControl, String, String> terminalCommand);
|
||||
|
||||
void executeCommand(String command) throws Exception;
|
||||
void executeLine(String command) throws Exception;
|
||||
|
||||
@Override
|
||||
ShellProcessControl start() throws Exception;
|
||||
|
@ -91,8 +90,7 @@ public interface ShellProcessControl extends ProcessControl {
|
|||
CommandProcessControl command(Function<ShellProcessControl, String> command);
|
||||
|
||||
CommandProcessControl command(
|
||||
Function<ShellProcessControl, String> command, Function<ShellProcessControl, String> terminalCommand
|
||||
);
|
||||
Function<ShellProcessControl, String> command, Function<ShellProcessControl, String> terminalCommand);
|
||||
|
||||
default CommandProcessControl command(String command) {
|
||||
return command(shellProcessControl -> command);
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.xpipe.core.process;
|
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import io.xpipe.core.charsetter.NewLine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -55,7 +54,7 @@ public interface ShellType {
|
|||
return getEchoCommand(s, false);
|
||||
}
|
||||
|
||||
String getSetVariableCommand(String variable, String value);
|
||||
String getSetEnvironmentVariableCommand(String variable, String value);
|
||||
|
||||
|
||||
String getEchoCommand(String s, boolean toErrorStream);
|
||||
|
@ -67,8 +66,14 @@ public interface ShellType {
|
|||
|
||||
String getPrintVariableCommand(String prefix, String name);
|
||||
|
||||
default String getPrintEnvironmentVariableCommand(String name) {
|
||||
return getPrintVariableCommand(name);
|
||||
}
|
||||
|
||||
String getNormalOpenCommand();
|
||||
|
||||
String getInitFileOpenCommand(String file);
|
||||
|
||||
String executeCommandWithShell(String cmd);
|
||||
|
||||
List<String> executeCommandListWithShell(String cmd);
|
||||
|
@ -79,7 +84,7 @@ public interface ShellType {
|
|||
|
||||
String getStreamFileWriteCommand(String file);
|
||||
|
||||
String getSimpleFileWriteCommand(String content, String file);
|
||||
String getTextFileWriteCommand(String content, String file);
|
||||
|
||||
String getFileDeleteCommand(String file);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.io.IOException;
|
|||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -57,7 +58,7 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getSetVariableCommand(String variableName, String value) {
|
||||
public String getSetEnvironmentVariableCommand(String variableName, String value) {
|
||||
return ("set \"" + variableName + "=" + value.replaceAll("\"", "^$0") + "\"");
|
||||
}
|
||||
|
||||
|
@ -68,13 +69,13 @@ public class ShellTypes {
|
|||
|
||||
@Override
|
||||
public String getEchoCommand(String s, boolean toErrorStream) {
|
||||
return "(echo " + s + (toErrorStream ? ")1>&2" : ")");
|
||||
return "(echo " + escapeStringValue(s).replaceAll("\r\n", "^\r\n") + (toErrorStream ? ")1>&2" : ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScriptEchoCommand(String s) {
|
||||
return ("@echo off\r\nset \"echov=" + escapeStringValue(s)
|
||||
+ "\"\r\necho %echov%\r\n@echo on\n(goto) 2>nul & del \"%~f0\"");
|
||||
return ("set \"echov=" + escapeStringValue(s)
|
||||
+ "\"\r\necho %echov%\r\n@echo on\r\n(goto) 2>nul & del \"%~f0\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -103,7 +104,7 @@ public class ShellTypes {
|
|||
|
||||
@Override
|
||||
public String prepareScriptContent(String content) {
|
||||
return "@echo off\n" + content;
|
||||
return "@echo off\r\n" + content;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,6 +126,11 @@ public class ShellTypes {
|
|||
return "cmd";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitFileOpenCommand(String file) {
|
||||
return "cmd /K \"" + file + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String executeCommandWithShell(String cmd) {
|
||||
return "cmd.exe /C " + cmd + "";
|
||||
|
@ -151,8 +157,15 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getSimpleFileWriteCommand(String content, String file) {
|
||||
return "echo " + content + " > \"" + file + "\"";
|
||||
public String getTextFileWriteCommand(String content, String file) {
|
||||
// if (true) return getEchoCommand(content, false) + " > \"" + file + "\"";
|
||||
|
||||
var command = new ArrayList<String>();
|
||||
for (String line : content.split("(\n|\r\n)")) {
|
||||
command.add("echo " + escapeStringValue(line) + ">> \"" + file + "\"");
|
||||
command.add("echo." + ">> \"" + file + "\"");
|
||||
}
|
||||
return String.join("&", command);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -219,7 +232,7 @@ public class ShellTypes {
|
|||
|
||||
@Override
|
||||
public void disableHistory(ShellProcessControl pc) throws Exception {
|
||||
pc.executeCommand("Set-PSReadLineOption -HistorySaveStyle SaveNothing");
|
||||
pc.executeLine("Set-PSReadLineOption -HistorySaveStyle SaveNothing");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -233,8 +246,8 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getSimpleFileWriteCommand(String content, String file) {
|
||||
return "echo \"" + content + "\" | Out-File \"" + file + "\"";
|
||||
public String getTextFileWriteCommand(String content, String file) {
|
||||
return "echo \"" + content.replaceAll("(\n|\r\n)", "`n") + "\" | Out-File \"" + file + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -258,7 +271,12 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getSetVariableCommand(String variableName, String value) {
|
||||
public String getPrintEnvironmentVariableCommand(String name) {
|
||||
return "echo \"" + "$env:" + escapeStringValue(name) + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSetEnvironmentVariableCommand(String variableName, String value) {
|
||||
return "$env:" + variableName + " = \"" + escapeStringValue(value) + "\"";
|
||||
}
|
||||
|
||||
|
@ -320,6 +338,11 @@ public class ShellTypes {
|
|||
return "powershell /nologo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitFileOpenCommand(String file) {
|
||||
return "powershell.exe -NoExit -File \"" + file + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String executeCommandWithShell(String cmd) {
|
||||
return "powershell.exe -Command '" + cmd + "'";
|
||||
|
@ -394,8 +417,13 @@ public class ShellTypes {
|
|||
public abstract static class PosixBase implements ShellType {
|
||||
|
||||
@Override
|
||||
public String getSimpleFileWriteCommand(String content, String file) {
|
||||
return "echo \"" + content + "\" > \"" + file + "\"";
|
||||
public String getInitFileOpenCommand(String file) {
|
||||
return getName() + " --rcfile \"" + file + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextFileWriteCommand(String content, String file) {
|
||||
return "echo -e '" + content.replaceAll("\n", "\\\\n").replaceAll("'","\\\\'") + "' > \"" + file + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -461,7 +489,7 @@ public class ShellTypes {
|
|||
|
||||
@Override
|
||||
public void disableHistory(ShellProcessControl pc) throws Exception {
|
||||
pc.executeCommand("unset HISTFILE");
|
||||
pc.executeLine("unset HISTFILE");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -485,7 +513,7 @@ public class ShellTypes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getSetVariableCommand(String variable, String value) {
|
||||
public String getSetEnvironmentVariableCommand(String variable, String value) {
|
||||
return "export " + variable + "=\"" + value + "\"";
|
||||
}
|
||||
|
||||
|
|
|
@ -79,9 +79,9 @@ public class XPipeInstallation {
|
|||
public static Path getLocalDynamicLibraryDirectory() {
|
||||
Path path = getLocalInstallationBasePath();
|
||||
if (OsType.getLocal().equals(OsType.WINDOWS)) {
|
||||
return path.resolve("runtime").resolve("bin");
|
||||
return path.resolve("app").resolve("runtime").resolve("bin");
|
||||
} else if (OsType.getLocal().equals(OsType.LINUX)) {
|
||||
return path.resolve("lib").resolve("runtime").resolve("lib");
|
||||
return path.resolve("app").resolve("lib").resolve("runtime").resolve("lib");
|
||||
} else {
|
||||
return path.resolve("Contents")
|
||||
.resolve("runtime")
|
||||
|
@ -95,7 +95,7 @@ public class XPipeInstallation {
|
|||
Path path = getLocalInstallationBasePath();
|
||||
return OsType.getLocal().equals(OsType.MAC)
|
||||
? path.resolve("Contents").resolve("Resources").resolve("extensions")
|
||||
: path.resolve("extensions");
|
||||
: path.resolve("app").resolve("extensions");
|
||||
}
|
||||
|
||||
private static Path getLocalInstallationBasePathForJavaExecutable(Path executable) {
|
||||
|
@ -108,9 +108,9 @@ public class XPipeInstallation {
|
|||
.getParent()
|
||||
.getParent();
|
||||
} else if (OsType.getLocal().equals(OsType.LINUX)) {
|
||||
return executable.getParent().getParent().getParent().getParent();
|
||||
return executable.getParent().getParent().getParent().getParent().getParent();
|
||||
} else {
|
||||
return executable.getParent().getParent().getParent();
|
||||
return executable.getParent().getParent().getParent().getParent();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,9 +118,9 @@ public class XPipeInstallation {
|
|||
if (OsType.getLocal().equals(OsType.MAC)) {
|
||||
return executable.getParent().getParent().getParent();
|
||||
} else if (OsType.getLocal().equals(OsType.LINUX)) {
|
||||
return executable.getParent().getParent();
|
||||
return executable.getParent().getParent().getParent();
|
||||
} else {
|
||||
return executable.getParent();
|
||||
return executable.getParent().getParent();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
24
dist/cli.gradle
vendored
24
dist/cli.gradle
vendored
|
@ -3,8 +3,6 @@ import java.nio.file.Paths
|
|||
import java.nio.file.StandardCopyOption
|
||||
|
||||
def distDir = "$buildDir/dist"
|
||||
def windows = org.gradle.internal.os.OperatingSystem.current().isWindows();
|
||||
|
||||
|
||||
apply plugin: 'org.asciidoctor.jvm.convert'
|
||||
asciidoctor {
|
||||
|
@ -29,21 +27,37 @@ task copyCompletion(type: Copy) {
|
|||
into "$distDir/cli"
|
||||
}
|
||||
|
||||
task setupMusl(type: Exec) {
|
||||
commandLine "${project(':cli').projectDir.toString()}/musl-setup.sh", project(':cli').projectDir.toString()
|
||||
}
|
||||
|
||||
task buildCli(type: DefaultTask) {
|
||||
if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) {
|
||||
if (org.gradle.internal.os.OperatingSystem.current().isLinux()) {
|
||||
dependsOn(setupMusl)
|
||||
project(':cli').getTasksByName('nativeCompile', true).forEach(v->v.mustRunAfter(setupMusl))
|
||||
}
|
||||
|
||||
if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) {
|
||||
dependsOn(project(':cli').getTasksByName('nativeCompile', true))
|
||||
}
|
||||
|
||||
doLast {
|
||||
if (windows) {
|
||||
if (rootProject.os.isWindows()) {
|
||||
exec {
|
||||
executable project(':cli').projectDir.toString() + '/native-build.bat'
|
||||
environment System.getenv()
|
||||
}
|
||||
}
|
||||
|
||||
if (rootProject.os.isLinux()) {
|
||||
exec {
|
||||
commandLine project(':cli').projectDir.toString() + '/native-build-musl.sh', project(':cli').projectDir.toString()
|
||||
environment System.getenv()
|
||||
}
|
||||
}
|
||||
|
||||
Files.createDirectories(Paths.get(distDir, 'cli'))
|
||||
def ending = windows ? ".exe" : ""
|
||||
def ending = rootProject.os.isWindows() ? ".exe" : ""
|
||||
var outputFile = Paths.get(project(':cli').buildDir.toString() + "/native/nativeCompile/xpipe$ending")
|
||||
if (!Files.exists(outputFile)) {
|
||||
throw new IOException("Cli output file does not exist")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.ext.jdbc;
|
||||
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.util.Proxyable;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
@ -26,6 +27,8 @@ public interface JdbcBaseStore extends JdbcStore, Proxyable {
|
|||
|
||||
Map<String, String> createProperties();
|
||||
|
||||
ShellStore getProxy();
|
||||
|
||||
default Map<String, Object> createDefaultProperties() {
|
||||
return Map.of();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.ext.jdbc;
|
||||
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.util.ProxyProvider;
|
||||
import io.xpipe.extension.DataStoreProvider;
|
||||
import io.xpipe.extension.util.DataStoreFormatter;
|
||||
|
@ -20,6 +21,13 @@ public abstract class JdbcStoreProvider implements DataStoreProvider {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore getParent(DataStore store) {
|
||||
JdbcBaseStore s = store.asNeeded();
|
||||
return !ShellStore.isLocal(s.getProxy()) ? s.getProxy() : null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean init() throws Exception {
|
||||
try {
|
||||
|
|
|
@ -60,7 +60,7 @@ public class PostgresPsqlAction implements DataStoreActionProvider<PostgresSimpl
|
|||
var t = pc.getShellType();
|
||||
String passwordPrefix = "";
|
||||
if (store.getAuth() instanceof SimpleAuthMethod p && p.getPassword() != null) {
|
||||
var passwordCommand = t.getSetVariableCommand(
|
||||
var passwordCommand = t.getSetEnvironmentVariableCommand(
|
||||
"PGPASSWORD",
|
||||
p.getPassword().getSecretValue());
|
||||
passwordPrefix = passwordCommand + "\n";
|
||||
|
|
|
@ -90,7 +90,7 @@ public class CommandProcessControlImpl extends ProcessControlImpl implements Com
|
|||
: terminalCommand.apply(parent))
|
||||
+ operator
|
||||
+ parent.getShellType().getPauseCommand();
|
||||
return parent.prepareTerminalOpen(consoleCommand);
|
||||
return parent.prepareIntermediateTerminalOpen(consoleCommand);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,8 +150,8 @@ public class CommandProcessControlImpl extends ProcessControlImpl implements Com
|
|||
parent.start();
|
||||
|
||||
var baseCommand = command.apply(parent);
|
||||
this.complex = complex || baseCommand.contains("\n");
|
||||
if (complex) {
|
||||
var createExecScript = complex || baseCommand.contains("\n");
|
||||
if (createExecScript) {
|
||||
var script = ScriptHelper.createExecScript(parent, baseCommand, true);
|
||||
baseCommand = "\"" + script + "\"";
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ public class CommandProcessControlImpl extends ProcessControlImpl implements Com
|
|||
if (elevated) {
|
||||
string = ElevationHelper.elevateNormalCommand(string, parent, baseCommand);
|
||||
}
|
||||
parent.executeCommand(string);
|
||||
parent.executeLine(string);
|
||||
running = true;
|
||||
|
||||
// Check for custom charset
|
||||
|
|
|
@ -26,6 +26,12 @@ import java.util.List;
|
|||
|
||||
public class CommandStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public DataStore getParent(DataStore store) {
|
||||
CommandStore s = store.asNeeded();
|
||||
return s.getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShareable() {
|
||||
return true;
|
||||
|
|
|
@ -20,6 +20,12 @@ import java.util.List;
|
|||
|
||||
public class DockerStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public DataStore getParent(DataStore store) {
|
||||
DockerStore s = store.asNeeded();
|
||||
return s.getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShareable() {
|
||||
return true;
|
||||
|
|
|
@ -27,6 +27,11 @@ public class LocalProcessControlImpl extends ShellProcessControlImpl {
|
|||
protected boolean stdinClosed;
|
||||
private Process process;
|
||||
|
||||
@Override
|
||||
public String prepareTerminalOpen() throws Exception {
|
||||
return prepareIntermediateTerminalOpen(null);
|
||||
}
|
||||
|
||||
public void closeStdin() throws IOException {
|
||||
if (stdinClosed) {
|
||||
return;
|
||||
|
@ -99,15 +104,10 @@ public class LocalProcessControlImpl extends ShellProcessControlImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String prepareTerminalOpen(String content) throws Exception {
|
||||
try (var ignored = start()) {
|
||||
if (content == null) {
|
||||
return getShellType().getNormalOpenCommand();
|
||||
}
|
||||
|
||||
var file = ScriptHelper.createExecScript(this, content, false);
|
||||
|
||||
TrackEvent.withTrace("proc", "Writing open init script")
|
||||
public String prepareIntermediateTerminalOpen(String content) throws Exception {
|
||||
try (var pc = start()) {
|
||||
var file = ScriptHelper.constructOpenWithInitScriptCommand(pc, initCommands, content);
|
||||
TrackEvent.withDebug("proc", "Writing open init script")
|
||||
.tag("file", file)
|
||||
.tag("content", content)
|
||||
.handle();
|
||||
|
@ -150,6 +150,10 @@ public class LocalProcessControlImpl extends ShellProcessControlImpl {
|
|||
charset = shellType.determineCharset(this);
|
||||
osType = OsType.getLocal();
|
||||
|
||||
for (String s : initCommands) {
|
||||
executeLine(s);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import io.xpipe.extension.prefs.PrefsChoiceValue;
|
|||
import io.xpipe.extension.prefs.PrefsHandler;
|
||||
import io.xpipe.extension.prefs.PrefsProvider;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
|
@ -17,11 +18,18 @@ import java.util.List;
|
|||
|
||||
public class ProcPrefs extends PrefsProvider {
|
||||
|
||||
private final BooleanProperty enableCaching = new SimpleBooleanProperty(true);
|
||||
|
||||
public ObservableBooleanValue enableCaching() {
|
||||
return enableCaching;
|
||||
}
|
||||
|
||||
private final ObjectProperty<TerminalType> terminalType = new SimpleObjectProperty<>(TerminalType.getDefault());
|
||||
private final SimpleListProperty<TerminalType> terminalTypeList = new SimpleListProperty<>(
|
||||
FXCollections.observableArrayList(PrefsChoiceValue.getSupported(TerminalType.class)));
|
||||
private final SingleSelectionField<TerminalType> terminalTypeControl =
|
||||
Field.ofSingleSelectionType(terminalTypeList, terminalType).render(() -> new TranslatableComboBoxControl<>());
|
||||
|
||||
// Custom terminal
|
||||
// ===============
|
||||
private final StringProperty customTerminalCommand = new SimpleStringProperty("");
|
||||
|
|
|
@ -21,6 +21,12 @@ import java.util.List;
|
|||
|
||||
public class ShellCommandStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public DataStore getParent(DataStore store) {
|
||||
ShellCommandStore s = store.asNeeded();
|
||||
return s.getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShareable() {
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package io.xpipe.ext.proc;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.core.process.ShellProcessControl;
|
||||
import io.xpipe.core.process.ShellType;
|
||||
import io.xpipe.core.store.MachineStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import io.xpipe.extension.util.Validators;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@SuperBuilder
|
||||
@Getter
|
||||
@Jacksonized
|
||||
@JsonTypeName("shellEnvironment")
|
||||
public class ShellEnvironmentStore extends JacksonizedValue implements MachineStore {
|
||||
|
||||
private final String commands;
|
||||
private final ShellStore host;
|
||||
private final ShellType shell;
|
||||
|
||||
|
||||
public ShellEnvironmentStore(String commands, ShellStore host, ShellType shell) {
|
||||
this.commands = commands;
|
||||
this.host = host;
|
||||
this.shell = shell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws Exception {
|
||||
Validators.nonNull(commands, "Commands");
|
||||
Validators.nonNull(host, "Host");
|
||||
Validators.namedStoreExists(host, "Host");
|
||||
host.checkComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShellProcessControl create() {
|
||||
var pc = host.create();
|
||||
if (shell != null) {
|
||||
pc = pc.subShell(shell);
|
||||
}
|
||||
return pc.initWith(commands.lines().toList());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package io.xpipe.ext.proc;
|
||||
|
||||
import io.xpipe.core.impl.LocalStore;
|
||||
import io.xpipe.core.process.ShellType;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.util.Identifiers;
|
||||
import io.xpipe.extension.DataStoreProvider;
|
||||
import io.xpipe.extension.GuiDialog;
|
||||
import io.xpipe.extension.I18n;
|
||||
import io.xpipe.extension.fxcomps.impl.ShellStoreChoiceComp;
|
||||
import io.xpipe.extension.util.DynamicOptionsBuilder;
|
||||
import io.xpipe.extension.util.SimpleValidator;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ShellEnvironmentStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public boolean isShareable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "shellEnvironment";
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(Property<DataStore> store) {
|
||||
var val = new SimpleValidator();
|
||||
ShellEnvironmentStore st = store.getValue().asNeeded();
|
||||
|
||||
Property<ShellStore> hostProperty = new SimpleObjectProperty<>(st.getHost());
|
||||
Property<String> commandProp = new SimpleObjectProperty<>(st.getCommands());
|
||||
Property<ShellType> shellTypeProperty = new SimpleObjectProperty<>(st.getShell());
|
||||
|
||||
var q = new DynamicOptionsBuilder(I18n.observable("configuration"))
|
||||
.addComp(I18n.observable("host"), ShellStoreChoiceComp.host(st, hostProperty), hostProperty)
|
||||
.nonNull(val)
|
||||
.addComp(
|
||||
I18n.observable("proc.shellType"),
|
||||
new ShellTypeChoiceComp(shellTypeProperty),
|
||||
shellTypeProperty)
|
||||
.addStringArea("proc.commands", commandProp, false)
|
||||
.nonNull(val)
|
||||
.bind(
|
||||
() -> {
|
||||
return new ShellEnvironmentStore(
|
||||
commandProp.getValue(), hostProperty.getValue(), shellTypeProperty.getValue());
|
||||
},
|
||||
store)
|
||||
.buildComp();
|
||||
return new GuiDialog(q, val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String queryInformationString(DataStore store, int length) throws Exception {
|
||||
ShellEnvironmentStore s = store.asNeeded();
|
||||
var name = s.getShell() != null ? s.getShell().getDisplayName() : "Default";
|
||||
return I18n.get("shellEnvironment.informationFormat", name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSummaryString(DataStore store, int length) {
|
||||
ShellEnvironmentStore s = store.asNeeded();
|
||||
var local = ShellStore.isLocal(s.getHost());
|
||||
var commandSummary = "<" + s.getCommands().lines().count() + " commands>";
|
||||
return commandSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore getParent(DataStore store) {
|
||||
ShellEnvironmentStore s = store.asNeeded();
|
||||
return s.getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Category getCategory() {
|
||||
return Category.SHELL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getStoreClasses() {
|
||||
return List.of(ShellEnvironmentStore.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore defaultStore() {
|
||||
return new ShellEnvironmentStore(null, new LocalStore(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPossibleNames() {
|
||||
return Identifiers.get("shell", "environment");
|
||||
}
|
||||
}
|
|
@ -1,12 +1,17 @@
|
|||
package io.xpipe.ext.proc;
|
||||
|
||||
import io.xpipe.core.process.*;
|
||||
import io.xpipe.core.process.CommandProcessControl;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.process.ShellProcessControl;
|
||||
import io.xpipe.core.process.ShellType;
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
import io.xpipe.ext.proc.util.ShellReader;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
@ -16,6 +21,7 @@ public abstract class ShellProcessControlImpl extends ProcessControlImpl impleme
|
|||
protected Integer startTimeout = 10000;
|
||||
protected UUID uuid;
|
||||
protected String command;
|
||||
protected List<String> initCommands = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
protected ShellType shellType;
|
||||
|
@ -38,6 +44,12 @@ public abstract class ShellProcessControlImpl extends ProcessControlImpl impleme
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShellProcessControl initWith(List<String> cmds) {
|
||||
this.initCommands.addAll(cmds);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShellProcessControl subShell(
|
||||
@NonNull Function<ShellProcessControl, String> command,
|
||||
|
@ -57,7 +69,7 @@ public abstract class ShellProcessControlImpl extends ProcessControlImpl impleme
|
|||
}
|
||||
|
||||
@Override
|
||||
public void executeCommand(String command) throws Exception {
|
||||
public void executeLine(String command) throws Exception {
|
||||
writeLine(command);
|
||||
if (getShellType().doesRepeatInput()) {
|
||||
ShellReader.readLine(getStdout(), getCharset());
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.xpipe.ext.proc.augment.SshCommandAugmentation;
|
|||
import io.xpipe.ext.proc.util.ShellHelper;
|
||||
import io.xpipe.ext.proc.util.SshToolHelper;
|
||||
import io.xpipe.extension.event.TrackEvent;
|
||||
import io.xpipe.extension.prefs.PrefsProvider;
|
||||
import io.xpipe.extension.util.ScriptHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -30,6 +31,11 @@ public class SshProcessControlImpl extends ShellProcessControlImpl {
|
|||
this.elevationPassword = store.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prepareTerminalOpen() throws Exception {
|
||||
return prepareIntermediateTerminalOpen(null);
|
||||
}
|
||||
|
||||
public void closeStdin() throws IOException {
|
||||
if (stdinClosed) {
|
||||
return;
|
||||
|
@ -61,7 +67,7 @@ public class SshProcessControlImpl extends ShellProcessControlImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String prepareTerminalOpen(String content) throws Exception {
|
||||
public String prepareIntermediateTerminalOpen(String content) throws Exception {
|
||||
String script = null;
|
||||
if (content != null) {
|
||||
try (var pc = start()) {
|
||||
|
@ -81,7 +87,7 @@ public class SshProcessControlImpl extends ShellProcessControlImpl {
|
|||
var operator = pc.getShellType().getOrConcatenationOperator();
|
||||
var consoleCommand = passwordCommand + operator + pc.getShellType().getPauseCommand();
|
||||
|
||||
return pc.prepareTerminalOpen(consoleCommand);
|
||||
return pc.prepareIntermediateTerminalOpen(consoleCommand);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,10 +121,6 @@ public class SshProcessControlImpl extends ShellProcessControlImpl {
|
|||
return false;
|
||||
}
|
||||
|
||||
private int getConnectionHash() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShellProcessControl elevated(Predicate<ShellProcessControl> elevationFunction) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -157,27 +159,20 @@ public class SshProcessControlImpl extends ShellProcessControlImpl {
|
|||
.tag("charset", charset.name())
|
||||
.handle();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeCommand(String command) throws Exception {
|
||||
TrackEvent.withTrace("ssh", "Executing ssh command")
|
||||
.tag("command", command)
|
||||
.tag("previousChannelActive", channel != null)
|
||||
.handle();
|
||||
|
||||
if (channel != null) {
|
||||
channel.disconnect();
|
||||
}
|
||||
|
||||
channel = session.openChannel("exec");
|
||||
((ChannelExec) channel).setCommand(command);
|
||||
((ChannelExec) channel).setCommand(getShellType().getNormalOpenCommand());
|
||||
((ChannelExec) channel).setPty(false);
|
||||
stdout = channel.getInputStream();
|
||||
stderr = ((ChannelExec) channel).getErrStream();
|
||||
stdin = channel.getOutputStream();
|
||||
channel.connect();
|
||||
|
||||
// Execute optional init commands
|
||||
for (String s : initCommands) {
|
||||
executeLine(s);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -197,9 +192,11 @@ public class SshProcessControlImpl extends ShellProcessControlImpl {
|
|||
running = false;
|
||||
stdinClosed = true;
|
||||
|
||||
shellType = null;
|
||||
charset = null;
|
||||
osType = null;
|
||||
if (!PrefsProvider.get(ProcPrefs.class).enableCaching().get()) {
|
||||
shellType = null;
|
||||
charset = null;
|
||||
command = null;
|
||||
}
|
||||
|
||||
getStderr().close();
|
||||
getStdin().close();
|
||||
|
|
|
@ -6,6 +6,8 @@ import io.xpipe.core.store.MachineStore;
|
|||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
import io.xpipe.ext.proc.augment.SshCommandAugmentation;
|
||||
import io.xpipe.ext.proc.util.SshToolHelper;
|
||||
import io.xpipe.extension.util.Validators;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
|
@ -14,8 +16,6 @@ import lombok.experimental.FieldDefaults;
|
|||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@SuperBuilder
|
||||
@Jacksonized
|
||||
@Getter
|
||||
|
@ -50,7 +50,41 @@ public class SshStore extends JacksonizedValue implements MachineStore {
|
|||
|
||||
@Override
|
||||
public ShellProcessControl create() {
|
||||
return new SshProcessControlImpl(this);
|
||||
if (ShellStore.isLocal(proxy)) {
|
||||
return new SshProcessControlImpl(this);
|
||||
}
|
||||
|
||||
return proxy.create()
|
||||
.subShell(
|
||||
shellProcessControl -> {
|
||||
var command = SshToolHelper.toCommand(this, shellProcessControl);
|
||||
var augmentedCommand = new SshCommandAugmentation().prepareNonTerminalCommand(shellProcessControl, command);
|
||||
var passwordCommand = SshToolHelper.passPassword(
|
||||
augmentedCommand,
|
||||
getPassword(),
|
||||
getKey() != null ? getKey().getPassword() : null,
|
||||
shellProcessControl);
|
||||
return passwordCommand;
|
||||
},
|
||||
(shellProcessControl, s) -> {
|
||||
var command = SshToolHelper.toCommand(this, shellProcessControl);
|
||||
var augmentedCommand = new SshCommandAugmentation().prepareTerminalCommand(shellProcessControl, command, s);
|
||||
var passwordCommand = SshToolHelper.passPassword(
|
||||
augmentedCommand,
|
||||
getPassword(),
|
||||
getKey() != null ? getKey().getPassword() : null,
|
||||
shellProcessControl);
|
||||
|
||||
if (true) return passwordCommand;
|
||||
var operator = shellProcessControl.getShellType().getOrConcatenationOperator();
|
||||
var consoleCommand = passwordCommand + operator + shellProcessControl.getShellType().getPauseCommand();
|
||||
try {
|
||||
return shellProcessControl.prepareIntermediateTerminalOpen(consoleCommand);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.elevated(shellProcessControl -> true);
|
||||
}
|
||||
|
||||
@SuperBuilder
|
||||
|
@ -58,7 +92,7 @@ public class SshStore extends JacksonizedValue implements MachineStore {
|
|||
@Getter
|
||||
public static class SshKey {
|
||||
@NonNull
|
||||
Path file;
|
||||
String file;
|
||||
|
||||
SecretValue password;
|
||||
}
|
||||
|
|
|
@ -22,11 +22,16 @@ import javafx.beans.property.Property;
|
|||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class SshStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public DataStore getParent(DataStore store) {
|
||||
SshStore s = store.asNeeded();
|
||||
return !ShellStore.isLocal(s.getProxy()) ? s.getProxy() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShareable() {
|
||||
return true;
|
||||
|
@ -89,10 +94,9 @@ public class SshStoreProvider implements DataStoreProvider {
|
|||
() -> {
|
||||
return keyFileProperty.get() != null
|
||||
? SshStore.SshKey.builder()
|
||||
.file(Path.of(keyFileProperty
|
||||
.file(keyFileProperty
|
||||
.get()
|
||||
.getFile()
|
||||
.toString()))
|
||||
.getFile())
|
||||
.password(keyPasswordProperty.get())
|
||||
.build()
|
||||
: null;
|
||||
|
|
|
@ -5,6 +5,7 @@ import io.xpipe.ext.proc.util.ElevationHelper;
|
|||
import io.xpipe.ext.proc.util.ShellHelper;
|
||||
import io.xpipe.ext.proc.util.ShellReader;
|
||||
import io.xpipe.extension.event.TrackEvent;
|
||||
import io.xpipe.extension.prefs.PrefsProvider;
|
||||
import io.xpipe.extension.util.ScriptHelper;
|
||||
import lombok.NonNull;
|
||||
|
||||
|
@ -45,50 +46,60 @@ public class SubShellProcessControlImpl extends ShellProcessControlImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String prepareTerminalOpen(String content) throws Exception {
|
||||
public String prepareTerminalOpen() throws Exception {
|
||||
if (isRunning()) {
|
||||
exitAndWait();
|
||||
}
|
||||
|
||||
try (var parentPc = parent.start()) {
|
||||
var operator = parent.getShellType().getOrConcatenationOperator();
|
||||
var consoleCommand = this.terminalCommand.apply(parent, null);
|
||||
var elevated = elevationFunction.test(parent);
|
||||
if (elevated) {
|
||||
consoleCommand = ElevationHelper.elevateTerminalCommand(consoleCommand, parent);
|
||||
}
|
||||
|
||||
var openCommand = "";
|
||||
try (var pc = start()) {
|
||||
var initCommand = ScriptHelper.constructOpenWithInitScriptCommand(pc, initCommands, null);
|
||||
openCommand = consoleCommand + " " + initCommand + operator
|
||||
+ parent.getShellType().getPauseCommand();
|
||||
TrackEvent.withDebug("proc", "Preparing for terminal open")
|
||||
.tag("initCommand", initCommand)
|
||||
.tag("openCommand", openCommand)
|
||||
.handle();
|
||||
}
|
||||
return parent.prepareIntermediateTerminalOpen(openCommand);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prepareIntermediateTerminalOpen(String content) throws Exception {
|
||||
if (this.terminalCommand == null) {
|
||||
throw new UnsupportedOperationException("Terminal open not supported");
|
||||
}
|
||||
|
||||
if (content == null) {
|
||||
if (isRunning()) {
|
||||
exitAndWait();
|
||||
try (var pc = start()) {
|
||||
var operator = parent.getShellType().getOrConcatenationOperator();
|
||||
var file = ScriptHelper.createExecScript(this, content, false);
|
||||
|
||||
var terminalCommand = this.terminalCommand.apply(parent, file);
|
||||
var elevated = elevationFunction.test(parent);
|
||||
if (elevated) {
|
||||
terminalCommand = ElevationHelper.elevateTerminalCommand(terminalCommand, parent);
|
||||
}
|
||||
|
||||
try (var ignored = parent.start()) {
|
||||
var operator = parent.getShellType().getOrConcatenationOperator();
|
||||
var consoleCommand = this.terminalCommand.apply(parent, null);
|
||||
var elevated = elevationFunction.test(parent);
|
||||
if (elevated) {
|
||||
consoleCommand = ElevationHelper.elevateTerminalCommand(consoleCommand, parent);
|
||||
}
|
||||
var openCommand =
|
||||
consoleCommand + operator + parent.getShellType().getPauseCommand();
|
||||
return parent.prepareTerminalOpen(openCommand);
|
||||
}
|
||||
} else {
|
||||
try (var ignored = start()) {
|
||||
var operator = parent.getShellType().getOrConcatenationOperator();
|
||||
var file = ScriptHelper.createExecScript(this, content, false);
|
||||
var initCommand = ScriptHelper.constructOpenWithInitScriptCommand(pc, initCommands, terminalCommand);
|
||||
var openCommand = initCommand + operator + parent.getShellType().getPauseCommand();
|
||||
TrackEvent.withDebug("proc", "Preparing for terminal open")
|
||||
.tag("file", file)
|
||||
.tag("content", ShellHelper.censor(content, sensitive))
|
||||
.tag("openCommand", openCommand)
|
||||
.tag("initCommand", initCommand)
|
||||
.handle();
|
||||
|
||||
var consoleCommand = this.terminalCommand.apply(parent, file);
|
||||
var elevated = elevationFunction.test(parent);
|
||||
if (elevated) {
|
||||
consoleCommand = ElevationHelper.elevateTerminalCommand(consoleCommand, parent);
|
||||
}
|
||||
var openCommand =
|
||||
consoleCommand + operator + parent.getShellType().getPauseCommand();
|
||||
|
||||
TrackEvent.withTrace("proc", "Preparing for console open")
|
||||
.tag("file", file)
|
||||
.tag("content", ShellHelper.censor(content, sensitive))
|
||||
.tag("openCommand", openCommand)
|
||||
.handle();
|
||||
|
||||
exitAndWait();
|
||||
parent.restart();
|
||||
return parent.prepareTerminalOpen(openCommand);
|
||||
}
|
||||
exitAndWait();
|
||||
return parent.prepareIntermediateTerminalOpen(openCommand);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +170,7 @@ public class SubShellProcessControlImpl extends ShellProcessControlImpl {
|
|||
if (elevated) {
|
||||
commandToExecute = ElevationHelper.elevateNormalCommand(commandToExecute, parent, command);
|
||||
}
|
||||
parent.executeCommand(commandToExecute);
|
||||
parent.executeLine(commandToExecute);
|
||||
|
||||
// Wait for prefix output
|
||||
// In case this fails, we know that the whole command has not been executed, which can only happen if the syntax ever occurred
|
||||
|
@ -170,20 +181,31 @@ public class SubShellProcessControlImpl extends ShellProcessControlImpl {
|
|||
}
|
||||
|
||||
running = true;
|
||||
shellType =
|
||||
ShellHelper.determineType(this, parent.getCharset(), commandToExecute, uuid.toString(), startTimeout);
|
||||
if (shellType == null) {
|
||||
shellType = ShellHelper.determineType(
|
||||
this, parent.getCharset(), commandToExecute, uuid.toString(), startTimeout);
|
||||
}
|
||||
shellType.disableHistory(this);
|
||||
charset = shellType.determineCharset(this);
|
||||
osType = ShellHelper.determineOsType(this);
|
||||
if (charset == null) {
|
||||
charset = shellType.determineCharset(this);
|
||||
}
|
||||
if (osType == null) {
|
||||
osType = ShellHelper.determineOsType(this);
|
||||
}
|
||||
|
||||
TrackEvent.withTrace("proc", "Detected shell environment...")
|
||||
TrackEvent.withDebug("proc", "Detected shell environment...")
|
||||
.tag("shellType", shellType.getName())
|
||||
.tag("charset", charset.name())
|
||||
.tag("osType", osType.getName())
|
||||
.handle();
|
||||
|
||||
// Execute optional init commands
|
||||
for (String s : initCommands) {
|
||||
executeLine(s);
|
||||
}
|
||||
|
||||
// Read all output until now
|
||||
executeCommand(getShellType().getEchoCommand(uuid.toString(), false)
|
||||
executeLine(getShellType().getEchoCommand(uuid.toString(), false)
|
||||
+ getShellType().getConcatenationOperator()
|
||||
+ getShellType().getEchoCommand(uuid.toString(), true));
|
||||
ShellReader.readUntilOccurrence(getStdout(), getCharset(), uuid.toString(), commandToExecute, false);
|
||||
|
@ -251,9 +273,11 @@ public class SubShellProcessControlImpl extends ShellProcessControlImpl {
|
|||
running = false;
|
||||
}
|
||||
|
||||
shellType = null;
|
||||
charset = null;
|
||||
if (!PrefsProvider.get(ProcPrefs.class).enableCaching().get()) {
|
||||
shellType = null;
|
||||
charset = null;
|
||||
command = null;
|
||||
}
|
||||
uuid = null;
|
||||
command = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,12 @@ import java.util.List;
|
|||
|
||||
public class WslStoreProvider implements DataStoreProvider {
|
||||
|
||||
@Override
|
||||
public DataStore getParent(DataStore store) {
|
||||
WslStore s = store.asNeeded();
|
||||
return s.getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(Property<DataStore> store) {
|
||||
var val = new SimpleValidator();
|
||||
|
|
|
@ -5,7 +5,7 @@ import java.util.List;
|
|||
public class PosixShellCommandAugmentation extends CommandAugmentation {
|
||||
@Override
|
||||
public boolean matches(String executable) {
|
||||
return executable.equals("sh") || executable.equals("bash");
|
||||
return executable.equals("sh") || executable.equals("bash") || executable.equals("zsh");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,7 +22,7 @@ public class SshToolHelper {
|
|||
store.getPort().toString()));
|
||||
if (store.getKey() != null) {
|
||||
list.add("-i");
|
||||
list.add(store.getKey().getFile().toString());
|
||||
list.add(store.getKey().getFile());
|
||||
}
|
||||
return parent.getShellType().flatten(list);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ open module io.xpipe.ext.proc {
|
|||
LaunchCommandAction;
|
||||
provides DataStoreProvider with
|
||||
SshStoreProvider,
|
||||
ShellEnvironmentStoreProvider,
|
||||
CommandStoreProvider,
|
||||
DockerStoreProvider,
|
||||
ShellCommandStoreProvider,
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
|
@ -8,6 +8,9 @@ inMemory.displayName=In Memory
|
|||
inMemory.displayDescription=Store binary data in memory
|
||||
shellCommand.displayName=Shell Command
|
||||
shellCommand.displayDescription=Open a shell with a custom command
|
||||
shellEnvironment.displayName=Shell Environment
|
||||
shellEnvironment.displayDescription=Run custom init commands on the shell connection
|
||||
shellEnvironment.informationFormat=$TYPE$ environment
|
||||
ssh.displayName=SSH Connection
|
||||
ssh.displayDescription=Connect via SSH
|
||||
binary.displayName=Binary
|
||||
|
@ -28,6 +31,7 @@ configuration=Configuration
|
|||
selectOutput=Select Output
|
||||
options=Options
|
||||
requiresElevation=Run Elevated
|
||||
commands=Commands
|
||||
selectStore=Select Store
|
||||
selectSource=Select Source
|
||||
commandLineRead=Read
|
||||
|
|
|
@ -7,6 +7,9 @@ import lombok.Getter;
|
|||
import org.apache.commons.lang3.function.FailableConsumer;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public enum ShellCheckTestItem {
|
||||
OS_NAME(shellProcessControl -> {
|
||||
|
@ -22,9 +25,21 @@ public enum ShellCheckTestItem {
|
|||
Assertions.assertEquals("world", s2);
|
||||
}),
|
||||
|
||||
INIT_FILE(shellProcessControl -> {
|
||||
var content = "<contentß>";
|
||||
try (var c = shellProcessControl
|
||||
.subShell(shellProcessControl.getShellType())
|
||||
.initWith(List.of(shellProcessControl.getShellType().getSetEnvironmentVariableCommand("testVar", content)))
|
||||
.start()) {
|
||||
var output = c.executeStringSimpleCommand(
|
||||
shellProcessControl.getShellType().getPrintEnvironmentVariableCommand("testVar"));
|
||||
Assertions.assertEquals(content, output);
|
||||
}
|
||||
}),
|
||||
|
||||
STREAM_WRITE(shellProcessControl -> {
|
||||
var content = "hello\nworldß";
|
||||
var fileOne = FileNames.join(shellProcessControl.getOsType().getTempDirectory(shellProcessControl), "f1.txt");
|
||||
var fileOne = FileNames.join(shellProcessControl.getOsType().getTempDirectory(shellProcessControl), UUID.randomUUID().toString());
|
||||
try (var c = shellProcessControl
|
||||
.command(shellProcessControl.getShellType().getStreamFileWriteCommand(fileOne))
|
||||
.start()) {
|
||||
|
@ -36,7 +51,7 @@ public enum ShellCheckTestItem {
|
|||
|
||||
shellProcessControl.restart();
|
||||
|
||||
var fileTwo = FileNames.join(shellProcessControl.getOsType().getTempDirectory(shellProcessControl), "f2.txt");
|
||||
var fileTwo = FileNames.join(shellProcessControl.getOsType().getTempDirectory(shellProcessControl), UUID.randomUUID().toString());
|
||||
try (var c = shellProcessControl
|
||||
.subShell(shellProcessControl.getShellType())
|
||||
.command(shellProcessControl.getShellType().getStreamFileWriteCommand(fileTwo))
|
||||
|
@ -59,11 +74,13 @@ public enum ShellCheckTestItem {
|
|||
|
||||
SIMPLE_WRITE(shellProcessControl -> {
|
||||
var content = "hello worldß";
|
||||
var fileOne = FileNames.join(shellProcessControl.getOsType().getTempDirectory(shellProcessControl), "f1.txt");
|
||||
shellProcessControl.executeSimpleCommand(shellProcessControl.getShellType().getSimpleFileWriteCommand(content, fileOne));
|
||||
var fileOne = FileNames.join(shellProcessControl.getOsType().getTempDirectory(shellProcessControl), UUID.randomUUID().toString());
|
||||
shellProcessControl.executeSimpleCommand(
|
||||
shellProcessControl.getShellType().getTextFileWriteCommand(content, fileOne));
|
||||
|
||||
var fileTwo = FileNames.join(shellProcessControl.getOsType().getTempDirectory(shellProcessControl), "f2.txt");
|
||||
shellProcessControl.executeSimpleCommand(shellProcessControl.getShellType().getSimpleFileWriteCommand(content, fileTwo));
|
||||
var fileTwo = FileNames.join(shellProcessControl.getOsType().getTempDirectory(shellProcessControl), UUID.randomUUID().toString());
|
||||
shellProcessControl.executeSimpleCommand(
|
||||
shellProcessControl.getShellType().getTextFileWriteCommand(content, fileTwo));
|
||||
|
||||
var s1 = shellProcessControl.executeStringSimpleCommand(
|
||||
shellProcessControl.getShellType().getFileReadCommand(fileOne));
|
||||
|
@ -74,12 +91,12 @@ public enum ShellCheckTestItem {
|
|||
}),
|
||||
|
||||
TERMINAL_OPEN(shellProcessControl -> {
|
||||
shellProcessControl.prepareTerminalOpen(null);
|
||||
shellProcessControl.prepareIntermediateTerminalOpen(null);
|
||||
}),
|
||||
|
||||
COMMAND_TERMINAL_OPEN(shellProcessControl -> {
|
||||
for (CommandCheckTestItem v : CommandCheckTestItem.values()) {
|
||||
shellProcessControl.prepareTerminalOpen(v.getCommandFunction().apply(shellProcessControl));
|
||||
shellProcessControl.prepareIntermediateTerminalOpen(v.getCommandFunction().apply(shellProcessControl));
|
||||
}
|
||||
}),
|
||||
|
||||
|
|
|
@ -39,6 +39,10 @@ public interface DataStoreProvider {
|
|||
throw new ExtensionException("Provider " + getId() + " has no set category");
|
||||
}
|
||||
|
||||
default DataStore getParent(DataStore store) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default GuiDialog guiDialog(Property<DataStore> store) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -72,6 +72,10 @@ public class TrackEvent {
|
|||
return builder().type("debug").message(message);
|
||||
}
|
||||
|
||||
public static TrackEventBuilder withDebug(String cat, String message) {
|
||||
return builder().category(cat).type("debug").message(message);
|
||||
}
|
||||
|
||||
public static void debug(String cat, String message) {
|
||||
builder().category(cat).type("debug").message(message).build().handle();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package io.xpipe.extension.fxcomps.util;
|
||||
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
|
@ -8,6 +10,7 @@ import java.lang.ref.WeakReference;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class BindingsHelper {
|
||||
|
||||
|
@ -44,6 +47,45 @@ public class BindingsHelper {
|
|||
});
|
||||
}
|
||||
|
||||
public static <T, V> ObservableList<T> mappedContentBinding(ObservableList<V> l2, Function<V, T> map) {
|
||||
ObservableList<T> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(l1, l2.stream().map(map).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> orderedContentBinding(ObservableList<V> l2, Comparator<V> comp) {
|
||||
ObservableList<V> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(l1, l2.stream().sorted(comp).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <V> ObservableList<V> filteredContentBinding(ObservableList<V> l2, ObservableValue<Predicate<V>> predicate) {
|
||||
ObservableList<V> l1 = FXCollections.observableList(new ArrayList<>());
|
||||
Runnable runnable = () -> {
|
||||
setContent(l1, l2.stream().filter(predicate.getValue()).toList());
|
||||
};
|
||||
runnable.run();
|
||||
l2.addListener((ListChangeListener<? super V>) c -> {
|
||||
runnable.run();
|
||||
});
|
||||
predicate.addListener((c,o,n) -> {
|
||||
runnable.run();
|
||||
});
|
||||
return l1;
|
||||
}
|
||||
|
||||
public static <T> void setContent(ObservableList<T> toSet, List<? extends T> newList) {
|
||||
if (toSet.equals(newList)) {
|
||||
return;
|
||||
|
|
|
@ -40,8 +40,8 @@ public class DataStoreFormatter {
|
|||
return func.apply(length);
|
||||
}
|
||||
|
||||
var fileString = func.apply(length - atString.length() - 4);
|
||||
return String.format("%s -> %s", atString, fileString);
|
||||
var fileString = func.apply(length - atString.length() - 3);
|
||||
return String.format("%s > %s", atString, fileString);
|
||||
}
|
||||
|
||||
public static String toName(DataStore input) {
|
||||
|
@ -96,6 +96,17 @@ public class DataStoreFormatter {
|
|||
);
|
||||
}
|
||||
|
||||
if (input.endsWith(".compute.amazonaws.com")) {
|
||||
var split = input.split("\\.");
|
||||
var name = split[0];
|
||||
var region = split[1];
|
||||
var lengthShare = (length - 3) / 2;
|
||||
return String.format(
|
||||
"%s.%s",
|
||||
DataStoreFormatter.cut(name, lengthShare), DataStoreFormatter.cut(region, length - lengthShare)
|
||||
);
|
||||
}
|
||||
|
||||
return cut(input, length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
package io.xpipe.extension.util;
|
||||
|
||||
import io.xpipe.core.impl.FileNames;
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.process.ShellProcessControl;
|
||||
import io.xpipe.core.process.ShellType;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
import io.xpipe.core.util.XPipeSession;
|
||||
import io.xpipe.core.util.XPipeTempDirectory;
|
||||
import io.xpipe.extension.event.TrackEvent;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class ScriptHelper {
|
||||
|
||||
public static int getConnectionHash(String command) {
|
||||
return Math.abs(Objects.hash(command, XPipeSession.get().getSystemSessionId()));
|
||||
// This deterministic approach can cause permission problems when two different users execute the same command on a system
|
||||
return new Random().nextInt(Integer.MAX_VALUE);
|
||||
//return Math.abs(Objects.hash(command, XPipeSession.get().getSystemSessionId()));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
|
@ -25,6 +28,38 @@ public class ScriptHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static String constructOpenWithInitScriptCommand(ShellProcessControl processControl, List<String> init, String toExecuteInShell) {
|
||||
ShellType t = processControl.getShellType();
|
||||
if (init.size() == 0 && toExecuteInShell == null) {
|
||||
return t.getNormalOpenCommand();
|
||||
}
|
||||
|
||||
String nl = t.getNewLine().getNewLineString();
|
||||
var content = String.join(nl, init)
|
||||
+ nl;
|
||||
|
||||
if (processControl.getOsType().equals(OsType.LINUX)
|
||||
|| processControl.getOsType().equals(OsType.MAC)) {
|
||||
content = "if [ -f ~/.bashrc ]; then . ~/.bashrc; fi\n" + content;
|
||||
}
|
||||
|
||||
if (toExecuteInShell != null) {
|
||||
content += toExecuteInShell + nl;
|
||||
content += t.getExitCommand() + nl;
|
||||
}
|
||||
|
||||
var initFile = createExecScript(processControl, content, true);
|
||||
return t.getInitFileOpenCommand(initFile);
|
||||
}
|
||||
|
||||
public static String prepend(ShellProcessControl processControl, List<String> init, String commands) {
|
||||
var prefix = init != null && init.size() > 0
|
||||
? String.join(processControl.getShellType().getNewLine().getNewLineString(), init)
|
||||
+ processControl.getShellType().getNewLine().getNewLineString()
|
||||
: "";
|
||||
return prefix + commands;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String createExecScript(ShellProcessControl processControl, String content, boolean restart) {
|
||||
var fileName = "exec-" + getConnectionHash(content);
|
||||
|
@ -35,7 +70,8 @@ public class ScriptHelper {
|
|||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static String createExecScript(ShellProcessControl processControl, String file, String content, boolean restart) {
|
||||
private static String createExecScript(
|
||||
ShellProcessControl processControl, String file, String content, boolean restart) {
|
||||
ShellType type = processControl.getShellType();
|
||||
content = type.prepareScriptContent(content);
|
||||
|
||||
|
@ -49,32 +85,19 @@ public class ScriptHelper {
|
|||
.handle();
|
||||
|
||||
processControl.executeSimpleCommand(type.getFileTouchCommand(file), "Failed to create script " + file);
|
||||
processControl.executeSimpleCommand(type.getMakeExecutableCommand(file), "Failed to make script " + file + " executable");
|
||||
|
||||
if (!content.contains("\n")) {
|
||||
processControl.executeSimpleCommand(type.getSimpleFileWriteCommand(content, file));
|
||||
return file;
|
||||
}
|
||||
|
||||
try (var c = processControl.command(type.getStreamFileWriteCommand(file)).start()) {
|
||||
c.discardOut();
|
||||
c.discardErr();
|
||||
c.getStdin().write(content.getBytes(processControl.getCharset()));
|
||||
c.closeStdin();
|
||||
}
|
||||
|
||||
if (restart) {
|
||||
processControl.restart();
|
||||
}
|
||||
processControl.executeSimpleCommand(
|
||||
type.getMakeExecutableCommand(file), "Failed to make script " + file + " executable");
|
||||
|
||||
processControl.executeSimpleCommand(type.getTextFileWriteCommand(content, file));
|
||||
return file;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String createAskPassScript(SecretValue pass, ShellProcessControl parent, ShellType type, boolean restart) {
|
||||
public static String createAskPassScript(
|
||||
SecretValue pass, ShellProcessControl parent, ShellType type, boolean restart) {
|
||||
var content = type.getScriptEchoCommand(pass.getSecretValue());
|
||||
var temp = XPipeTempDirectory.get(parent);
|
||||
var file = FileNames.join(temp, "askpass-" + getConnectionHash(content) + "." + type.getScriptFileEnding());
|
||||
return createExecScript(parent,file, content, restart);
|
||||
return createExecScript(parent, file, content, restart);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue