Try to fix push

This commit is contained in:
crschnick 2023-02-01 09:16:26 +00:00
parent 07563bb059
commit fd2e6f968b
28 changed files with 516 additions and 113 deletions

View file

@ -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"

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
}
}
}

View file

@ -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() {

View file

@ -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

View file

@ -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));
}
}

View file

@ -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;
}

View file

@ -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() {

View file

@ -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);

View file

@ -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();
}

View file

@ -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;

View file

@ -75,3 +75,7 @@
-fx-padding: 0;
-fx-focus-color: transparent;
}
.store-list-comp .list-cell {
-fx-padding: 0;
}

View file

@ -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;
}

View file

@ -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
View 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
View 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"

View file

@ -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"

View file

@ -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);

View file

@ -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);

View 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 + "\"";
}

View file

@ -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
View file

@ -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")

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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);
}
}