diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b2ace54c2..e47dae730 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,6 +14,9 @@ updates: versions: ["2.0.1.MR"] - dependency-name: "org.openjfx:*" update-types: ["version-update:semver-major"] + # due to https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter/issues/68 + - dependency-name: "org.apache.maven.plugins:maven-surefire-plugin" + versions: [ "3.5.4", "3.5.5" ] groups: java-test-dependencies: patterns: diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index 601f3dc7f..d3da01b7b 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -8,6 +8,10 @@ on: version: description: 'Version' required: false + create-pr: + description: 'Create a PR for aur-bin repo' + type: boolean + default: false push: branches-ignore: - 'dependabot/**' @@ -31,16 +35,20 @@ jobs: name: Build AppImage runs-on: ${{ matrix.os }} needs: [get-version] + env: + SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }} + SEMVER_NUM: ${{ needs.get-version.outputs.semVerNum }} + REV_NUM: ${{ needs.get-version.outputs.revNum }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest - appimage-suffix: x86_64 + arch: x86_64 openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip' openjfx-sha: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b' - os: ubuntu-24.04-arm - appimage-suffix: aarch64 + arch: aarch64 openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip' openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646' steps: @@ -55,7 +63,7 @@ jobs: - name: Download OpenJFX jmods id: download-jmods run: | - curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip + curl --silent --fail-with-body --proto "=https" -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip echo "${{ matrix.openjfx-sha }} openjfx-jmods.zip" | shasum -a256 --check mkdir -p openjfx-jmods unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods @@ -73,7 +81,7 @@ jobs: exit 1 fi - name: Set version - run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} + run : mvn versions:set -DnewVersion="$SEMVER_STR" - name: Run maven run: mvn -B clean package -Plinux -DskipTests - name: Patch target dir @@ -94,13 +102,15 @@ jobs: ${JAVA_HOME}/bin/jlink --verbose --output runtime - --module-path "${{ steps.jep-493-check.outputs.jmod_paths }}" + --module-path "${JMOD_PATHS}" --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.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler --strip-native-commands --no-header-files --no-man-pages --strip-debug --compress zip-0 + env: + JMOD_PATHS: ${{ steps.jep-493-check.outputs.jmod_paths }} - name: Run jpackage run: > ${JAVA_HOME}/bin/jpackage @@ -114,12 +124,12 @@ jobs: --name Cryptomator --vendor "Skymatic GmbH" --copyright "(C) 2016 - 2025 Skymatic GmbH" - --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}" + --app-version "${SEMVER_NUM}.${REV_NUM}" --java-options "--enable-preview" --java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" --java-options "-Xss5m" --java-options "-Xmx256m" - --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\"" + --java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" --java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Djava.net.useSystemProxies=true" --java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" @@ -130,7 +140,7 @@ jobs: --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" --java-options "-Dcryptomator.showTrayIcon=true" --java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\"" - --java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\"" + --java-options "-Dcryptomator.buildNumber=\"appimage-${REV_NUM}\"" --java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" --java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" --resource-dir dist/linux/resources @@ -155,7 +165,7 @@ jobs: ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun - name: Download AppImageKit run: | - curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage + curl --silent --fail-with-body --proto "=https" -L "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${{ matrix.arch }}.AppImage" -o appimagetool.AppImage chmod +x appimagetool.AppImage ./appimagetool.AppImage --appimage-extract - name: Prepare GPG-Agent for signing with key 615D449FE6E6A235 @@ -167,17 +177,17 @@ jobs: GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} - name: Build AppImage run: > - ./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage - -u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync" + ./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${SEMVER_STR}-${{ matrix.arch }}.AppImage + -u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.arch }}.AppImage.zsync" --sign --sign-key=615D449FE6E6A235 - name: Create detached GPG signatures run: | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync - name: Upload artifacts - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: appimage-${{ matrix.appimage-suffix }} + name: appimage-${{ matrix.arch }} path: | cryptomator-*.AppImage cryptomator-*.AppImage.zsync @@ -196,65 +206,77 @@ jobs: create-aur-bin-pr: name: Create PR for aur-bin repo - needs: [build, get-version] + if: github.event_name == 'workflow_dispatch' && inputs.create-pr || github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable' runs-on: ubuntu-latest - if: github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable' + needs: [build, get-version] + container: + image: archlinux:base-devel + env: + SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }} + PKGDEST: ${{ github.workspace }}/pkgdest + SRCDEST: ${{ github.workspace }}/srcdest steps: - - name: Download AppImages - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 - with: - path: downloads/ - merge-multiple: true - - name: Compute sha256 hash of AppImages - id: checksums + - name: Prepare pacman run: | - X64_SHA256=$(sha256sum downloads/cryptomator-*-x86_64.AppImage | cut -d ' ' -f1) - echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT" - AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1) - echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT" - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + pacman-key --init + pacman-key --populate archlinux + pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip github-cli curl pacman-contrib + - name: Checkout cryptomator/aur-bin + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: 'cryptomator/aur-bin' token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - - name: Install dependencies + - name: Create build user run: | - sudo apt-get update - sudo apt-get -y install makepkg pacman-package-manager + useradd -m builder + echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/builder + chown -R builder:builder "$GITHUB_WORKSPACE" + install -d -m 0755 -o builder -g builder "$PKGDEST" "$SRCDEST" + - name: Import Cryptomator release signing key + # try first ubuntu. on failure try openpgp keyservers + run: > + sudo -u builder gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235 + || sudo -u builder gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235 - name: Checkout release branch run: | - git checkout -b release/${{ needs.get-version.outputs.semVerStr }} + git config --global safe.directory '*' + git checkout -b "release/${SEMVER_STR}" - name: Update build file run: | - sed -i -e 's|^pkgver=.*$|pkgver=${{ needs.get-version.outputs.semVerStr }}|' PKGBUILD + sed -i -e "s|^pkgver=.*$|pkgver=${SEMVER_STR}|" PKGBUILD sed -i -e 's|^pkgrel=.*$|pkgrel=1|' PKGBUILD - sed -i -e "s|^sha256sums_x86_64=.*$|sha256sums_x86_64=('${{ steps.checksums.outputs.x64-sha256sum }}'|" PKGBUILD - sed -i -e "s|^sha256sums_aarch64=.*$|sha256sums_aarch64=('${{ steps.checksums.outputs.aarch64-sha256sum}}'|" PKGBUILD - makepkg --printsrcinfo > .SRCINFO + sudo -u builder updpkgsums + sudo -u builder makepkg --printsrcinfo > .SRCINFO + - name: Build package with makepkg + run: > + sudo -u builder + env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST" + makepkg --syncdeps --cleanbuild --noconfirm --log - name: Commit and push run: | - git config user.name "${{ github.actor }}" - git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" + git config user.name "cryptobot" + git config user.email "cryptobot@users.noreply.github.com" git config push.autoSetupRemote true - git stage . - git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}" + git stage PKGBUILD .SRCINFO + git commit -m "Prepare release ${SEMVER_STR}" git push - name: Create pull request id: create-pr run: | - printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update build instructions\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md - URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md) - echo "PR_URL=$URL" >> "$GITHUB_OUTPUT" + printf "Created by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" > pr_body.md + PR_URL=$(gh pr create --title "Release ${SEMVER_STR}" --body-file pr_body.md) + echo "url=$PR_URL" >> "$GITHUB_OUTPUT" env: GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - name: Slack Notification uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 env: - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }} SLACK_USERNAME: 'Cryptobot' SLACK_ICON: false SLACK_ICON_EMOJI: ':bot:' SLACK_CHANNEL: 'cryptomator-desktop' - SLACK_TITLE: "AUR-bin release PR for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created." - SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.PR_URL }}|PR> on how to proceed." + SLACK_TITLE: "AUR-bin release PR for ${{ github.event.repository.name }} ${{ needs.get-version.outputs.semVerStr }} created." + SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.url }}|PR> on how to proceed." SLACK_FOOTER: false MSG_MINIMAL: true diff --git a/.github/workflows/aur.yml b/.github/workflows/aur.yml deleted file mode 100644 index 303ce398c..000000000 --- a/.github/workflows/aur.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: Create PR for AUR - -on: - release: - types: [published] - workflow_dispatch: - inputs: - tag: - description: 'Release tag' - required: true - -jobs: - get-version: - uses: ./.github/workflows/get-version.yml - with: - version: ${{ inputs.tag }} - tarball: - name: Determines tarball url and compute checksum - runs-on: ubuntu-latest - needs: [get-version] - if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable' - env: - INPUT_TAG: ${{ inputs.tag }} - outputs: - url: ${{ steps.url.outputs.url}} - sha256: ${{ steps.sha256.outputs.sha256}} - steps: - - name: Determine tarball url - id: url - run: | - URL=""; - if [[ -n "${INPUT_TAG}" ]]; then - URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${INPUT_TAG}.tar.gz" - else - URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz" - fi - echo "url=${URL}" >> "$GITHUB_OUTPUT" - - name: Download source tarball and compute checksum - id: sha256 - run: | - curl --silent --fail-with-body -L -H "Accept: application/vnd.github+json" ${{ steps.url.outputs.url }} --output cryptomator.tar.gz - TARBALL_SHA256=$(sha256sum cryptomator.tar.gz | cut -d ' ' -f1) - echo "sha256=${TARBALL_SHA256}" >> "$GITHUB_OUTPUT" - aur: - name: Create PR for AUR - runs-on: ubuntu-latest - needs: [tarball, get-version] - env: - AUR_PR_URL: tbd - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: 'cryptomator/aur' - token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install makepkg pacman-package-manager - - name: Checkout release branch - run: | - git checkout -b release/${{ needs.get-version.outputs.semVerStr }} - - name: Update build file - run: | - sed -i -e 's|^pkgver=.*$|pkgver=${{ needs.get-version.outputs.semVerStr }}|' PKGBUILD - sed -i -e 's|^pkgrel=.*$|pkgrel=1|' PKGBUILD - sed -i -e "s|^sha256sums=.*$|sha256sums=('${{ needs.tarball.outputs.sha256 }}'|" PKGBUILD - makepkg --printsrcinfo > .SRCINFO - - name: Commit and push - run: | - git config user.name "${{ github.actor }}" - git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" - git config push.autoSetupRemote true - git stage . - git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}" - git push - - name: Create pull request - run: | - printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update build instructions\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md - PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md) - echo "AUR_PR_URL=$PR_URL" >> "$GITHUB_ENV" - env: - GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - - name: Slack Notification - if: github.event_name == 'release' - uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 - env: - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_USERNAME: 'Cryptobot' - SLACK_ICON: false - SLACK_ICON_EMOJI: ':bot:' - SLACK_CHANNEL: 'cryptomator-desktop' - SLACK_TITLE: "AUR release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created." - SLACK_MESSAGE: "See <${{ env.AUR_PR_URL }}|PR> on how to proceed." - SLACK_FOOTER: false - MSG_MINIMAL: true \ No newline at end of file diff --git a/.github/workflows/av-whitelist.yml b/.github/workflows/av-whitelist.yml index 4a8aba9af..0c2bc3d9a 100644 --- a/.github/workflows/av-whitelist.yml +++ b/.github/workflows/av-whitelist.yml @@ -49,9 +49,9 @@ jobs: url="${INPUT_URL}" echo "fileName=${url##*/}" >> $GITHUB_OUTPUT - name: Download file - run: curl "${INPUT_URL}" -L -o "${{steps.extractName.outputs.fileName}}" --fail-with-body + run: curl --silent --fail-with-body --proto "=https" -L "${INPUT_URL}" -o "${{steps.extractName.outputs.fileName}}" - name: Upload artifact - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: ${{ steps.extractName.outputs.fileName }} path: ${{ steps.extractName.outputs.fileName }} @@ -63,7 +63,7 @@ jobs: if: inputs.kaspersky steps: - name: Download artifact - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: name: ${{ needs.download-file.outputs.fileName }} path: upload @@ -83,7 +83,7 @@ jobs: if: inputs.avast steps: - name: Download artifact - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: name: ${{ needs.download-file.outputs.fileName }} path: upload diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index 178f46441..8a2cae05a 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -71,11 +71,11 @@ jobs: - name: Download OpenJFX jmods id: download-jmods run: | - curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip + curl --silent --fail-with-body --proto "=https" -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip echo "${{ env.OPENJFX_JMODS_AMD64_HASH }} openjfx-amd64.zip" | shasum -a256 --check mkdir -p jmods/amd64 unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64 - curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip + curl --silent --fail-with-body --proto "=https" -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip echo "${{ env.OPENJFX_JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | shasum -a256 --check mkdir -p jmods/aarch64 unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64 @@ -143,7 +143,7 @@ jobs: run: | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb - name: Upload artifacts - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: linux-deb-package path: | diff --git a/.github/workflows/flathub.yml b/.github/workflows/flathub.yml index e31f4cfdc..bf22cec30 100644 --- a/.github/workflows/flathub.yml +++ b/.github/workflows/flathub.yml @@ -33,7 +33,7 @@ jobs: - name: Download source tarball and compute checksum id: sha512 run: | - curl --silent --fail-with-body -L -H "Accept: application/vnd.github+json" ${{ steps.url.outputs.url }} --output cryptomator.tar.gz + curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" ${{ steps.url.outputs.url }} --output cryptomator.tar.gz TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1) echo "sha512=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT" flathub: diff --git a/.github/workflows/linux-makepkg.yml b/.github/workflows/linux-makepkg.yml new file mode 100644 index 000000000..1b2fb3fdb --- /dev/null +++ b/.github/workflows/linux-makepkg.yml @@ -0,0 +1,200 @@ +name: Build Arch package + +on: + release: + types: [published] + workflow_dispatch: + inputs: + version: + description: 'Version' + required: false + create-pr: + description: 'Create a PR for aur repo' + type: boolean + default: false + push: + branches-ignore: + - 'dependabot/**' + paths: + - '.github/workflows/linux-makepkg.yml' + - 'dist/linux/makepkg/**' + - 'dist/linux/common/**' + - 'dist/linux/resources/**' + +jobs: + get-version: + uses: ./.github/workflows/get-version.yml + with: + version: ${{ inputs.version }} + + makepkg: + name: Build with makepkg + needs: [get-version] + runs-on: ubuntu-latest + container: + image: archlinux:base-devel + env: + PKGDEST: ${{ github.workspace }}/pkgdest + SRCDEST: ${{ github.workspace }}/srcdest + steps: + - name: Prepare pacman + run: | + pacman-key --init + pacman-key --populate archlinux + pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + path: cryptomator + - name: Create build user + run: | + useradd -m builder + echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/builder + chown -R builder:builder "$GITHUB_WORKSPACE" + install -d -m 0755 -o builder -g builder "$PKGDEST" "$SRCDEST" + - name: Prepare PKGBUILD + # cannot use github.workspace due to https://github.com/actions/runner/issues/2058 + run: | + export SOURCES="${SOURCES_1}${GITHUB_WORKSPACE}${SOURCES_2}" + envsubst '$PKG_VERSION $PKG_RELEASE $SOURCES $SOURCES_SHA' < cryptomator/dist/linux/makepkg/PKGBUILD.template > PKGBUILD + env: + PKG_VERSION: ${{ needs.get-version.outputs.semVerNum }} + PKG_RELEASE: 1 + SOURCES_1: '"${_src_app_dir}::git+file://' + SOURCES_2: '/cryptomator"' + SOURCES_SHA: "'SKIP'" + - name: Build package with makepkg + run: > + sudo -u builder + env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST" + makepkg --syncdeps --cleanbuild --noconfirm --log + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: arch-package + if-no-files-found: error + path: | + ${{ env.PKGDEST }}/*.pkg.tar.zst + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: pkgbuild-file + if-no-files-found: error + path: | + cryptomator/dist/linux/makepkg/PKGBUILD.template + + create-pr: + name: Create PR for aur repo + if: github.event_name == 'workflow_dispatch' && inputs.create-pr || github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable' + runs-on: ubuntu-latest + needs: [get-version, makepkg] + container: + image: archlinux:base-devel + env: + PKGDEST: ${{ github.workspace }}/pkgdest + SRCDEST: ${{ github.workspace }}/srcdest + steps: + - name: Prepare pacman + run: | + pacman-key --init + pacman-key --populate archlinux + pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip github-cli curl + - name: Download source tarball and compute checksum + id: sha256 + run: | + URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz" + curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" ${URL} --output cryptomator.tar.gz + TARBALL_SHA256=$(sha256sum cryptomator.tar.gz | cut -d ' ' -f1) + echo "value=${TARBALL_SHA256}" >> "$GITHUB_OUTPUT" + env: + TAG: ${{ needs.get-version.outputs.semVerStr || github.event.release.tag_name }} + - name: Checkout cryptomator/aur repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: 'cryptomator/aur' + token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} + - name: Create build user + run: | + useradd -m builder + echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/builder + chown -R builder:builder "$GITHUB_WORKSPACE" + install -d -m 0755 -o builder -g builder "$PKGDEST" "$SRCDEST" + - name: Import Cryptomator release signing key + # try first ubuntu. on failure try openpgp keyservers + run: > + sudo -u builder gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235 + || sudo -u builder gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235 + - name: Checkout release branch + run: | + git config --global safe.directory '*' + git checkout -b release/${VERSION} + env: + VERSION: ${{ needs.get-version.outputs.semVerStr }} + - name: Determine pkgrel + id: pkgrel + run: | + TARGET_VERSION='${{ needs.get-version.outputs.semVerStr }}' + CURRENT_VERSION="$(sed -nE 's/^pkgver=(.*)$/\1/p' PKGBUILD | head -n1)" + CURRENT_REL="$(sed -nE 's/^pkgrel=([0-9]+).*$/\1/p' PKGBUILD | head -n1)" + + if [[ "$CURRENT_VERSION" == "$TARGET_VERSION" && "$CURRENT_REL" =~ ^[0-9]+$ ]]; then + NEXT_REL=$((CURRENT_REL + 1)) + else + NEXT_REL=1 + fi + + echo "value=${NEXT_REL}" >> "$GITHUB_OUTPUT" + echo "dist-version=${VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT" + env: + VERSION: ${{ needs.get-version.outputs.semVerStr }} + - name: Download PKGBUILD template + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: pkgbuild-file + - name: Prepare PKGBUILD + run: | + envsubst '$PKG_VERSION $PKG_RELEASE $SOURCES $SOURCES_SHA' < PKGBUILD.template > PKGBUILD + sudo -u builder makepkg --printsrcinfo > .SRCINFO + env: + PKG_VERSION: ${{ needs.get-version.outputs.semVerNum }} + PKG_RELEASE: ${{ steps.pkgrel.outputs.value }} + SOURCES: |- + "cryptomator-${pkgver//_/-}.tar.gz::https://github.com/cryptomator/cryptomator/archive/refs/tags/${pkgver//_/-}.tar.gz" + "cryptomator-${pkgver//_/-}.tar.gz.asc::https://github.com/cryptomator/cryptomator/releases/download/${pkgver//_/-}/cryptomator-${pkgver//_/-}.tar.gz.asc" + SOURCES_SHA: |- + '${{steps.sha256.outputs.value}}' + 'SKIP' + - name: Build package with makepkg + run: > + sudo -u builder + env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST" + makepkg --syncdeps --cleanbuild --noconfirm --log + - name: Commit and push + run: | + git config user.name "cryptobot" + git config user.email "cryptobot@users.noreply.github.com" + git config push.autoSetupRemote true + git stage PKGBUILD .SRCINFO + git commit -m "Prepare release ${DIST_VERSION}" + git push + env: + DIST_VERSION: ${{ steps.pkgrel.outputs.dist-version }} + - name: Create pull request + id: create-pr + run: | + printf "Created by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" > pr_body.md + PR_URL=$(gh pr create --title "Release $DIST_VERSION" --body-file pr_body.md) + echo "url=$PR_URL" >> "$GITHUB_OUTPUT" + env: + DIST_VERSION: ${{ steps.pkgrel.outputs.dist-version }} + GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} + - name: Slack Notification + if: github.event_name == 'release' + uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }} + SLACK_USERNAME: 'Cryptobot' + SLACK_ICON: false + SLACK_ICON_EMOJI: ':bot:' + SLACK_CHANNEL: 'cryptomator-desktop' + SLACK_TITLE: "AUR release PR created for ${{ github.event.repository.name }} ${{ steps.pkgrel.outputs.dist-version }} ." + SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.url }}|PR> on how to proceed." + SLACK_FOOTER: false + MSG_MINIMAL: true diff --git a/.github/workflows/mac-dmg-x64.yml b/.github/workflows/mac-dmg-x64.yml index 9afc867a6..bdb66d667 100644 --- a/.github/workflows/mac-dmg-x64.yml +++ b/.github/workflows/mac-dmg-x64.yml @@ -59,7 +59,7 @@ jobs: - name: Download OpenJFX jmods id: download-jmods run: | - curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip + curl --silent --fail-with-body --proto "=https" -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check mkdir -p openjfx-jmods/ unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods @@ -144,6 +144,7 @@ jobs: run: | mv appdir/Cryptomator.app Cryptomator.app mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/ + cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/ sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile @@ -151,20 +152,6 @@ jobs: VERSION_NO: ${{ needs.get-version.outputs.semVerNum }} REVISION_NO: ${{ needs.get-version.outputs.revNum }} PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }} - - name: Build and install DockTilePlugin - env: - DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build - run: | - xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \ - -scheme DockTilePlugin \ - -configuration Release \ - -destination "platform=macOS,arch=x86_64" \ - -derivedDataPath ${DERIVED_DATA_PATH} \ - -quiet \ - clean build - mkdir -p Cryptomator.app/Contents/PlugIns - cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/ - rm -rf ${DERIVED_DATA_PATH} - name: Generate license for dmg run: > mvn -B license:add-third-party @@ -283,7 +270,7 @@ jobs: run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db continue-on-error: true - name: Upload artifacts - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: dmg-${{ matrix.output-suffix }} path: | diff --git a/.github/workflows/mac-dmg.yml b/.github/workflows/mac-dmg.yml index 06116d2a7..743586153 100644 --- a/.github/workflows/mac-dmg.yml +++ b/.github/workflows/mac-dmg.yml @@ -57,7 +57,7 @@ jobs: - name: Download OpenJFX jmods id: download-jmods run: | - curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip + curl --silent --fail-with-body --proto "=https" -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check mkdir -p openjfx-jmods/ unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods @@ -143,6 +143,7 @@ jobs: run: | mv appdir/Cryptomator.app Cryptomator.app mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/ + cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/ sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile @@ -150,20 +151,6 @@ jobs: VERSION_NO: ${{ needs.get-version.outputs.semVerNum }} REVISION_NO: ${{ needs.get-version.outputs.revNum }} PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }} - - name: Build and install DockTilePlugin - env: - DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build - run: | - xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \ - -scheme DockTilePlugin \ - -configuration Release \ - -destination "platform=macOS,arch=arm64" \ - -derivedDataPath ${DERIVED_DATA_PATH} \ - -quiet \ - clean build - mkdir -p Cryptomator.app/Contents/PlugIns - cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/ - rm -rf ${DERIVED_DATA_PATH} - name: Generate license for dmg run: > mvn -B license:add-third-party @@ -282,7 +269,7 @@ jobs: run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db continue-on-error: true - name: Upload artifacts - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: dmg-${{ matrix.output-suffix }} path: | diff --git a/.github/workflows/no-response.yml b/.github/workflows/no-response.yml index 7c180e77d..6585256bb 100644 --- a/.github/workflows/no-response.yml +++ b/.github/workflows/no-response.yml @@ -12,7 +12,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: days-before-stale: 14 days-before-close: 0 diff --git a/.github/workflows/post-publish.yml b/.github/workflows/post-publish.yml index 020a41a06..619f0f607 100644 --- a/.github/workflows/post-publish.yml +++ b/.github/workflows/post-publish.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Download source tarball run: | - curl -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz --output cryptomator-${{ github.event.release.tag_name }}.tar.gz + curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz --output cryptomator-${{ github.event.release.tag_name }}.tar.gz - name: Sign source tarball with key 615D449FE6E6A235 run: | echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import @@ -28,7 +28,7 @@ jobs: - name: Slack Notification uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 env: - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }} SLACK_USERNAME: 'Cryptobot' SLACK_ICON: false SLACK_ICON_EMOJI: ':bot:' diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml index 897ff8647..2e6779093 100644 --- a/.github/workflows/release-check.yml +++ b/.github/workflows/release-check.yml @@ -43,6 +43,7 @@ jobs: exit 1 fi - name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file + if: ${{ ! (contains(github.event.head_commit.message, '[skip metadata check]') || contains(github.event.head_commit.message, '[metadata check skip]')) }} run: | if ! grep -q "" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 40559ca06..9f30d89a5 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -12,7 +12,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: days-before-stale: 365 days-before-close: 90 diff --git a/.github/workflows/win-exe.yml b/.github/workflows/win-exe.yml index e41d3c618..1e36d0af4 100644 --- a/.github/workflows/win-exe.yml +++ b/.github/workflows/win-exe.yml @@ -72,7 +72,7 @@ jobs: if: matrix.arch == 'x64' #In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip run: | - curl --output openjfx-jmods.zip -L "${{ env.OPENJFX_JMODS_AMD64 }}" + curl --silent --fail-with-body --proto "=https" -L "${{ env.OPENJFX_JMODS_AMD64 }}" --output openjfx-jmods.zip if(!(Get-FileHash -Path openjfx-jmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) { throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}."; } @@ -280,7 +280,7 @@ jobs: GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} - name: Upload artifacts - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: msi-${{ matrix.arch }} path: | @@ -311,7 +311,7 @@ jobs: env: WIX_VERSION: ${{ env.WIX_VERSION }} - name: Download .msi - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: name: msi-${{ matrix.arch }} path: dist/win/bundle/resources @@ -338,7 +338,7 @@ jobs: shell: pwsh - name: Download WinFsp run: | - curl --output $env:WINFSP_PATH -L ${{ env.WINFSP_MSI }} + curl --silent --fail-with-body --proto "=https" -L ${{ env.WINFSP_MSI }} --output $env:WINFSP_PATH $computedHash = (Get-FileHash -Path $env:WINFSP_PATH -Algorithm SHA256).Hash.ToLower() if ($computedHash -ne "${{ env.WINFSP_MSI_HASH }}") { throw "Checksum mismatch for $env:WINFSP_PATH (expected ${{ env.WINFSP_MSI_HASH }}, got $computedHash)." @@ -348,7 +348,7 @@ jobs: shell: pwsh - name: Download Legacy-WinFsp uninstaller run: | - curl --output dist/win/bundle/resources/winfsp-uninstaller.exe -L ${{ env.WINFSP_UNINSTALLER }} + curl --silent --fail-with-body --proto "=https" -L ${{ env.WINFSP_UNINSTALLER }} --output dist/win/bundle/resources/winfsp-uninstaller.exe shell: pwsh - name: Create Wix Burn bundle working-directory: dist/win @@ -423,7 +423,7 @@ jobs: GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} - name: Upload artifacts - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: exe-${{ matrix.executable-suffix }} path: | @@ -441,7 +441,7 @@ jobs: download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }} steps: - name: Download installers - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: merge-multiple: true - name: Publish installers on GitHub Releases diff --git a/CHANGELOG.md b/CHANGELOG.md index d5ce88491..ec9763ae0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,10 +20,12 @@ Changes to prior versions can be found on the [Github release page](https://gith * Support automatic app theme selection according to OS theme on Linux ([#4027](https://github.com/cryptomator/cryptomator/issues/4027)) * Admin configuration: Allow overwriting certain app properties by external config file ([#4105](https://github.com/cryptomator/cryptomator/pull/4105)) * New keychain backend using [secret service API](https://specifications.freedesktop.org/secret-service/0.2) for Linux ([#4025](https://github.com/cryptomator/cryptomator/pull/4025)) +* Liquid Glass icon for macOS ([#4166](https://github.com/cryptomator/cryptomator/pull/4166)) ### Fixed * Fixed password reset/show recovery possible for vaults without masterkey file ([#4120](https://github.com/cryptomator/cryptomator/pull/4120)) * Fixed restore vault config failed due to selecting a directory instead of file ([#4141](https://github.com/cryptomator/cryptomator/issues/4141)) +* Fixed leaking of cleartext paths into application log ([GHSA-j83j-mwhc-rcgw](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-j83j-mwhc-rcgw)) ### Changed * Disable user defined app start config on Windows ([#4132](https://github.com/cryptomator/cryptomator/issues/4132)) @@ -32,19 +34,19 @@ Changes to prior versions can be found on the [Github release page](https://gith * Update JavaFX to 25.0.2 ([#4145](https://github.com/cryptomator/cryptomator/pull/4145))) * Updated translations * Updated dependencies - * `ch.qos.logback:*` from 1.5.19 to 1.5.31 - * `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.0 - * `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.0 + * `ch.qos.logback:*` from 1.5.19 to 1.5.32 + * `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.1 + * `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.1 * `com.github.ben-manes.caffeine:caffeine` from 3.2.2 to 3.2.3 - * `com.google.dagger:*` from 2.57.2 to 2.59.1 + * `com.google.dagger:*` from 2.57.2 to 2.59.2 * `org.apache.commons:commons-lang3` from 3.19.0 to 3.20.0 - * `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0-beta3 + * `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0 * `org.cryptomator:cryptolib` from 2.2.1 to 2.2.2 - * `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.0 + * `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.1 * `org.cryptomator:integrations-api` from 1.7.0 to 1.8.0-beta1 * `org.cryptomator:integrations-linux` from 1.6.1 to 1.7.0-beta4 * `org.cryptomator:integrations-mac` from 1.4.1 to 1.5.0-beta3 * `org.cryptomator:integrations-win` from 1.5.1 to 1.6.0 * `org.cryptomator:webdav-nio-adapter` from 3.0.0 to 3.0.1 - + * `org.cryptomator:webdav-nio-adapter-servlet` to 1.2.12 diff --git a/dist/linux/makepkg/PKGBUILD.template b/dist/linux/makepkg/PKGBUILD.template new file mode 100644 index 000000000..003ca8c47 --- /dev/null +++ b/dist/linux/makepkg/PKGBUILD.template @@ -0,0 +1,118 @@ +# Maintainer: Aaron Graves +# Contributor: Julian Raufelder +# Contributor: Morten Linderud +# Contributor: Sebastian Stenzel +# Contributor: Armin Schrenk + +pkgname=cryptomator +pkgver=$PKG_VERSION +pkgrel=$PKG_RELEASE +pkgdesc="Multiplatform transparent client-side encryption of your files in the cloud." +arch=('any') +url="https://cryptomator.org/" +license=('GPL3') +depends=('fuse3' 'alsa-lib' 'hicolor-icon-theme' 'libxtst' 'libnet' 'libxrender') +makedepends=('maven' 'unzip') +optdepends=('keepassxc-cryptomator: Use KeePassXC to store vault passwords' 'ttf-hanazono: Install this font when using Japanese system language') +_jdkver=25.0.2+10 +_jfxver=25.0.2 +_src_app_dir=cryptomator-${pkgver//_/-} +source=($SOURCES); +source_x86_64=("jdk-${_jdkver}.tar.gz::https://github.com/adoptium/temurin${_jdkver:0:2}-binaries/releases/download/jdk-${_jdkver//\+/%2B}/OpenJDK${_jdkver:0:2}U-jdk_x64_linux_hotspot_${_jdkver//\+/_}.tar.gz" + "openjfx-${_jfxver}.zip::https://download2.gluonhq.com/openjfx/${_jfxver}/openjfx-${_jfxver}_linux-x64_bin-jmods.zip") +source_aarch64=("jdk-${_jdkver}.tar.gz::https://github.com/adoptium/temurin${_jdkver:0:2}-binaries/releases/download/jdk-${_jdkver//\+/%2B}/OpenJDK${_jdkver:0:2}U-jdk_aarch64_linux_hotspot_${_jdkver//\+/_}.tar.gz" + "openjfx-${_jfxver}.zip::https://download2.gluonhq.com/openjfx/${_jfxver}/openjfx-${_jfxver}_linux-aarch64_bin-jmods.zip") +noextract=("jdk-${_jdkver}.tar.gz" "openjfx-${_jfxver}.zip") +sha256sums=($SOURCES_SHA) +sha256sums_x86_64=('987387933b64b9833846dee373b640440d3e1fd48a04804ec01a6dbf718e8ab8' + 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b') +sha256sums_aarch64=('a9d73e711d967dc44896d4f430f73a68fd33590dabc29a7f2fb9f593425b854c' + 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646') +options=('!strip') + +validpgpkeys=('58117AFA1F85B3EEC154677D615D449FE6E6A235') + +build() { + export JAVA_HOME="${srcdir}/jdk-${_jdkver}" + JMODS_PATH="${srcdir}/openjfx-${_jfxver}-jmods" + #JEP 493 + if ! $(${JAVA_HOME}/bin/jlink --help | grep -q "Linking from run-time image enabled"); then + JMODS_PATH="${JMODS_PATH}:${JAVA_HOME}/jmods:" + fi + + tar xfz "jdk-${_jdkver}.tar.gz" + + mkdir "openjfx-${_jfxver}-jmods" + unzip -j "openjfx-${_jfxver}.zip" \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d "openjfx-${_jfxver}-jmods" + + cd "${srcdir}/${_src_app_dir}" + + mvn -B clean package -DskipTests -Plinux + + cp LICENSE.txt target + cp target/cryptomator-*.jar target/mods + + cd target + + "$JAVA_HOME/bin/jlink" \ + --output runtime \ + --module-path "$JMODS_PATH" \ + --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.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \ + --strip-native-commands \ + --no-header-files \ + --no-man-pages \ + --strip-debug \ + --compress=zip-0 + + ##Note: jpackage does not allow -beta suffixes, have to strip those + "$JAVA_HOME/bin/jpackage" \ + --type app-image \ + --runtime-image runtime \ + --input libs \ + --module-path mods \ + --module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \ + --dest . \ + --name cryptomator \ + --vendor "Skymatic GmbH" \ + --copyright "(C) 2016 - 2026 Skymatic GmbH" \ + --java-options "--enable-preview" \ + --java-options '--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator' \ + --java-options "-Xss5m" \ + --java-options "-Xmx256m" \ + --java-options "-Dfile.encoding=\"utf-8\"" \ + --java-options "-Djava.net.useSystemProxies=true" \ + --java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" \ + --java-options "-Dcryptomator.appVersion=\"${pkgver//_/-}\"" \ + --java-options "-Dcryptomator.buildNumber=\"aur-${pkgrel}\"" \ + --java-options "-Dcryptomator.disableUpdateCheck=true" \ + --java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \ + --java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \ + --java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \ + --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \ + --java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \ + --java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \ + --java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \ + --java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \ + --java-options "-Dcryptomator.showTrayIcon=true" \ + --app-version "${pkgver//_*/}" \ + --verbose +} + +package() { + install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/application-vnd.cryptomator.vault.xml" "${pkgdir}/usr/share/mime/packages/cryptomator-vault.xml" + install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.desktop" "${pkgdir}/usr/share/applications/org.cryptomator.Cryptomator.desktop" + install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator256.png" "${pkgdir}/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png" + install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator512.png" "${pkgdir}/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png" + install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg" + install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.tray.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray.svg" + install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray-unlocked.svg" + install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.tray.svg" "${pkgdir}/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg" + install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg" "${pkgdir}/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg" + + mkdir -p "${pkgdir}/opt/cryptomator/" + cp -R "${srcdir}/${_src_app_dir}/target/cryptomator" "${pkgdir}/opt/" + install -Dm644 "${srcdir}/${_src_app_dir}/target/LICENSE.txt" -t "${pkgdir}/usr/share/licenses/${pkgname}" + + mkdir -p "${pkgdir}/usr/bin" + ln -s "/opt/cryptomator/bin/cryptomator" "${pkgdir}/usr/bin/cryptomator" +} diff --git a/dist/mac/.gitignore b/dist/mac/.gitignore index 81a0afdc2..bd6569978 100644 --- a/dist/mac/.gitignore +++ b/dist/mac/.gitignore @@ -1,2 +1 @@ embedded.provisionprofile -xcuserdata/ diff --git a/dist/mac/DockTilePlugin/CryptomatorDockTilePlugin.swift b/dist/mac/DockTilePlugin/CryptomatorDockTilePlugin.swift deleted file mode 100644 index 1b54ea17d..000000000 --- a/dist/mac/DockTilePlugin/CryptomatorDockTilePlugin.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// CryptomatorDockTilePlugin.swift -// Integrations -// -// Created by Tobias Hagemann on 22.09.25. -// Copyright © 2025 Cryptomator. All rights reserved. -// - -import AppKit - -class CryptomatorDockTilePlugin: NSObject, NSDockTilePlugIn { - func setDockTile(_ dockTile: NSDockTile?) { - guard let dockTile = dockTile, let image = Bundle(for: Self.self).image(forResource: "Cryptomator") else { - return - } - dockTile.contentView = NSImageView(image: image) - dockTile.display() - } -} diff --git a/dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.pbxproj b/dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.pbxproj deleted file mode 100644 index fa7ec5b77..000000000 --- a/dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.pbxproj +++ /dev/null @@ -1,314 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */; }; - 74E08DED2E858532007E665C /* Cryptomator.icns in Resources */ = {isa = PBXBuildFile; fileRef = 74E08DEC2E858532007E665C /* Cryptomator.icns */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 74E08DD92E858467007E665C /* Cryptomator.docktileplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Cryptomator.docktileplugin; sourceTree = BUILT_PRODUCTS_DIR; }; - 74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptomatorDockTilePlugin.swift; sourceTree = ""; }; - 74E08DEC2E858532007E665C /* Cryptomator.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Cryptomator.icns; path = ../resources/Cryptomator.icns; sourceTree = SOURCE_ROOT; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 74E08DD62E858467007E665C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 74E08DD02E858467007E665C = { - isa = PBXGroup; - children = ( - 74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */, - 74E08DEC2E858532007E665C /* Cryptomator.icns */, - 74E08DDA2E858467007E665C /* Products */, - ); - sourceTree = ""; - }; - 74E08DDA2E858467007E665C /* Products */ = { - isa = PBXGroup; - children = ( - 74E08DD92E858467007E665C /* Cryptomator.docktileplugin */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 74E08DD82E858467007E665C /* DockTilePlugin */ = { - isa = PBXNativeTarget; - buildConfigurationList = 74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */; - buildPhases = ( - 74E08DD52E858467007E665C /* Sources */, - 74E08DD62E858467007E665C /* Frameworks */, - 74E08DD72E858467007E665C /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = DockTilePlugin; - packageProductDependencies = ( - ); - productName = DockTilePlugin; - productReference = 74E08DD92E858467007E665C /* Cryptomator.docktileplugin */; - productType = "com.apple.product-type.bundle"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 74E08DD12E858467007E665C /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastUpgradeCheck = 2600; - ORGANIZATIONNAME = Cryptomator; - TargetAttributes = { - 74E08DD82E858467007E665C = { - CreatedOnToolsVersion = 26.0.1; - }; - }; - }; - buildConfigurationList = 74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 74E08DD02E858467007E665C; - minimizedProjectReferenceProxies = 1; - preferredProjectObjectVersion = 77; - productRefGroup = 74E08DDA2E858467007E665C /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 74E08DD82E858467007E665C /* DockTilePlugin */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 74E08DD72E858467007E665C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74E08DED2E858532007E665C /* Cryptomator.icns in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 74E08DD52E858467007E665C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 74E08DDB2E858467007E665C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = YZQJQUHA3L; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 11.5; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 74E08DDC2E858467007E665C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = YZQJQUHA3L; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 11.5; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = macosx; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 74E08DDE2E858467007E665C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved."; - INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin; - PRODUCT_NAME = Cryptomator; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - WRAPPER_EXTENSION = docktileplugin; - }; - name = Debug; - }; - 74E08DDF2E858467007E665C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved."; - INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin; - PRODUCT_NAME = Cryptomator; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - WRAPPER_EXTENSION = docktileplugin; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 74E08DDB2E858467007E665C /* Debug */, - 74E08DDC2E858467007E665C /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 74E08DDE2E858467007E665C /* Debug */, - 74E08DDF2E858467007E665C /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 74E08DD12E858467007E665C /* Project object */; -} diff --git a/dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a62..000000000 --- a/dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/xcshareddata/xcschemes/DockTilePlugin.xcscheme b/dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/xcshareddata/xcschemes/DockTilePlugin.xcscheme deleted file mode 100644 index 7d86bdcc5..000000000 --- a/dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj/xcshareddata/xcschemes/DockTilePlugin.xcscheme +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dist/mac/dmg/build.sh b/dist/mac/dmg/build.sh index a9cdd6c46..3a8ac2d70 100755 --- a/dist/mac/dmg/build.sh +++ b/dist/mac/dmg/build.sh @@ -130,23 +130,11 @@ ${JAVA_HOME}/bin/jpackage \ # transform app dir cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/ +cp ../resources/Assets.car ${APP_NAME}.app/Contents/Resources/ sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/ -# build and install dock tile plugin -echo "Building and installing Cryptomator.docktileplugin..." -DERIVED_DATA_PATH=../DockTilePlugin/build -xcodebuild -project ../DockTilePlugin/DockTilePlugin.xcodeproj \ - -scheme DockTilePlugin \ - -configuration Release \ - -derivedDataPath ${DERIVED_DATA_PATH} \ - -quiet \ - clean build -mkdir -p ${APP_NAME}.app/Contents/PlugIns -cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin ${APP_NAME}.app/Contents/PlugIns/ -rm -rf ${DERIVED_DATA_PATH} - # generate license mvn -B -f../../../pom.xml license:add-third-party \ -Dlicense.thirdPartyFilename=license.rtf \ diff --git a/dist/mac/resources/Assets.car b/dist/mac/resources/Assets.car new file mode 100644 index 000000000..26afcec2d Binary files /dev/null and b/dist/mac/resources/Assets.car differ diff --git a/dist/mac/resources/Info.plist b/dist/mac/resources/Info.plist index 00487369c..2d5b65247 100644 --- a/dist/mac/resources/Info.plist +++ b/dist/mac/resources/Info.plist @@ -12,6 +12,8 @@ Cryptomator CFBundleIconFile Cryptomator.icns + CFBundleIconName + Cryptomator CFBundleIdentifier org.cryptomator CFBundleInfoDictionaryVersion @@ -117,8 +119,5 @@ NSSupportsAutomaticGraphicsSwitching - - NSDockTilePlugIn - Cryptomator.docktileplugin diff --git a/pom.xml b/pom.xml index c3e724ba6..698d7a6c0 100644 --- a/pom.xml +++ b/pom.xml @@ -33,45 +33,46 @@ org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents - 2.10.0-beta3 + 2.10.0 2.2.2 1.8.0-beta1 1.6.0 1.5.0-beta3 1.7.0-beta4 - 6.0.0 + 6.0.1 3.0.1 + 1.2.12 3.2.3 3.20.0 - 2.59.1 + 2.59.2 2.2 - 2.21.0 + 2.21.1 25.0.2 - 4.5.0 + 4.5.1 10.5 - 1.5.31 + 1.5.32 2.0.17 0.8.1 1.9.0 - 5.13.4 - 5.20.0 + 6.0.3 + 5.22.0 3.0 - 26.0.2-1 - 12.1.5 + 26.1.0 + 12.2.0 0.8.14 - 2.7.0 - 1.4.0 - 3.14.1 - 3.3.1 - 3.8.1 - 3.5.4 - 3.4.2 + 2.7.1 + 1.5.1 + 3.15.0 + 3.5.0 + 3.10.0 + 3.5.3 + 3.5.0 @@ -93,6 +94,11 @@ + + org.cryptomator + webdav-nio-adapter-servlet + ${cryptomator.webdav-servlet.version} + org.cryptomator cryptolib diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 0ea231bdd..2671bcae1 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -13,6 +13,7 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider; import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider; import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider; +import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.integrations.tray.TrayMenuController; import org.cryptomator.integrations.uiappearance.UiAppearanceProvider; import org.cryptomator.logging.LogbackConfiguratorFactory; @@ -20,6 +21,7 @@ import org.cryptomator.networking.SSLContextProvider; import org.cryptomator.networking.SSLContextWithMacKeychain; import org.cryptomator.networking.SSLContextWithPKCS12TrustStore; import org.cryptomator.networking.SSLContextWithWindowsCertStore; +import org.cryptomator.ui.fxapp.JfxRevealPathService; import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider; import org.cryptomator.ui.traymenu.AwtTrayMenuController; @@ -64,6 +66,7 @@ open module org.cryptomator.desktop { uses org.cryptomator.event.NotificationHandler; provides UiAppearanceProvider with JfxUiAppearanceProvider; + provides RevealPathService with JfxRevealPathService; provides TrayMenuController with AwtTrayMenuController; provides Configurator with LogbackConfiguratorFactory; provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore; diff --git a/src/main/java/org/cryptomator/common/CommonsModule.java b/src/main/java/org/cryptomator/common/CommonsModule.java index 739c358a9..bcc5709af 100644 --- a/src/main/java/org/cryptomator/common/CommonsModule.java +++ b/src/main/java/org/cryptomator/common/CommonsModule.java @@ -22,8 +22,6 @@ import javax.inject.Named; import javax.inject.Singleton; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.Comparator; -import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.SynchronousQueue; @@ -76,8 +74,8 @@ public abstract class CommonsModule { @Provides @Singleton - static Optional provideRevealPathService() { - return RevealPathService.get().findFirst(); + static RevealPathService provideRevealPathService() { + return RevealPathService.get().findFirst().orElseThrow(); } diff --git a/src/main/java/org/cryptomator/common/SubstitutingProperties.java b/src/main/java/org/cryptomator/common/SubstitutingProperties.java index 0536e3554..3120abde8 100644 --- a/src/main/java/org/cryptomator/common/SubstitutingProperties.java +++ b/src/main/java/org/cryptomator/common/SubstitutingProperties.java @@ -1,7 +1,7 @@ package org.cryptomator.common; import org.jetbrains.annotations.VisibleForTesting; -import org.slf4j.LoggerFactory; +import org.slf4j.Logger; import java.util.Map; import java.util.Properties; @@ -13,10 +13,12 @@ public class SubstitutingProperties extends PropertiesDecorator { private static final Pattern TEMPLATE = Pattern.compile("@\\{(\\w+)}"); private final Map env; + private final Logger logger; - public SubstitutingProperties(Properties props, Map systemEnvironment) { + public SubstitutingProperties(Properties props, Map systemEnvironment, Logger logger) { super(props); this.env = systemEnvironment; + this.logger = logger; } @Override @@ -44,7 +46,7 @@ public class SubstitutingProperties extends PropertiesDecorator { case "localappdata" -> resolveFrom("LOCALAPPDATA", Source.ENV); case "userhome" -> resolveFrom("user.home", Source.PROPS); default -> { - LoggerFactory.getLogger(SubstitutingProperties.class).warn("Unknown variable {} in property value {}.", match.group(), value); + logger.warn("Unknown variable {} in property value {}.", match.group(), value); yield match.group(); } }); @@ -56,7 +58,7 @@ public class SubstitutingProperties extends PropertiesDecorator { case PROPS -> delegate.getProperty(key); }; if (val == null) { - LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src); + logger.warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src); return ""; } else { return Matcher.quoteReplacement(val); diff --git a/src/main/java/org/cryptomator/launcher/Cryptomator.java b/src/main/java/org/cryptomator/launcher/Cryptomator.java index 4a6d0750d..d12438e37 100644 --- a/src/main/java/org/cryptomator/launcher/Cryptomator.java +++ b/src/main/java/org/cryptomator/launcher/Cryptomator.java @@ -36,7 +36,7 @@ public class Cryptomator { static { var adminProps = AdminPropertiesFactory.create(); - var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv()); + var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv(), EventualLogger.INSTANCE); System.setProperties(lazyProcessedProps); CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME); LOG = LoggerFactory.getLogger(Cryptomator.class); @@ -65,7 +65,6 @@ public class Cryptomator { } public static void main(String[] args) { - EventualLogger.INSTANCE.drainTo(LOG); var printVersion = Optional.ofNullable(args) // .stream() //Streams either one element (the args-array) or zero elements .flatMap(Arrays::stream) // @@ -91,10 +90,11 @@ public class Cryptomator { * @return Nonzero exit code in case of an error. */ private int run(String[] args) { + debugMode.initialize(); + EventualLogger.INSTANCE.drainTo(LOG); env.log(); LOG.debug("Dagger graph initialized after {}ms", System.currentTimeMillis() - STARTUP_TIME); LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); - debugMode.initialize(); supportedLanguages.applyPreferred(); changeDefaultSSLContext(); /* diff --git a/src/main/java/org/cryptomator/networking/SSLContextDifferentTrustStoreBase.java b/src/main/java/org/cryptomator/networking/SSLContextDifferentTrustStoreBase.java index 87e1b82ef..5da47597a 100644 --- a/src/main/java/org/cryptomator/networking/SSLContextDifferentTrustStoreBase.java +++ b/src/main/java/org/cryptomator/networking/SSLContextDifferentTrustStoreBase.java @@ -18,7 +18,7 @@ abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider { public SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException { try { KeyStore truststore = getTruststore(); - truststore.load(null, null); + ensureLoaded(truststore); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(truststore); @@ -30,4 +30,13 @@ abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider { throw new SSLContextBuildException(e); } } + + static void ensureLoaded(KeyStore truststore) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { + try { + truststore.aliases(); + } catch (KeyStoreException e) { + // Not initialized yet (e.g. custom KeyStore SPI); initialize without replacing preloaded stores. + truststore.load(null, null); + } + } } diff --git a/src/main/java/org/cryptomator/networking/SSLContextWithWindowsCertStore.java b/src/main/java/org/cryptomator/networking/SSLContextWithWindowsCertStore.java index 0f1ea04b5..83815b611 100644 --- a/src/main/java/org/cryptomator/networking/SSLContextWithWindowsCertStore.java +++ b/src/main/java/org/cryptomator/networking/SSLContextWithWindowsCertStore.java @@ -1,21 +1,73 @@ package org.cryptomator.networking; +import org.cryptomator.common.Nullable; import org.cryptomator.integrations.common.OperatingSystem; +import org.jetbrains.annotations.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.cert.CertificateException; +import java.util.List; +import java.util.Properties; /** - * SSLContextProvider for Windows using the Windows certificate store as trust store + * SSLContextProvider for Windows using the Windows certificate store as trust store and the bundled JDK cacerts as fallback *

* In order to work, the jdk.crypto.mscapi jmod is needed */ @OperatingSystem(OperatingSystem.Value.WINDOWS) public class SSLContextWithWindowsCertStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider { + private static final Logger LOG = LoggerFactory.getLogger(SSLContextWithWindowsCertStore.class); + private static final String DEFAULT_TRUSTSTORE_PASSWORD = "changeit"; //default JDK cacerts password + @Override - KeyStore getTruststore() throws KeyStoreException { - return KeyStore.getInstance("WINDOWS-ROOT"); + KeyStore getTruststore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { + var windowsKeyStore = KeyStore.getInstance("WINDOWS-ROOT"); + var jdkKeyStore = getShippedCaCertsStore(); + if (jdkKeyStore == null) { + return windowsKeyStore; + } + + ensureLoaded(windowsKeyStore); + ensureLoaded(jdkKeyStore); + try { + CombinedKeyStoreSpi spi = CombinedKeyStoreSpi.create(windowsKeyStore, jdkKeyStore); + Provider dummyProvider = new Provider("CombinedKeyStoreProvider", "1.0", "Provides a combined, read-only KeyStore") {}; + return new KeyStore(spi, dummyProvider, "CombinedKeyStoreProvider") {}; + } catch (IllegalArgumentException e) { + throw new KeyStoreException(e); + } + } + + @Nullable + KeyStore getShippedCaCertsStore() { + return getCaCertsStoreByProperties(System.getProperties()); + } + + //for testability + @VisibleForTesting + @Nullable + KeyStore getCaCertsStoreByProperties(Properties props) { + var javaHome = Path.of(props.getProperty("java.home")); + var trustStorePassword = props.getProperty("javax.net.ssl.trustStorePassword", DEFAULT_TRUSTSTORE_PASSWORD).toCharArray(); + for (var candidate : List.of(javaHome.resolve("lib/security/cacerts"), javaHome.resolve("conf/security/cacerts"))) { + try { + if (Files.isRegularFile(candidate)) { + return KeyStore.getInstance(candidate.toFile(), trustStorePassword); + } + } catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException e) { + LOG.info("Unable to load fallback cacerts {} file. Skipping fallback.", candidate, e); + } + } + return null; } } diff --git a/src/main/java/org/cryptomator/ui/common/VaultService.java b/src/main/java/org/cryptomator/ui/common/VaultService.java index 95129f6ee..d02064caa 100644 --- a/src/main/java/org/cryptomator/ui/common/VaultService.java +++ b/src/main/java/org/cryptomator/ui/common/VaultService.java @@ -1,17 +1,16 @@ package org.cryptomator.ui.common; -import dagger.Lazy; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.integrations.mount.Mountpoint; import org.cryptomator.integrations.mount.UnmountFailedException; +import org.cryptomator.integrations.revealpath.RevealFailedException; +import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javafx.application.Application; -import javafx.application.HostServices; import javafx.concurrent.Task; import javafx.stage.Stage; import java.io.IOException; @@ -28,12 +27,12 @@ public class VaultService { private static final Logger LOG = LoggerFactory.getLogger(VaultService.class); - private final Lazy application; + private final RevealPathService revealPathService; private final ExecutorService executorService; @Inject - public VaultService(Lazy application, ExecutorService executorService) { - this.application = application; + public VaultService(RevealPathService revealPathService, ExecutorService executorService) { + this.revealPathService = revealPathService; this.executorService = executorService; } @@ -47,9 +46,9 @@ public class VaultService { * @param vault The vault to reveal */ public Task createRevealTask(Vault vault) { - Task task = new RevealVaultTask(vault, application.get().getHostServices()); - task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayName())); - task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), evt.getSource().getException())); + Task task = new RevealVaultTask(vault, revealPathService); + task.setOnSucceeded(_ -> LOG.info("Revealed {}", vault.getDisplayName())); + task.setOnFailed(evt -> LOG.warn("Failed to reveal {}", vault.getDisplayName(), evt.getSource().getException())); return task; } @@ -110,19 +109,18 @@ public class VaultService { private static class RevealVaultTask extends Task { private final Vault vault; - private final HostServices hostServices; + private final RevealPathService rs; - public RevealVaultTask(Vault vault, HostServices hostServices) { + public RevealVaultTask(Vault vault, RevealPathService revealPathService) { this.vault = vault; - this.hostServices = hostServices; - setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), getException())); + this.rs = revealPathService; } @Override - protected Vault call() { + protected Vault call() throws RevealFailedException { switch (vault.getMountPoint()) { case null -> LOG.warn("Not currently mounted"); - case Mountpoint.WithPath m -> hostServices.showDocument(m.uri().toString()); + case Mountpoint.WithPath m -> rs.reveal(m.path()); case Mountpoint.WithUri m -> LOG.info("Vault mounted at {}", m.uri()); // TODO show in UI? } return vault; diff --git a/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java b/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java index 2327cc262..46bebbdb8 100644 --- a/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java +++ b/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java @@ -2,18 +2,17 @@ package org.cryptomator.ui.eventview; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Constants; -import org.cryptomator.cryptofs.event.FileIsInUseEvent; -import org.cryptomator.event.FSEventBucket; -import org.cryptomator.event.FSEventBucketContent; -import org.cryptomator.event.FileSystemEventAggregator; import org.cryptomator.common.Nullable; import org.cryptomator.common.ObservableUtil; -import org.cryptomator.cryptofs.CryptoPath; import org.cryptomator.cryptofs.event.BrokenDirFileEvent; import org.cryptomator.cryptofs.event.BrokenFileNodeEvent; import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent; import org.cryptomator.cryptofs.event.ConflictResolvedEvent; import org.cryptomator.cryptofs.event.DecryptionFailedEvent; +import org.cryptomator.cryptofs.event.FileIsInUseEvent; +import org.cryptomator.event.FSEventBucket; +import org.cryptomator.event.FSEventBucketContent; +import org.cryptomator.event.FileSystemEventAggregator; import org.cryptomator.integrations.revealpath.RevealFailedException; import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.ui.common.FxController; @@ -46,7 +45,6 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.util.Map; -import java.util.Optional; import java.util.ResourceBundle; import java.util.function.Function; @@ -57,7 +55,6 @@ public class EventListCellController implements FxController { private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault()); private final FileSystemEventAggregator fileSystemEventAggregator; - @Nullable private final RevealPathService revealService; private final ResourceBundle resourceBundle; private final ObjectProperty> eventEntry; @@ -82,15 +79,17 @@ public class EventListCellController implements FxController { Button eventActionsButton; @Inject - public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator, Optional revealService, ResourceBundle resourceBundle) { + public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator, + RevealPathService revealService, + ResourceBundle resourceBundle) { this.fileSystemEventAggregator = fileSystemEventAggregator; - this.revealService = revealService.orElseGet(() -> null); + this.revealService = revealService; this.resourceBundle = resourceBundle; this.eventEntry = new SimpleObjectProperty<>(null); this.eventMessage = new SimpleStringProperty(); this.eventDescription = new SimpleStringProperty(); this.eventIcon = new SimpleObjectProperty<>(); - this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1? "" : "("+ e.getValue().count() +")", ""); + this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1 ? "" : "(" + e.getValue().count() + ")", ""); this.vaultUnlocked = ObservableUtil.mapWithDefault(eventEntry.flatMap(e -> e.getKey().vault().unlockedProperty()), Function.identity(), false); this.readableTime = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_TIME_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), ""); this.readableDate = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_DATE_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), ""); @@ -136,13 +135,8 @@ public class EventListCellController implements FxController { eventMessage.setValue(resourceBundle.getString("eventView.entry.inUse.message")); var indexFileName = fiiue.cleartextPath().lastIndexOf("/"); eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1)); - if (revealService != null) { - addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath()))); - addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath())); - } else { - addLocalizedAction("eventView.entry.inUse.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(fiiue.cleartextPath()).toString())); - addLocalizedAction("eventView.entry.inUse.copyEncrypted", () -> copyToClipboard(fiiue.ciphertextPath().toString())); - } + addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath()))); + addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath())); var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR); var user = userAndDevice[0]; @@ -156,11 +150,7 @@ public class EventListCellController implements FxController { eventIcon.setValue(FontAwesome5Icon.TIMES); eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message")); eventDescription.setValue(bfe.ciphertextPath().getFileName().toString()); - if (revealService != null) { - addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath())); - } else { - addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString())); - } + addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath())); addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString())); } @@ -168,46 +158,29 @@ public class EventListCellController implements FxController { eventIcon.setValue(FontAwesome5Icon.CHECK); eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message")); eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString()); - if (revealService != null) { - addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath()))); - } else { - addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString())); - } + addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath()))); } private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) { eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT); eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message")); eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString()); - if (revealService != null) { - addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath()))); - addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath())); - } else { - addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString())); - addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString())); - } + addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath()))); + addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath())); } private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) { eventIcon.setValue(FontAwesome5Icon.BAN); eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message")); eventDescription.setValue(dfe.ciphertextPath().getFileName().toString()); - if (revealService != null) { - addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath())); - } else { - addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString())); - } + addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath())); } private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) { eventIcon.setValue(FontAwesome5Icon.TIMES); eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message")); eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString()); - if (revealService != null) { - addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath())); - } else { - addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString())); - } + addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath())); } private void addLocalizedAction(String localizationKey, Runnable action) { @@ -270,7 +243,7 @@ public class EventListCellController implements FxController { } var mountPoint = v.getMountPoint().uri().getPath(); - if(SystemUtils.IS_OS_WINDOWS) { + if (SystemUtils.IS_OS_WINDOWS) { mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors } return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index 7fb5b523b..a6ae45efc 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -10,16 +10,20 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; +import javafx.application.Application; import javafx.application.Platform; import java.time.Duration; import java.time.Instant; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; @FxApplicationScoped public class FxApplication { private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class); + static final AtomicReference INSTANCE = new AtomicReference<>(); + private final long startupTime; private final Environment environment; private final Settings settings; @@ -33,7 +37,8 @@ public class FxApplication { private final FxNotificationManager notificationManager; @Inject - FxApplication(@Named("startupTime") long startupTime, // + FxApplication(Application fxApp, + @Named("startupTime") long startupTime, // Environment environment, // Settings settings, // AppLaunchEventHandler launchEventHandler, // @@ -55,6 +60,8 @@ public class FxApplication { this.autoUnlocker = autoUnlocker; this.fxFSEventList = fxFSEventList; this.notificationManager = notificationManager; + + INSTANCE.set(fxApp); } public void start() { diff --git a/src/main/java/org/cryptomator/ui/fxapp/JfxRevealPathService.java b/src/main/java/org/cryptomator/ui/fxapp/JfxRevealPathService.java new file mode 100644 index 000000000..1271f42e8 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/fxapp/JfxRevealPathService.java @@ -0,0 +1,37 @@ +package org.cryptomator.ui.fxapp; + +import org.cryptomator.integrations.common.DisplayName; +import org.cryptomator.integrations.common.OperatingSystem; +import org.cryptomator.integrations.common.Priority; +import org.cryptomator.integrations.revealpath.RevealFailedException; +import org.cryptomator.integrations.revealpath.RevealPathService; + +import java.nio.file.Path; + +/** + * A {@link RevealPathService} service implementation using the JavaFX {@link javafx.application.HostServices#showDocument(String)} to reveal documents. + *

+ * Internally the HostServices class uses GTK on Linux. + * + * @implNote {@link #reveal(Path)} only succeeds when the class {@link FxApplication} is initialized. + */ +@DisplayName("JavaFX HostServices (GTK)") +@OperatingSystem(OperatingSystem.Value.LINUX) +@Priority(10) +public class JfxRevealPathService implements RevealPathService { + + @Override + public void reveal(Path p) throws RevealFailedException { + var fxApp = FxApplication.INSTANCE.get(); + if (fxApp != null) { + fxApp.getHostServices().showDocument(p.toUri().toString()); + } else { + throw new RevealFailedException("JavaFX Application not initialized"); + } + } + + @Override + public boolean isSupported() { + return true; + } +} diff --git a/src/main/java/org/cryptomator/ui/health/ReportWriter.java b/src/main/java/org/cryptomator/ui/health/ReportWriter.java index 18b785e91..a61aab868 100644 --- a/src/main/java/org/cryptomator/ui/health/ReportWriter.java +++ b/src/main/java/org/cryptomator/ui/health/ReportWriter.java @@ -4,9 +4,12 @@ import com.google.common.base.Throwables; import org.cryptomator.common.Environment; import org.cryptomator.common.vaults.Vault; import org.cryptomator.cryptofs.VaultConfig; +import org.cryptomator.integrations.revealpath.RevealFailedException; +import org.cryptomator.integrations.revealpath.RevealPathService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javafx.application.Application; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; @@ -25,6 +28,7 @@ import java.util.stream.Collectors; @HealthCheckScoped public class ReportWriter { + private static final Logger LOG = LoggerFactory.getLogger(ReportWriter.class); private static final String REPORT_HEADER = """ ******************************************* * Cryptomator Vault Health Report * @@ -43,14 +47,14 @@ public class ReportWriter { private final Vault vault; private final VaultConfig vaultConfig; - private final Application application; + private final RevealPathService revealPathService; private final Path exportDestination; @Inject - public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference vaultConfigRef, Application application, Environment env) { + public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference vaultConfigRef, RevealPathService revealPathService, Environment env) { this.vault = vault; this.vaultConfig = Objects.requireNonNull(vaultConfigRef.get()); - this.application = application; + this.revealPathService = revealPathService; this.exportDestination = env.getLogDir().orElse(Path.of(System.getProperty("user.home"))).resolve("healthReport_" + vault.getDisplayName() + "_" + TIME_STAMP.format(Instant.now()) + ".log"); } @@ -92,7 +96,11 @@ public class ReportWriter { } private void reveal() { - application.getHostServices().showDocument(exportDestination.getParent().toUri().toString()); + try { + revealPathService.reveal(exportDestination.getParent()); + } catch (RevealFailedException e) { + LOG.warn("Failed to reveal export destination location of report", e); + } } } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java index be4f7f78c..7151572a4 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java @@ -2,14 +2,17 @@ package org.cryptomator.ui.mainwindow; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; +import org.cryptomator.integrations.revealpath.RevealFailedException; +import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.AutoAnimator; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.controls.FontAwesome5Icon; import org.cryptomator.ui.controls.FontAwesome5IconView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javafx.application.Application; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; @@ -19,10 +22,12 @@ import javafx.fxml.FXML; @MainWindowScoped public class VaultDetailController implements FxController { + private static final Logger LOG = LoggerFactory.getLogger(VaultDetailController.class); + private final ReadOnlyObjectProperty vault; - private final Application application; private final ObservableValue glyph; private final BooleanBinding anyVaultSelected; + private final RevealPathService revealPathService; private AutoAnimator spinAnimation; @@ -31,11 +36,11 @@ public class VaultDetailController implements FxController { @Inject - VaultDetailController(ObjectProperty vault, Application application) { + VaultDetailController(ObjectProperty vault, RevealPathService revealPathService) { this.vault = vault; - this.application = application; this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState); this.anyVaultSelected = vault.isNotNull(); + this.revealPathService = revealPathService; } public void initialize() { @@ -61,7 +66,11 @@ public class VaultDetailController implements FxController { @FXML public void revealStorageLocation() { - application.getHostServices().showDocument(vault.get().getPath().toUri().toString()); + try { + revealPathService.reveal(vault.get().getPath()); + } catch (RevealFailedException e) { + LOG.warn("Failed to reveal vault storage location", e); + } } /* Observable Properties */ diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java index 42a8fda7e..742e3baaa 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java @@ -21,7 +21,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyObjectProperty; @@ -31,7 +30,6 @@ import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; -import javafx.scene.input.DataFormat; import javafx.scene.input.DragEvent; import javafx.scene.input.TransferMode; import javafx.stage.FileChooser; @@ -40,12 +38,8 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.ResourceBundle; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; @@ -60,7 +54,7 @@ public class VaultDetailUnlockedController implements FxController { private final VaultService vaultService; private final WrongFileAlertComponent.Builder wrongFileAlert; private final Stage mainWindow; - private final Optional revealPathService; + private final RevealPathService revealPathService; private final DecryptNameComponent.Factory decryptNameWindowFactory; private final ResourceBundle resourceBundle; private final LoadingCache vaultStats; @@ -84,7 +78,7 @@ public class VaultDetailUnlockedController implements FxController { VaultStatisticsComponent.Builder vaultStatsBuilder, // WrongFileAlertComponent.Builder wrongFileAlert, // @MainWindow Stage mainWindow, // - Optional revealPathService, // + RevealPathService revealPathService, // DecryptNameComponent.Factory decryptNameWindowFactory, // ResourceBundle resourceBundle) { this.vault = vault; @@ -111,7 +105,7 @@ public class VaultDetailUnlockedController implements FxController { public void initialize() { revealEncryptedDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverLocateEncrypted)); - revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealOrCopyPaths)); + revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealPaths)); revealEncryptedDropZone.setOnDragExited(_ -> draggingOverLocateEncrypted.setValue(false)); decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName)); @@ -156,7 +150,7 @@ public class VaultDetailUnlockedController implements FxController { if (cleartextFile != null) { var ciphertextPath = getCiphertextPath(cleartextFile.toPath()); if (ciphertextPath != null) { - revealOrCopyPaths(List.of(ciphertextPath)); + revealPaths(List.of(ciphertextPath)); } } } @@ -188,34 +182,18 @@ public class VaultDetailUnlockedController implements FxController { } } - private void revealOrCopyPaths(List paths) { - revealPathService.ifPresentOrElse(svc -> revealPaths(svc, paths), () -> { - LOG.warn("No service provider to reveal files found."); - copyPathsToClipboard(paths); - }); - } - - private void revealPaths(RevealPathService service, List paths) { + private void revealPaths(List paths) { paths.forEach(path -> { try { LOG.debug("Revealing {}", path); - service.reveal(path); + revealPathService.reveal(path); } catch (RevealFailedException e) { + //TODO: show popup in ui LOG.error("Revealing ciphertext file failed.", e); } }); } - private void copyPathsToClipboard(List paths) { - StringBuilder clipboardString = new StringBuilder(); - paths.forEach(p -> clipboardString.append(p.toString()).append("\n")); - Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, clipboardString.toString())); - ciphertextPathsCopied.setValue(true); - CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> { - ciphertextPathsCopied.set(false); - }); - } - private VaultStatisticsComponent buildVaultStats(Vault vault) { return vaultStatsBuilder.vault(vault).build(); } diff --git a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java index 088bad0dd..c59d03846 100644 --- a/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java @@ -10,13 +10,14 @@ import org.cryptomator.integrations.common.NamedServiceProvider; import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.cryptomator.integrations.quickaccess.QuickAccessService; +import org.cryptomator.integrations.revealpath.RevealFailedException; +import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javafx.application.Application; import javafx.beans.Observable; import javafx.beans.binding.Bindings; import javafx.fxml.FXML; @@ -41,7 +42,7 @@ public class GeneralPreferencesController implements FxController { private final Settings settings; private final Optional autoStartProvider; private final List quickAccessServices; - private final Application application; + private final RevealPathService revealPathService; private final Environment environment; private final List keychainAccessProviders; private final KeychainManager keychain; @@ -60,9 +61,15 @@ public class GeneralPreferencesController implements FxController { private CompletionStage keychainMigrations = CompletableFuture.completedFuture(null); @Inject - GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional autoStartProvider, // - List keychainAccessProviders, KeychainManager keychain, Application application, // - Environment environment, FxApplicationWindows appWindows, ExecutorService backgroundExecutor) { + GeneralPreferencesController(@PreferencesWindow Stage window, // + Settings settings, // + Optional autoStartProvider, // + List keychainAccessProviders, // + KeychainManager keychain, // + RevealPathService revealPathService, // + Environment environment, // + FxApplicationWindows appWindows, // + ExecutorService backgroundExecutor) { this.window = window; this.settings = settings; this.autoStartProvider = autoStartProvider; @@ -70,7 +77,7 @@ public class GeneralPreferencesController implements FxController { this.keychain = keychain; this.backgroundExecutor = backgroundExecutor; this.quickAccessServices = QuickAccessService.get().toList(); - this.application = application; + this.revealPathService = revealPathService; this.environment = environment; this.appWindows = appWindows; } @@ -148,7 +155,11 @@ public class GeneralPreferencesController implements FxController { @FXML public void showLogfileDirectory() { - environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString())); + try { + revealPathService.reveal(environment.getLogDir().orElseThrow()); + } catch (RevealFailedException e) { + LOG.warn("Failed to reveal log files directory.", e); + } } /* Helper classes */ @@ -196,4 +207,5 @@ public class GeneralPreferencesController implements FxController { } } } + } diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index 89c03800e..c21630bde 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -3,18 +3,19 @@ package org.cryptomator.ui.preferences; import org.cryptomator.common.Environment; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.vaults.Vault; +import org.cryptomator.integrations.revealpath.RevealFailedException; +import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.integrations.update.UpdateStep; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.VaultService; -import org.cryptomator.updater.UpdateChecker; import org.cryptomator.updater.FallbackUpdateInfo; +import org.cryptomator.updater.UpdateChecker; import org.cryptomator.updater.UpdateService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.animation.PauseTransition; -import javafx.application.Application; import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; @@ -49,7 +50,7 @@ public class UpdatesPreferencesController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(UpdatesPreferencesController.class); private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault()); - private final Application application; + private final RevealPathService revealPathService; private final Environment environment; private final ResourceBundle resourceBundle; private final Settings settings; @@ -72,8 +73,8 @@ public class UpdatesPreferencesController implements FxController { public CheckBox checkForUpdatesCheckbox; @Inject - UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList vaults, VaultService vaultService) { - this.application = application; + UpdatesPreferencesController(RevealPathService revealPathService, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList vaults, VaultService vaultService) { + this.revealPathService = revealPathService; this.environment = environment; this.resourceBundle = resourceBundle; this.settings = settings; @@ -106,9 +107,14 @@ public class UpdatesPreferencesController implements FxController { updateService.setOnFailed(this::updateFailed); } + @FXML public void showLogfileDirectory() { - environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString())); + try { + revealPathService.reveal(environment.getLogDir().orElseThrow()); + } catch (RevealFailedException e) { + LOG.warn("Failed to reveal log files directory.", e); + } } @FXML diff --git a/src/main/resources/fxml/notification.fxml b/src/main/resources/fxml/notification.fxml index 74ed9b18a..84dd2fa26 100644 --- a/src/main/resources/fxml/notification.fxml +++ b/src/main/resources/fxml/notification.fxml @@ -15,7 +15,7 @@ @@ -65,7 +65,7 @@