diff --git a/.github/workflows/win-exe.yml b/.github/workflows/win-exe.yml index cb754ca17..45ce36c2e 100644 --- a/.github/workflows/win-exe.yml +++ b/.github/workflows/win-exe.yml @@ -8,11 +8,17 @@ on: version: description: 'Version' required: false + isDebug: + description: 'Build debug version with console output' + type: boolean + env: JAVA_VERSION: 19 - JAVA_DIST: 'zulu' + JAVA_DIST: 'temurin' JAVA_CACHE: 'maven' + JFX_JMODS_URL: 'https://download2.gluonhq.com/openjfx/19.0.2.1/openjfx-19.0.2.1_windows-x64_bin-jmods.zip' + JFX_JMODS_HASH: 'B7CF2CAD2468842B3B78D99F6C0555771499A36FA1F1EE3DD1B9A4597F1FAB86' defaults: run: @@ -30,6 +36,7 @@ jobs: needs: [get-version] env: LOOPBACK_ALIAS: 'cryptomator-vault' + WIN_CONSOLE_FLAG: '' steps: - uses: actions/checkout@v3 - name: Setup Java @@ -37,17 +44,31 @@ jobs: with: distribution: ${{ env.JAVA_DIST }} java-version: ${{ env.JAVA_VERSION }} - java-package: 'jdk+fx' + java-package: 'jdk' cache: ${{ env.JAVA_CACHE }} - - name: Ensure major jfx version in pom equals in jdk - shell: pwsh + - name: Download and extract JavaFX jmods from Gluon + #In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip run: | - $jfxPomVersion = (&mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) -split "\." - $jfxJdkVersion = ((Get-Content -path "${env:JAVA_HOME}/lib/javafx.properties" | Where-Object {$_ -like 'javafx.version=*' }) -replace '.*=','') -split "\." - if ($jfxPomVersion[0] -ne $jfxJdkVersion[0]) { - Write-Error "Major part of JavaFX version in pom($($jfxPomVersion[0])) does not match the version in JDK($($jfxJdkVersion[0])) " - exit 1 + curl --output jfxjmods.zip -L "${{ env.JFX_JMODS_URL }}" + if(!(Get-FileHash -Path jfxjmods.zip -Algorithm SHA256).Hash.equals("${{ env.JFX_JMODS_HASH }}")) { + exit 1; } + Expand-Archive -Path jfxjmods.zip -DestinationPath jfxjmods + Get-ChildItem -Path jfxjmods -Recurse -Filter "*.jmod" | ForEach-Object { Move-Item -Path $_ -Destination $_.Directory.Parent} + shell: pwsh + - name: Ensure major jfx version in pom and in jmods is the same + run: | + JMOD_VERSION_AMD64=$(jmod describe jfxjmods/javafx.base.jmod | head -1) + JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@} + JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*} + POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) + POM_JFX_VERSION=${POM_JFX_VERSION#*@} + POM_JFX_VERSION=${POM_JFX_VERSION%%.*} + + if [ $POM_JFX_VERSION -ne $JMOD_VERSION_AMD64 ]; then + >&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != amd64 jmod version (${JMOD_VERSION_AMD64})" + exit 1 + fi - name: Set version run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} - name: Run maven @@ -61,13 +82,16 @@ jobs: ${JAVA_HOME}/bin/jlink --verbose --output runtime - --module-path "${JAVA_HOME}/jmods" + --module-path "jfxjmods;${JAVA_HOME}/jmods" --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr --strip-native-commands --no-header-files --no-man-pages --strip-debug --compress=1 + - name: Change win-console flag if debug is active + if: ${{ inputs.isDebug }} + run: echo "WIN_CONSOLE_FLAG=--win-console" >> $GITHUB_ENV - name: Run jpackage run: > ${JAVA_HOME}/bin/jpackage @@ -99,8 +123,10 @@ jobs: --java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\"" --java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\"" --java-options "-Dcryptomator.integrationsWin.keychainPaths=\"~/AppData/Roaming/Cryptomator/keychain.json\"" + --java-options "-Djavafx.verbose=${{ inputs.isDebug }}" --resource-dir dist/win/resources --icon dist/win/resources/Cryptomator.ico + ${WIN_CONSOLE_FLAG} - name: Patch Application Directory run: | cp dist/win/contrib/* appdir/Cryptomator diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml deleted file mode 100644 index 632b02de5..000000000 --- a/.github/workflows/winget.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Release to Winget - -on: - workflow_call: - inputs: - releaseTag: - required: true - type: string - workflow_dispatch: - inputs: - releaseTag: - description: 'Release tag name' - required: true - type: string - -jobs: - publish-winget: - name: Publish on winget repo - runs-on: windows-latest - steps: - - name: Get download url for release assets - id: get-release-assets - uses: actions/github-script@v6 - with: - script: | - const query =`query($tag:String!) { - repository(owner:"cryptomator", name:"cryptomator"){ - release(tagName: $tag) { - releaseAssets(first:20) { - nodes { - name - downloadUrl - } - } - } - } - }`; - const variables = { - tag: "${{ inputs.releaseTag }}" - } - return await github.graphql(query, variables) - - name: Submit package to Windows Package Manager Community Repository - id: submit-winget - run: | - iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - $releaseAssets = (ConvertFrom-Json '${{ steps.get-release-assets.outputs.result }}').repository.release.releaseAssets.nodes - $installerUrl = $releaseAssets | Where-Object -Property name -match '^Cryptomator-.*\.msi$' | Select -ExpandProperty downloadUrl -First 1 - .\wingetcreate.exe update Cryptomator.Cryptomator -s -v "${{ inputs.releaseTag }}" -u "$installerUrl" -t ${{ secrets.CRYPTOBOT_WINGET_TOKEN }} - shell: pwsh diff --git a/README.md b/README.md index 88e5d1475..b78fb7d88 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ Cryptomator is provided free of charge as an open-source project despite the hig gee-whiz - Proxy-Hub diff --git a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml index 7295b66e9..db65581f0 100644 --- a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml +++ b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml @@ -66,6 +66,7 @@ + diff --git a/pom.xml b/pom.xml index f257a2fa7..487e404d1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cryptomator cryptomator - 1.7.3 + 1.7.4 Cryptomator Desktop App @@ -33,12 +33,12 @@ org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh - 2.6.2 + 2.6.3 1.2.0 1.2.0 1.2.0 1.2.0 - 2.0.4 + 2.0.5 2.0.0 2.0.2 @@ -317,41 +317,6 @@ - - org.codehaus.mojo - exec-maven-plugin - 3.1.0 - - - compile-light-theme - compile - - java - - - javafx.graphics/com.sun.javafx.css.parser.Css2Bin - - ${project.basedir}/src/main/resources/css/light_theme.css - ${project.build.outputDirectory}/css/light_theme.bss - - - - - compile-dark-theme - compile - - java - - - javafx.graphics/com.sun.javafx.css.parser.Css2Bin - - ${project.basedir}/src/main/resources/css/dark_theme.css - ${project.build.outputDirectory}/css/dark_theme.bss - - - - - org.apache.maven.plugins maven-jar-plugin diff --git a/src/main/java/org/cryptomator/common/ObservableUtil.java b/src/main/java/org/cryptomator/common/ObservableUtil.java index 289f6e929..7927b6e54 100644 --- a/src/main/java/org/cryptomator/common/ObservableUtil.java +++ b/src/main/java/org/cryptomator/common/ObservableUtil.java @@ -2,7 +2,9 @@ package org.cryptomator.common; import javafx.beans.binding.Bindings; import javafx.beans.value.ObservableValue; +import java.util.Objects; import java.util.function.Function; +import java.util.function.Supplier; public class ObservableUtil { @@ -15,4 +17,14 @@ public class ObservableUtil { } }, observable); } + + public static ObservableValue mapWithDefault(ObservableValue observable, Function mapper, Supplier defaultValue) { + return Bindings.createObjectBinding(() -> { + if (observable.getValue() == null) { + return defaultValue.get(); + } else { + return mapper.apply(observable.getValue()); + } + }, observable); + } } diff --git a/src/main/java/org/cryptomator/common/mount/MountModule.java b/src/main/java/org/cryptomator/common/mount/MountModule.java index d78cc3216..0978c44db 100644 --- a/src/main/java/org/cryptomator/common/mount/MountModule.java +++ b/src/main/java/org/cryptomator/common/mount/MountModule.java @@ -2,50 +2,71 @@ package org.cryptomator.common.mount; import dagger.Module; import dagger.Provides; +import org.cryptomator.common.ObservableUtil; import org.cryptomator.common.settings.Settings; +import org.cryptomator.integrations.mount.Mount; import org.cryptomator.integrations.mount.MountService; +import javax.inject.Named; import javax.inject.Singleton; -import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; @Module public class MountModule { + private static final AtomicReference formerSelectedMountService = new AtomicReference<>(null); + private static final List problematicFuseMountServices = List.of("org.cryptomator.frontend.fuse.mount.MacFuseMountProvider", "org.cryptomator.frontend.fuse.mount.FuseTMountProvider"); + @Provides @Singleton static List provideSupportedMountServices() { return MountService.get().toList(); } - //currently not used, because macFUSE and FUSE-T cannot be used in the same JVM - /* @Provides @Singleton - static ObservableValue provideMountService(Settings settings, List serviceImpls) { + @Named("FUPFMS") + static AtomicReference provideFirstUsedProblematicFuseMountService() { + return new AtomicReference<>(null); + } + + @Provides + @Singleton + static ObservableValue provideMountService(Settings settings, List serviceImpls, @Named("FUPFMS") AtomicReference fupfms) { var fallbackProvider = serviceImpls.stream().findFirst().orElse(null); - return ObservableUtil.mapWithDefault(settings.mountService(), // + + var observableMountService = ObservableUtil.mapWithDefault(settings.mountService(), // desiredServiceImpl -> { // - var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); // - return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); // + var serviceFromSettings = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(desiredServiceImpl)).findAny(); // + var targetedService = serviceFromSettings.orElse(fallbackProvider); + return applyWorkaroundForProblematicFuse(targetedService, serviceFromSettings.isPresent(), fupfms); }, // - new ActualMountService(fallbackProvider, true)); - } - */ - - @Provides - @Singleton - static ActualMountService provideActualMountService(Settings settings, List serviceImpls) { - var fallbackProvider = serviceImpls.stream().findFirst().orElse(null); - var desiredService = serviceImpls.stream().filter(serviceImpl -> serviceImpl.getClass().getName().equals(settings.mountService().getValue())).findFirst(); // - return new ActualMountService(desiredService.orElse(fallbackProvider), desiredService.isPresent()); // + () -> { // + return applyWorkaroundForProblematicFuse(fallbackProvider, true, fupfms); + }); + return observableMountService; } - @Provides - @Singleton - static ObservableValue provideMountService(ActualMountService service) { - return new SimpleObjectProperty<>(service); + //see https://github.com/cryptomator/cryptomator/issues/2786 + private synchronized static ActualMountService applyWorkaroundForProblematicFuse(MountService targetedService, boolean isDesired, AtomicReference firstUsedProblematicFuseMountService) { + //set the first used problematic fuse service if applicable + var targetIsProblematicFuse = isProblematicFuseService(targetedService); + if (targetIsProblematicFuse && firstUsedProblematicFuseMountService.get() == null) { + firstUsedProblematicFuseMountService.set(targetedService); + } + + //do not use the targeted mount service and fallback to former one, if the service is problematic _and_ not the first problematic one used. + if (targetIsProblematicFuse && !firstUsedProblematicFuseMountService.get().equals(targetedService)) { + return new ActualMountService(formerSelectedMountService.get(), false); + } else { + formerSelectedMountService.set(targetedService); + return new ActualMountService(targetedService, isDesired); + } } + public static boolean isProblematicFuseService(MountService service) { + return problematicFuseMountServices.contains(service.getClass().getName()); + } } diff --git a/src/main/java/org/cryptomator/ui/preferences/VolumePreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/VolumePreferencesController.java index 61a54ba22..6c60e74b7 100644 --- a/src/main/java/org/cryptomator/ui/preferences/VolumePreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/VolumePreferencesController.java @@ -2,12 +2,14 @@ package org.cryptomator.ui.preferences; import dagger.Lazy; import org.cryptomator.common.ObservableUtil; +import org.cryptomator.common.mount.MountModule; import org.cryptomator.common.settings.Settings; import org.cryptomator.integrations.mount.MountCapability; import org.cryptomator.integrations.mount.MountService; import org.cryptomator.ui.common.FxController; import javax.inject.Inject; +import javax.inject.Named; import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanExpression; @@ -19,6 +21,7 @@ import javafx.util.StringConverter; import java.util.List; import java.util.Optional; import java.util.ResourceBundle; +import java.util.concurrent.atomic.AtomicReference; @PreferencesScoped public class VolumePreferencesController implements FxController { @@ -33,6 +36,7 @@ public class VolumePreferencesController implements FxController { private final ObservableValue mountToDriveLetterSupported; private final ObservableValue mountFlagsSupported; private final ObservableValue readonlySupported; + private final ObservableValue fuseRestartRequired; private final Lazy application; private final List mountProviders; public ChoiceBox volumeTypeChoiceBox; @@ -40,7 +44,7 @@ public class VolumePreferencesController implements FxController { public Button loopbackPortApplyButton; @Inject - VolumePreferencesController(Settings settings, Lazy application, List mountProviders, ResourceBundle resourceBundle) { + VolumePreferencesController(Settings settings, Lazy application, List mountProviders, @Named("FUPFMS") AtomicReference firstUsedProblematicFuseMountService, ResourceBundle resourceBundle) { this.settings = settings; this.application = application; this.mountProviders = mountProviders; @@ -53,6 +57,12 @@ public class VolumePreferencesController implements FxController { this.mountToDriveLetterSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER)); this.mountFlagsSupported = selectedMountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS)); this.readonlySupported = selectedMountService.map(s -> s.hasCapability(MountCapability.READ_ONLY)); + this.fuseRestartRequired = selectedMountService.map(s -> {// + return firstUsedProblematicFuseMountService.get() != null // + && MountModule.isProblematicFuseService(s) // + && !firstUsedProblematicFuseMountService.get().equals(s); + }); + } public void initialize() { @@ -129,6 +139,14 @@ public class VolumePreferencesController implements FxController { return mountFlagsSupported.getValue(); } + public ObservableValue fuseRestartRequiredProperty() { + return fuseRestartRequired; + } + + public boolean getFuseRestartRequired() { + return fuseRestartRequired.getValue(); + } + /* Helpers */ private class MountServiceConverter extends StringConverter { diff --git a/src/main/resources/css/dark_theme.css b/src/main/resources/css/dark_theme.css index 5f0877842..45cadba93 100644 --- a/src/main/resources/css/dark_theme.css +++ b/src/main/resources/css/dark_theme.css @@ -116,6 +116,10 @@ -fx-font-size: 0.64em; } +.label-red { + -fx-text-fill: RED_5; +} + .text-flow > * { -fx-fill: TEXT_FILL; } diff --git a/src/main/resources/css/light_theme.css b/src/main/resources/css/light_theme.css index decf64b64..c3c0faaa9 100644 --- a/src/main/resources/css/light_theme.css +++ b/src/main/resources/css/light_theme.css @@ -116,6 +116,10 @@ -fx-font-size: 0.64em; } +.label-red { + -fx-text-fill: RED_5; +} + .text-flow > * { -fx-fill: TEXT_FILL; } diff --git a/src/main/resources/fxml/preferences_volume.fxml b/src/main/resources/fxml/preferences_volume.fxml index 16ccc2b52..f48b1c1c8 100644 --- a/src/main/resources/fxml/preferences_volume.fxml +++ b/src/main/resources/fxml/preferences_volume.fxml @@ -8,9 +8,9 @@ + - +