diff --git a/.github/actions/win-sign-action/action.yml b/.github/actions/win-sign-action/action.yml index b4c2eaa73..518de6dff 100644 --- a/.github/actions/win-sign-action/action.yml +++ b/.github/actions/win-sign-action/action.yml @@ -48,7 +48,7 @@ runs: echo "client-secret=${{ inputs.client-secret }}" >> "$GITHUB_OUTPUT" shell: bash - name: Sign DLLs with Azure Trusted Signing - uses: azure/artifact-signing-action@87c2e83e6868da99d3380aa309851b32ed9a8346 # v1.1.0 + uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0 with: files-folder: ${{ inputs.base-dir }} files-folder-filter: ${{ inputs.file-extensions }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e47dae730..948b82782 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,51 +3,19 @@ updates: - package-ecosystem: "maven" directory: "/" schedule: - interval: "weekly" - day: "monday" - time: "06:00" - timezone: "Etc/UTC" + interval: "monthly" ignore: - dependency-name: "org.cryptomator:integrations-api" versions: ["2.0.0-alpha1"] - dependency-name: "jakarta.inject:jakarta.inject-api" 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" ] + - dependency-name: "com.fasterxml.jackson.*" + versions: [ "[2.22,)" ] # 2.21.x is LTS, see https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.21 groups: - java-test-dependencies: - patterns: - - "org.junit.jupiter:*" - - "org.mockito:*" - - "org.hamcrest:*" - - "com.google.jimfs:jimfs" - maven-build-plugins: - patterns: - - "org.apache.maven.plugins:*" - - "org.jacoco:jacoco-maven-plugin" - - "org.owasp:dependency-check-maven" - - "me.fabriciorby:maven-surefire-junit5-tree-reporter" - - "org.codehaus.mojo:license-maven-plugin" - javafx: - patterns: - - "org.openjfx:*" - java-production-dependencies: + maven-dependencies: patterns: - "*" - exclude-patterns: - - "org.openjfx:*" - - "org.apache.maven.plugins:*" - - "org.jacoco:jacoco-maven-plugin" - - "org.owasp:dependency-check-maven" - - "me.fabriciorby:maven-surefire-junit5-tree-reporter" - - "org.codehaus.mojo:license-maven-plugin" - - "org.junit.jupiter:*" - - "org.mockito:*" - - "org.hamcrest:*" - - "com.google.jimfs:jimfs" - package-ecosystem: "github-actions" directory: "/" # even for `.github/workflows` diff --git a/.github/release-body.md.template b/.github/release-body.md.template new file mode 100644 index 000000000..e0a351603 --- /dev/null +++ b/.github/release-body.md.template @@ -0,0 +1,34 @@ + +> [!WARN] +> 🚧 DO NOT EDIT 🚧 +> +> The [builds are still running](https://github.com/cryptomator/cryptomator/actions/workflows/create-release.yml). +> This banner will be replaced after the builds are finished. + + + + +For a comprehensive view of changes, read the [CHANGELOG](https://github.com/cryptomator/cryptomator/blob/$VERSION/CHANGELOG.md). + +--- + +💾 SHA-256 checksums of release artifacts: +``` +$TARBALL +$EXE +$MSI +$DMG_x64 +$DMG_arm64 +$APPIMAGE_x86_64 +$APPIMAGE_aarch64 +``` + +> [!TIP] +> You can verify the GPG signature of all assets using our public key: [`5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a). + + + diff --git a/.github/workflows/RELEASE.md b/.github/workflows/RELEASE.md new file mode 100644 index 000000000..09ee58604 --- /dev/null +++ b/.github/workflows/RELEASE.md @@ -0,0 +1,189 @@ +# Cryptomator Release Workflow + +This document describes the automated release pipeline defined in [`draft-release.yml`](draft-release.yml) and [`post-publish.yml`](post-publish.yml). + +## Overview + +The release process has two phases: + +1. **Draft phase** (`draft-release.yml`) -- triggered by pushing a signed git tag. Compiles, tests, builds platform installers, and creates a **draft** GitHub Release. +2. **Post-publish phase** (`post-publish.yml`) -- triggered when the draft release is manually **published**. Submits Windows installers for AV whitelisting, notifies the team for DEB build and latest-version update, and triggers downstream updates (website, docs, winget). + +```mermaid +--- +config: + htmlLabels: false +--- +flowchart TD + %% ── Trigger ────────────────────────────────────────────── + push_tag([🏷 Signed tag pushed]) + + %% ── Draft phase ────────────────────────────────────────── + push_tag --> get-version + + subgraph draft["draft-release.yml"] + get-version["get-version + *parse semver from tag*"] + + get-version --> create-release-draft + create-release-draft["create-release-draft + *compile & test (Linux) + create draft release + sign source tarball*"] + + create-release-draft --> build-exe-and-msi + create-release-draft --> build-dmg-arm64 + create-release-draft --> build-dmg-x64 + create-release-draft --> build-appimages + + build-exe-and-msi["build-exe-and-msi + *calls win-exe.yml + MSI + EXE (x64) + code-signed & GPG-signed*"] + build-dmg-arm64["build-dmg-arm64 + *calls mac-dmg.yml + DMG (arm64) + notarized & GPG-signed*"] + build-dmg-x64["build-dmg-x64 + *calls mac-dmg-x64.yml + DMG (x64) + notarized & GPG-signed*"] + build-appimages["build-appimages + *calls appimage.yml + AppImage (x86_64 + aarch64) + GPG-signed*"] + + build-exe-and-msi --> update-sha256sums + build-dmg-arm64 --> update-sha256sums + build-dmg-x64 --> update-sha256sums + build-appimages --> update-sha256sums + + update-sha256sums["update-sha256sums + *compute checksums + update release body*"] + end + + update-sha256sums --> manual_review + + %% ── Manual gate ────────────────────────────────────────── + manual_review{{Manual review + & publish}} + + %% ── Post-publish phase ─────────────────────────────────── + manual_review --> published([📢 Release published]) + published --> post-publish + + subgraph post-publish["post-publish.yml"] + direction TB + + check-release["check-release + *classify release tag + stable, alpha, beta, rc, unknown*"] + notify["notify + *Slack notifications + deb build & version check*"] + get-asset-urls["get-asset-urls + *extract MSI & EXE + download URLs*"] + + check-release --> notify-winget + check-release --> trigger-website + check-release --> trigger-docs + + get-asset-urls --> allowlist-msi + allowlist-msi --> allowlist-exe + + allowlist-msi["allowlist-msi-x64 + *av-whitelist.yml + Kaspersky & Avast*"] + allowlist-exe["allowlist-exe-x64 + *av-whitelist.yml + Kaspersky & Avast*"] + + notify-winget["notify-winget + *Slack: ready for winget + stable only*"] + trigger-website["trigger-website-update + *dispatch to + cryptomator.github.io + stable only*"] + trigger-docs["trigger-docs-update + *dispatch to + cryptomator/docs + stable only, Windows*"] + end +``` + +## Phase 1: Draft Release (`draft-release.yml`) + +**Trigger:** push of any tag (`*`) + +### Jobs + +| Job | Runs on | Description | +|-----|---------|-------------| +| **get-version** | ubuntu | Parses the tag into semver components (`semVerNum`, `semVerSuffix`, `revNum`, `versionType`). The release is aborted if not an alpha, beta, rc or 'stable' release. | +| **create-release-draft** | ubuntu | Checks out the repo, verifies the tag is **signed** and lives on a `main` or `release/*` branch. Runs `./mvnw verify` (with `xvfb-run`). Creates a GitHub Release **draft** using the [release body template](../release-body.md.template). Downloads and GPG-signs the source tarball. | +| **build-exe-and-msi** | windows | Calls [`win-exe.yml`](win-exe.yml). Builds the MSI and EXE bundle installer for x64 Windows. Code-signed via Azure Trusted Signing, GPG-signed, and uploaded to the draft release. Outputs SHA-256 checksums. | +| **build-dmg-arm64** | macos-15 | Calls [`mac-dmg.yml`](mac-dmg.yml). Builds the DMG for Apple Silicon. Code-signed, notarized with Apple, GPG-signed, and uploaded. Outputs SHA-256 checksum. | +| **build-dmg-x64** | macos-15-large | Calls [`mac-dmg-x64.yml`](mac-dmg-x64.yml). Same as above but for Intel Macs. Uses macFUSE instead of FUSE-T. | +| **build-appimages** | ubuntu | Calls [`appimage.yml`](appimage.yml). Builds AppImages for x86_64 and aarch64 (matrix). GPG-signed and uploaded with `.zsync` delta-update files. Outputs SHA-256 checksums. | +| **update-sha256sums** | ubuntu | Runs after all builds complete. Computes the source tarball checksum, collects all artifact checksums, and updates the draft release body via `envsubst`. Replaces the "builds still running" banner with a success notice. | + +### Release Artifacts + +After the draft phase, the GitHub Release contains: + +| Artifact | Platform | +|----------|----------| +| `cryptomator-.tar.gz.asc` | Source (GPG signature) | +| `Cryptomator--x64.msi` + `.asc` | Windows | +| `Cryptomator--x64.exe` + `.asc` | Windows | +| `Cryptomator--arm64.dmg` + `.asc` | macOS (Apple Silicon) | +| `Cryptomator--x64.dmg` + `.asc` | macOS (Intel) | +| `cryptomator--x86_64.AppImage` + `.zsync` + `.asc` | Linux (x86_64) | +| `cryptomator--aarch64.AppImage` + `.zsync` + `.asc` | Linux (aarch64) | + +All artifacts are signed with GPG key [`615D449FE6E6A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a). + +## Manual Review Gate + +After the draft phase completes, a maintainer reviews the draft release on GitHub. This is the point to: + +- Verify all artifacts are present and checksums look correct. +- Edit the auto-generated release notes (What's New, Bugfixes, Other Changes). +- **Publish** the release when ready, which triggers phase 2. + +## Phase 2: Post-Publish (`post-publish.yml`) + +**Trigger:** `release: [published]` + +### Jobs + +| Job | Condition | Description | +|-----|-----------|-------------| +| **notify** | always | Sends Slack notifications to `#cryptomator-desktop`: ready to build `.deb` package, and reminder to update `latest-version.json` on S3. | +| **get-asset-urls** | always | Extracts MSI and EXE download URLs from the release assets. | +| **check-release** | always | Classifies the published release tag as `stable`, `alpha`, `beta`, `rc`, or `unknown`. Stable-only follow-up jobs depend on this output. Unlike `get-version.yml` workflow, this job does not perform semver validation. | +| **allowlist-msi-x64** | Windows release | Calls [`av-whitelist.yml`](av-whitelist.yml). Uploads the MSI to Kaspersky and Avast for whitelisting. | +| **allowlist-exe-x64** | Windows release | Same as above for the EXE. Runs sequentially after MSI. | +| **notify-winget** | stable + Windows | Sends a Slack notification that the release is ready for [winget submission](winget.yml). | +| **trigger-website-update** | stable | Dispatches `desktop-release` event to `cryptomator/cryptomator.github.io`. | +| **trigger-docs-update** | stable + Windows | Dispatches `desktop-release` event to `cryptomator/docs`. | + +### Manual Follow-ups + +These steps are triggered by team members after Slack notifications: + +- **Debian package** -- Run the [`debian.yml`](debian.yml) workflow to build `.deb` and optionally upload to the PPA. +- **winget** -- Run the [`winget.yml`](winget.yml) workflow to submit to the Windows Package Manager. +- **latest-version.json** -- Update the version-check file on S3 (`static.cryptomator.org/desktop/latest-version.json`). + +## Signing & Security + +- **Git tag** must be SSH-signed and reside on `main` or `release/*`. +- **Windows** installers are code-signed using Azure Trusted Signing. +- **macOS** DMGs are code-signed with an Apple Developer certificate and notarized via `notarytool`. +- **All artifacts** receive a detached GPG signature (`.asc`) using key `615D449FE6E6A235`. +- **AV whitelisting** is submitted to Kaspersky and Avast after publish (Windows installers only). +- The draft release is created using `CRYPTOBOT_RELEASE_TOKEN`, not `GITHUB_TOKEN`, to ensure proper permissions and trigger downstream workflows. diff --git a/.github/workflows/appimage.yml b/.github/workflows/appimage.yml index b12a74569..344a86e11 100644 --- a/.github/workflows/appimage.yml +++ b/.github/workflows/appimage.yml @@ -1,17 +1,44 @@ name: Build AppImage on: - release: - types: [published] + schedule: + - cron: '0 23 20 * *' + workflow_call: + inputs: + semVerNum: + type: string + description: 'The Major.Minor.Patch part of the version' + required: true + revisionNum: + type: string + description: 'The revision number' + required: true + semVerSuffix: + type: string + description: 'The suffix of the version, including dash' + required: true + upload-to-draft: + type: boolean + default: true + outputs: + sha256-appimage-x64: + description: "SHA256 sum of the x64 appimage" + value: ${{ jobs.collect-sha256sums.outputs.x64-sha256sum}} + sha256-appimage-aarch64: + description: "SHA256 sum of the aarch64 appimage" + value: ${{ jobs.collect-sha256sums.outputs.aarch64-sha256sum}} workflow_dispatch: inputs: - version: - description: 'Version' + semVerNum: + description: 'The Major.Minor.Patch part of the version' required: false - create-pr: - description: 'Create a PR for aur-bin repo' - type: boolean - default: false + revisionNum: + description: 'The revision number' + required: false + semVerSuffix: + description: 'The suffix of the version, including dash' + required: false + default: '-SNAPSHOT' push: branches-ignore: - 'dependabot/**' @@ -23,40 +50,34 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: '25.0.2+10.0.LTS' + JAVA_VERSION: '26.0.1+8' + VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}} + REVISION_NUM: ${{ inputs.revisionNum || '0' }} + VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}} + jobs: - get-version: - uses: ./.github/workflows/get-version.yml - with: - version: ${{ inputs.version }} #okay if not defined - build: 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 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' + openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-x64_bin-jmods.zip' + openjfx-sha: '47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50' appimagetool-sha: 'ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0' - os: ubuntu-24.04-arm arch: aarch64 - openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip' - openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646' + openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-aarch64_bin-jmods.zip' + openjfx-sha: 'e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f' appimagetool-sha: 'f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Java - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ env.JAVA_DIST }} java-version: ${{ env.JAVA_VERSION }} @@ -74,7 +95,7 @@ jobs: JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1) JMOD_VERSION=${JMOD_VERSION#*@} JMOD_VERSION=${JMOD_VERSION%%.*} - POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) + POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) POM_JFX_VERSION=${POM_JFX_VERSION#*@} POM_JFX_VERSION=${POM_JFX_VERSION%%.*} @@ -83,9 +104,9 @@ jobs: exit 1 fi - name: Set version - run : mvn versions:set -DnewVersion="$SEMVER_STR" + run : ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}" - name: Run maven - run: mvn -B clean package -Plinux -DskipTests + run: ./mvnw -B clean package -DskipTests - name: Patch target dir run: | cp LICENSE.txt target @@ -125,13 +146,13 @@ jobs: --dest appdir --name Cryptomator --vendor "Skymatic GmbH" - --copyright "(C) 2016 - 2025 Skymatic GmbH" - --app-version "${SEMVER_NUM}.${REV_NUM}" + --copyright "(C) 2016 - 2026 Skymatic GmbH" + --app-version "${VERSION_NUM}.${REVISION_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=\"${SEMVER_STR}\"" + --java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\"" --java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Djava.net.useSystemProxies=true" --java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" @@ -142,7 +163,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-${REV_NUM}\"" + --java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NUM}\"" --java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" --java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" --java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" @@ -181,7 +202,7 @@ jobs: GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} - name: Build AppImage run: > - ./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${SEMVER_STR}-${{ matrix.arch }}.AppImage + ./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.AppImage -u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.arch }}.AppImage.zsync" --sign --sign-key=615D449FE6E6A235 - name: Create detached GPG signatures @@ -189,7 +210,7 @@ jobs: 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@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: appimage-${{ matrix.arch }} path: | @@ -198,9 +219,10 @@ jobs: cryptomator-*.asc if-no-files-found: error - name: Publish AppImage on GitHub Releases - if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published' - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 + if: inputs.upload-to-draft + uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 with: + draft: true fail_on_unmatched_files: true token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} files: | @@ -208,79 +230,24 @@ jobs: cryptomator-*.zsync cryptomator-*.asc - create-aur-bin-pr: - name: Create PR for aur-bin repo - if: github.event_name == 'workflow_dispatch' && inputs.create-pr || github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable' + collect-sha256sums: + name: Collect AppImage checksums runs-on: ubuntu-latest - 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 + needs: [build] + if: inputs.upload-to-draft + outputs: + x64-sha256sum: ${{ steps.sha256sum.outputs.x64-sha256sum }} + aarch64-sha256sum: ${{ steps.sha256sum.outputs.aarch64-sha256sum }} 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 pacman-contrib - - name: Checkout cryptomator/aur-bin - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Download AppImage artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - repository: 'cryptomator/aur-bin' - token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - - name: Create build user + pattern: appimage-* + path: appimage-artifacts + - name: Compute SHA256 sums + id: sha256sum 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/${SEMVER_STR}" - - name: Update build file - run: | - sed -i -e "s|^pkgver=.*$|pkgver=${SEMVER_STR}|" PKGBUILD - sed -i -e 's|^pkgrel=.*$|pkgrel=1|' PKGBUILD - 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 "cryptobot" - git config user.email "cryptobot@users.noreply.github.com" - git config push.autoSetupRemote true - git stage PKGBUILD .SRCINFO - git commit -m "Prepare release ${SEMVER_STR}" - git push - - 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 ${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_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 }} ${{ 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 + read -ra X64_SUM < <(sha256sum appimage-artifacts/appimage-x86_64/cryptomator-*-x86_64.AppImage) + read -ra AARCH64_SUM < <(sha256sum appimage-artifacts/appimage-aarch64/cryptomator-*-aarch64.AppImage) + echo "x64-sha256sum=${X64_SUM[0]}" >> "$GITHUB_OUTPUT" + echo "aarch64-sha256sum=${AARCH64_SUM[0]}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/aur-bin.yml b/.github/workflows/aur-bin.yml new file mode 100644 index 000000000..f6f00a93e --- /dev/null +++ b/.github/workflows/aur-bin.yml @@ -0,0 +1,115 @@ +name: PR for aur-bin repo + +on: + release: + types: [published] + workflow_dispatch: + inputs: + src-tag: + description: 'Source or Release tag' + required: false + +jobs: + get-version: + uses: ./.github/workflows/get-version.yml + with: + version: ${{ inputs.src-tag }} + + create-aur-bin-pr: + name: Create PR for aur-bin repo + if: (github.event_name == 'workflow_dispatch') || (github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable') + runs-on: ubuntu-latest + needs: [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: Prepare pacman + run: | + 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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: 'cryptomator/aur-bin' + 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/${SEMVER_STR}" + - name: Determine pkgrel + id: pkgrel + run: | + 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=${TARGET_VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT" + env: + TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }} + - name: Update build file + run: | + sed -i -e "s|^pkgver=.*$|pkgver=${PKG_VERSION}|" PKGBUILD + sed -i -e "s|^pkgrel=.*$|pkgrel=${PKG_RELEASE}|" PKGBUILD + sudo -u builder updpkgsums + sudo -u builder makepkg --printsrcinfo > .SRCINFO + env: + PKG_VERSION: ${{ needs.get-version.outputs.semVerNum }} + PKG_RELEASE: ${{ steps.pkgrel.outputs.value }} + - 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 + uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }} + SLACK_USERNAME: 'Cryptobot' + SLACK_ICON: '' + SLACK_ICON_EMOJI: ':bot:' + SLACK_CHANNEL: 'cryptomator-desktop' + 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: '' + MSG_MINIMAL: true \ No newline at end of file diff --git a/.github/workflows/av-whitelist.yml b/.github/workflows/av-whitelist.yml index 0c2bc3d9a..637c1c4e5 100644 --- a/.github/workflows/av-whitelist.yml +++ b/.github/workflows/av-whitelist.yml @@ -37,7 +37,7 @@ on: jobs: download-file: name: Downloads the file into the VM - runs-on: ubuntu-latest + runs-on: ubuntu-slim outputs: fileName: ${{ steps.extractName.outputs.fileName}} env: @@ -51,24 +51,24 @@ jobs: - name: Download file run: curl --silent --fail-with-body --proto "=https" -L "${INPUT_URL}" -o "${{steps.extractName.outputs.fileName}}" - name: Upload artifact - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ steps.extractName.outputs.fileName }} path: ${{ steps.extractName.outputs.fileName }} if-no-files-found: error allowlist-kaspersky: name: Anti Virus Allowlisting Kaspersky - runs-on: ubuntu-latest + runs-on: ubuntu-slim needs: download-file if: inputs.kaspersky steps: - name: Download artifact - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ${{ needs.download-file.outputs.fileName }} path: upload - name: Upload to Kaspersky - uses: SamKirkland/FTP-Deploy-Action@a51268f67f6605236975928ae28b0f7e9971d50a # v4.6.3 + uses: SamKirkland/FTP-Deploy-Action@110f9186c050f71550953127052e77650219c287 # v4.6.3 with: protocol: ftps server: allowlist.kaspersky-labs.com @@ -78,12 +78,12 @@ jobs: local-dir: ./upload/ allowlist-avast: name: Anti Virus Allowlisting Avast - runs-on: ubuntu-latest + runs-on: ubuntu needs: download-file if: inputs.avast steps: - name: Download artifact - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ${{ needs.download-file.outputs.fileName }} path: upload diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 218b1caae..6e0345816 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: 25 + JAVA_VERSION: 26 defaults: run: @@ -22,14 +22,14 @@ jobs: name: Compile and Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ env.JAVA_DIST }} java-version: ${{ env.JAVA_VERSION }} cache: 'maven' - name: Cache SonarCloud packages - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar @@ -37,7 +37,7 @@ jobs: - name: Build and Test run: > xvfb-run - mvn -B verify + ./mvnw -B verify jacoco:report org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Pcoverage @@ -47,40 +47,3 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - name: Draft a release - if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 - with: - draft: true - discussion_category_name: releases - token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} - generate_release_notes: true - body: |- - > [!NOTE] - > 🚧 Work in Progress 🚧 - > - > Please be patient, the [builds are still running](https://github.com/cryptomator/cryptomator/actions). Binary packages can be found here in a few moments. - - - - For a comprehensive view of changes, read the [CHANGELOG](https://github.com/cryptomator/cryptomator/blob/develop/CHANGELOG.md). - - --- - - - - - > [!TIP] - > You can verify the GPG signature of all assets using our public key: [`5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a). - - - - diff --git a/.github/workflows/check-jdk-updates.yml b/.github/workflows/check-jdk-updates.yml index 9eae6da00..d721bf1ad 100644 --- a/.github/workflows/check-jdk-updates.yml +++ b/.github/workflows/check-jdk-updates.yml @@ -26,7 +26,7 @@ jobs: run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,2) >> "$env:GITHUB_ENV" shell: pwsh - name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }} - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: java-version: ${{ env.JDK_MAJOR_VERSION}} distribution: ${{ env.JDK_VENDOR }} @@ -70,14 +70,14 @@ jobs: } - name: Notify if: steps.determine.outputs.UPDATE_AVAILABLE == 'true' - uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 + uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} SLACK_USERNAME: 'Cryptobot' - SLACK_ICON: false + SLACK_ICON: '' SLACK_ICON_EMOJI: ':bot:' SLACK_CHANNEL: 'cryptomator-desktop' SLACK_TITLE: "JDK update available" SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ steps.determine.outputs.LATEST_JDK_VERSION }}. Check the Nextcloud collective for instructions." - SLACK_FOOTER: false + SLACK_FOOTER: '' MSG_MINIMAL: true \ No newline at end of file diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index 8a2cae05a..f0d76d6e7 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -1,6 +1,8 @@ name: Build Debian Package on: + schedule: + - cron: '0 22 20 * *' workflow_dispatch: inputs: semver: @@ -23,12 +25,12 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: '25.0.2+10.0.LTS' - DEB_BUILD_DEPENDS: 'debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1' - OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip' - OPENJFX_JMODS_AMD64_HASH: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b' - OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip' - OPENJFX_JMODS_AARCH64_HASH: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646' + JAVA_VERSION: '26.0.1+8' + DEB_BUILD_DEPENDS: 'debhelper (>=10), coffeelibs-jdk-26 (>= 26.0.1+8-0ppa1), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1' + OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-x64_bin-jmods.zip' + OPENJFX_JMODS_AMD64_HASH: '47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50' + OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-aarch64_bin-jmods.zip' + OPENJFX_JMODS_AARCH64_HASH: 'e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f' jobs: get-version: @@ -43,7 +45,7 @@ jobs: env: INPUT_PPAVER: ${{ inputs.ppaver }} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - id: deb-version name: Determine deb-version run: | @@ -54,20 +56,21 @@ jobs: fi - name: Install build tools run: | + sudo add-apt-repository -y ppa:coffeelibs/openjdk sudo apt-get update sudo apt-get install devscripts dput sudo apt-get satisfy "${DEB_BUILD_DEPENDS}" env: DEB_BUILD_DEPENDS: ${{ env.DEB_BUILD_DEPENDS }} - name: Setup Java - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ env.JAVA_DIST }} java-version: ${{ env.JAVA_VERSION }} check-latest: true cache: 'maven' - name: Run maven - run: mvn -B clean package -Plinux -DskipTests + run: ./mvnw -B clean package -DskipTests - name: Download OpenJFX jmods id: download-jmods run: | @@ -87,7 +90,7 @@ jobs: JMOD_VERSION_AARCH64=$(jmod describe jmods/aarch64/javafx.base.jmod | head -1) JMOD_VERSION_AARCH64=${JMOD_VERSION_AARCH64#*@} JMOD_VERSION_AARCH64=${JMOD_VERSION_AARCH64%%.*} - POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) + POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) POM_JFX_VERSION=${POM_JFX_VERSION#*@} POM_JFX_VERSION=${POM_JFX_VERSION%%.*} @@ -143,7 +146,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@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: linux-deb-package path: | diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index 4ffcb9ff1..7677d802d 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -7,11 +7,11 @@ on: jobs: check-dependencies: - uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # v3.0.3 + uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@8356563bf7b8d1c8d693f75ca487e8f57573cec9 # v3.1.0 with: runner-os: 'ubuntu-latest' java-distribution: 'temurin' - java-version: 25 + java-version: 26 secrets: nvd-api-key: ${{ secrets.NVD_API_KEY }} ossindex-username: ${{ secrets.OSSINDEX_USERNAME }} diff --git a/.github/workflows/dl-stats.yml b/.github/workflows/dl-stats.yml index bfc53db3c..3c9c0b107 100644 --- a/.github/workflows/dl-stats.yml +++ b/.github/workflows/dl-stats.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Get download count of latest releases id: get-stats - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const query = `query($owner:String!, $name:String!) { diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml new file mode 100644 index 000000000..45221d36f --- /dev/null +++ b/.github/workflows/draft-release.yml @@ -0,0 +1,157 @@ +name: Draft a Cryptomator Release + +on: + push: + tags: + - '*' + +env: + JAVA_DIST: 'temurin' + JAVA_VERSION: '26.0.1+8' + +defaults: + run: + shell: bash + +jobs: + get-version: + uses: ./.github/workflows/get-version.yml + with: + version: '' + + create-release-draft: + name: Compile and Test + runs-on: ubuntu-latest + needs: get-version + if: needs.get-version.outputs.versionType != 'unknown' + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 0 + - name: Check the git tag is signed + run: git cat-file -p "${GITHUB_REF_NAME}" | grep "BEGIN SSH SIGNATURE" + - name: Check the git tag is on release or main branch + run: git branch -r --contains "${GITHUB_REF_NAME}" | grep -E '^\s*origin/(main|release/.*)\s*$' + - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 + with: + distribution: ${{ env.JAVA_DIST }} + java-version: ${{ env.JAVA_VERSION }} + cache: 'maven' + - name: Build and Test + run: xvfb-run ./mvnw -B verify + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + - name: Draft a release + uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 + with: + draft: true + discussion_category_name: releases + token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} + generate_release_notes: true + body_path: .github/release-body.md.template + - name: Download source tarball + run: | + curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/${{ github.ref }}.tar.gz --output cryptomator-${{ github.ref_name }}.tar.gz + - name: Sign source tarball with key 615D449FE6E6A235 + run: | + echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import + echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz + env: + GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} + - name: Publish asc on GitHub Releases + uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 + with: + draft: true + fail_on_unmatched_files: true + token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} + files: | + cryptomator-*.tar.gz.asc + + build-exe-and-msi: + needs: [get-version, create-release-draft] + uses: ./.github/workflows/win-exe.yml + with: + semVerNum: ${{needs.get-version.outputs.semVerNum}} + revisionNum: ${{needs.get-version.outputs.revNum}} + semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}} + secrets: inherit + + build-dmg-arm64: + needs: [get-version, create-release-draft] + uses: ./.github/workflows/mac-dmg.yml + with: + semVerNum: ${{needs.get-version.outputs.semVerNum}} + revisionNum: ${{needs.get-version.outputs.revNum}} + semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}} + secrets: inherit + + build-dmg-x64: + needs: [get-version, create-release-draft] + uses: ./.github/workflows/mac-dmg-x64.yml + with: + semVerNum: ${{needs.get-version.outputs.semVerNum}} + revisionNum: ${{needs.get-version.outputs.revNum}} + semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}} + secrets: inherit + + build-appimages: + needs: [get-version, create-release-draft] + uses: ./.github/workflows/appimage.yml + with: + semVerNum: ${{needs.get-version.outputs.semVerNum}} + revisionNum: ${{needs.get-version.outputs.revNum}} + semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}} + secrets: inherit + + update-sha256sums: + runs-on: ubuntu-latest + needs: [get-version, build-exe-and-msi, build-dmg-arm64, build-dmg-x64, build-appimages] + env: + TAG: ${{ github.ref_name }} + SEMVER: ${{ needs.get-version.outputs.semVerStr }} + GH_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - name: Compute source tarball SHA256 + id: src-sha256 + run: | + curl --silent --fail-with-body --proto "=https" -L \ + -H "Accept: application/vnd.github+json" \ + "https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz" \ + --output "cryptomator-${SEMVER}.tar.gz" + read -ra CMD_OUTPUT < <(sha256sum "cryptomator-${SEMVER}.tar.gz") + echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT + - name: Update release body with checksums + run: | + CURRENT_BODY=$(gh release view "${TAG}" --json body --jq .body) + RELEASE_BODY=$(printf '%s\n' "${CURRENT_BODY}" | sed '//,//c\ + \ + > [!NOTE]\ + > Release artifacts finished building successfully.\ + >\ + > SHA-256 checksums have been updated below.\ + ') + + export TARBALL="${SRC_SHA} cryptomator-${SEMVER}.tar.gz" + export MSI="${MSI_SHA} Cryptomator-${SEMVER}-x64.msi" + export EXE="${EXE_SHA} Cryptomator-${SEMVER}-x64.exe" + export DMG_arm64="${DMG_ARM64_SHA} Cryptomator-${SEMVER}-arm64.dmg" + export DMG_x64="${DMG_X64_SHA} Cryptomator-${SEMVER}-x64.dmg" + export APPIMAGE_x86_64="${APPIMAGE_X64_SHA} cryptomator-${SEMVER}-x86_64.AppImage" + export APPIMAGE_aarch64="${APPIMAGE_AARCH64_SHA} cryptomator-${SEMVER}-aarch64.AppImage" + + envsubst '$VERSION $TARBALL $EXE $MSI $DMG_x64 $DMG_arm64 $APPIMAGE_x86_64 $APPIMAGE_aarch64' \ + <<< "${RELEASE_BODY}" \ + > release-body.md + + gh release edit "${TAG}" --draft --notes-file release-body.md + env: + VERSION: ${{ needs.get-version.outputs.semVerStr }} + SRC_SHA: ${{ steps.src-sha256.outputs.value }} + MSI_SHA: ${{ needs.build-exe-and-msi.outputs.sha256-msi }} + EXE_SHA: ${{ needs.build-exe-and-msi.outputs.sha256-exe }} + DMG_ARM64_SHA: ${{ needs.build-dmg-arm64.outputs.sha256-dmg }} + DMG_X64_SHA: ${{ needs.build-dmg-x64.outputs.sha256-dmg }} + APPIMAGE_X64_SHA: ${{ needs.build-appimages.outputs.sha256-appimage-x64 }} + APPIMAGE_AARCH64_SHA: ${{ needs.build-appimages.outputs.sha256-appimage-aarch64 }} diff --git a/.github/workflows/error-db.yml b/.github/workflows/error-db.yml index 5c2ffebe8..7e27c89ed 100644 --- a/.github/workflows/error-db.yml +++ b/.github/workflows/error-db.yml @@ -14,7 +14,7 @@ jobs: - name: Query Discussion Data if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted' id: query-data - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) { diff --git a/.github/workflows/flathub.yml b/.github/workflows/flathub.yml deleted file mode 100644 index bf22cec30..000000000 --- a/.github/workflows/flathub.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Create PR for flathub - -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' - outputs: - url: ${{ steps.url.outputs.url}} - sha512: ${{ steps.sha512.outputs.sha512}} - steps: - - name: Determine tarball url - id: url - run: | - URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz" - echo "url=${URL}" >> "$GITHUB_OUTPUT" - env: - TAG: ${{ inputs.tag || github.event.release.tag_name}} - - name: Download source tarball and compute checksum - id: sha512 - run: | - 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: - name: Create PR for flathub - runs-on: ubuntu-latest - needs: [tarball, get-version] - env: - FLATHUB_PR_URL: tbd - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: 'flathub/org.cryptomator.Cryptomator' - token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - - name: Checkout release branch - run: | - git checkout -b release/${{ needs.get-version.outputs.semVerStr }} - - name: Update build file - run: | - sed -i -e 's/VERSION: [0-9]\+\.[0-9]\+\.[0-9]\+.*/VERSION: ${{ needs.get-version.outputs.semVerStr }}/g' org.cryptomator.Cryptomator.yaml - sed -i -e 's/sha512: [0-9A-Za-z_\+-]\{128\} #CRYPTOMATOR/sha512: ${{ needs.tarball.outputs.sha512 }} #CRYPTOMATOR/g' org.cryptomator.Cryptomator.yaml - sed -i -e 's;url: https://github.com/cryptomator/cryptomator/archive/refs/tags/[^[:blank:]]\+;url: ${{ needs.tarball.outputs.url }};g' org.cryptomator.Cryptomator.yaml - - 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 maven dependencies\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 "FLATHUB_PR_URL=$PR_URL" >> "$GITHUB_ENV" - env: - GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - - name: Slack Notification - uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 - if: github.event_name == 'release' - env: - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_USERNAME: 'Cryptobot' - SLACK_ICON: false - SLACK_ICON_EMOJI: ':bot:' - SLACK_CHANNEL: 'cryptomator-desktop' - SLACK_TITLE: "Flathub release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created." - SLACK_MESSAGE: "See <${{ env.FLATHUB_PR_URL }}|PR> on how to proceed.>." - SLACK_FOOTER: false - MSG_MINIMAL: true \ No newline at end of file diff --git a/.github/workflows/get-version.yml b/.github/workflows/get-version.yml index f75b22a63..7d519f33c 100644 --- a/.github/workflows/get-version.yml +++ b/.github/workflows/get-version.yml @@ -14,6 +14,9 @@ on: semVerNum: description: "The numerical part of the version string" value: ${{ jobs.determine-version.outputs.semVerNum}} + semVerSuffix: + description: "The suffix of the version string" + value: ${{ jobs.determine-version.outputs.semVerSuffix}} revNum: description: "The revision number" value: ${{ jobs.determine-version.outputs.revNum}} @@ -23,7 +26,7 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: 25 + JAVA_VERSION: 26 jobs: determine-version: @@ -32,14 +35,15 @@ jobs: outputs: semVerNum: ${{ steps.versions.outputs.semVerNum }} semVerStr: ${{ steps.versions.outputs.semVerStr }} + semVerSuffix: ${{ steps.versions.outputs.semVerSuffix }} revNum: ${{ steps.versions.outputs.revNum }} type: ${{ steps.versions.outputs.type}} steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 - name: Setup Java - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ env.JAVA_DIST }} java-version: ${{ env.JAVA_VERSION }} @@ -52,27 +56,32 @@ jobs: elif [[ "${VERSION_STRING}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then SEM_VER_STR="${VERSION_STRING}" else - SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout` + SEM_VER_STR=`./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout` fi - SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'` + + SEM_VER_NUM=$(echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/') + SEM_VER_SUFFIX="${SEM_VER_STR#"$SEM_VER_NUM"}" REVCOUNT=`git rev-list --count HEAD` + TYPE="unknown" - if [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ -z $SEM_VER_SUFFIX ]]; then TYPE="stable" - elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-alpha[1-9]+$ ]]; then + elif [[ $SEM_VER_SUFFIX =~ -alpha[1-9]+$ ]]; then TYPE="alpha" - elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-beta[1-9]+$ ]]; then + elif [[ $SEM_VER_SUFFIX =~ -beta[1-9]+$ ]]; then TYPE="beta" - elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-rc[1-9]$ ]]; then + elif [[ $SEM_VER_SUFFIX =~ -rc[1-9]+$ ]]; then TYPE="rc" fi + echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT + echo "semVerSuffix=${SEM_VER_SUFFIX}" >> $GITHUB_OUTPUT echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT echo "type=${TYPE}" >> $GITHUB_OUTPUT env: VERSION_STRING: ${{ inputs.version }} - name: Validate Version - uses: skymatic/semver-validation-action@7a6ae1c9e121540d11c9c7e4e667c83d583aa153 # v3.0.0 + uses: skymatic/semver-validation-action@7c80b6b03a18b42884761daa9862ff5683ec8c8a # v4.0.0 with: version: ${{ steps.versions.outputs.semVerStr }} \ No newline at end of file diff --git a/.github/workflows/linux-flatpak.yml b/.github/workflows/linux-flatpak.yml new file mode 100644 index 000000000..eb8a17978 --- /dev/null +++ b/.github/workflows/linux-flatpak.yml @@ -0,0 +1,264 @@ +name: Build flatpak + +on: + release: + types: [published] + workflow_dispatch: + inputs: + src-tag: + description: 'Source or Release tag' + required: false + create-pr: + description: 'Create Flathub PR' + required: false + type: boolean + default: false + push: + branches-ignore: + - 'dependabot/**' + paths: + - '.github/workflows/get-version.yml' + - '.github/workflows/linux-flatpak.yml' + - 'dist/linux/flatpak/**' + - 'dist/linux/common/**' + - 'dist/linux/resources/**' + +jobs: + get-version: + uses: ./.github/workflows/get-version.yml + with: + version: ${{ inputs.src-tag }} + + build-flatpak: + name: "Build flatpak" + needs: [get-version] + container: + image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08 + options: --privileged + strategy: + fail-fast: false + matrix: + variant: + - arch: x86_64 + runner: ubuntu-24.04 + - arch: aarch64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.variant.runner }} + permissions: + contents: read + env: + SRC_GIT_SHA: ${{ inputs.src-tag || github.sha}} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: flathub/org.cryptomator.Cryptomator + submodules: true + - name: Checkout build script + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + path: build-scripts + - name: Checkout app source + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + path: cryptomator + ref: ${{ env.SRC_GIT_SHA }} + fetch-depth: 0 + - name: Prepare build files + # using envsubst instead of yq to keep linebreaks + run: | + cp -r -f build-scripts/dist/linux/flatpak/* . + envsubst '$FLATPAK_VERSION $FLATPAK_REVISION $CRYPTOMATOR_SOURCE' < org.cryptomator.Cryptomator.TEMPLATE.yaml > org.cryptomator.Cryptomator.yaml + env: + FLATPAK_VERSION: ${{ needs.get-version.outputs.semVerNum }} + FLATPAK_REVISION: 1 + CRYPTOMATOR_SOURCE: |- + type: git + path: cryptomator + commit: ${{ env.SRC_GIT_SHA }} + - name: Copy build script for upload + run: cp org.cryptomator.Cryptomator.yaml org.cryptomator.Cryptomator.${{matrix.variant.arch}}.yaml + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + archive: false + if-no-files-found: error + path: | + org.cryptomator.Cryptomator.${{matrix.variant.arch}}.yaml + - uses: flatpak/flatpak-github-actions/flatpak-builder@401fe28a8384095fc1531b9d320b292f0ee45adb # SNAPSHOT due to using keep-build-dirs + with: + bundle: cryptomator.flatpak + manifest-path: org.cryptomator.Cryptomator.yaml + cache-key: flatpak-builder-${{ env.SRC_GIT_SHA }} + arch: ${{ matrix.variant.arch }} + keep-build-dirs: true + - name: Collect maven dependencies + working-directory: .flatpak-builder/build/cryptomator-1/.m2/repository/ + run: | + find * -type f \( -iname '*.jar' -o -iname '*.pom' \) | sort -V > /tmp/maven-dependency-files.txt + grep -v '^org/openjfx/javafx-' /tmp/maven-dependency-files.txt > maven-dependency-files-common.txt + grep '^org/openjfx/javafx-' /tmp/maven-dependency-files.txt > maven-dependency-files-javafx.txt + - name: Update arch independent maven dependencies + run: | + ( + cd .flatpak-builder/build/cryptomator-1/.m2/repository/ + + while IFS= read -r dependencyPath; do + dependencyName=$(dirname "$dependencyPath") + dependencySha=$(sha256sum "$dependencyPath" | cut -c 1-64) + cat < maven-dependencies.yaml + - name: Update arch specific maven dependencies + run: | + ( + cd .flatpak-builder/build/cryptomator-1/.m2/repository/ + + while IFS= read -r dependencyPath; do + dependencyName=$(dirname "$dependencyPath") + dependencySha=$(sha256sum "$dependencyPath" | cut -c 1-64) + cat < javafx-maven-dependencies-${{ matrix.variant.arch }}.yaml + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: maven-sources-${{ matrix.variant.arch }} + if-no-files-found: error + path: | + maven-dependencies.yaml + javafx-maven-dependencies-${{ matrix.variant.arch }}.yaml + + verify-maven-sources: + name: Verify maven sources + runs-on: ubuntu-latest + needs: [build-flatpak] + permissions: + contents: none + steps: + - name: Download updated maven aarch64 dependencies + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: maven-sources-aarch64 + path: mvn-src-aarch64 + - name: Download updated maven x86_64 dependencies + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: maven-sources-x86_64 + path: mvn-src-x64 + - name: Verify arch independent maven dependencies + run: cmp --silent mvn-src-aarch64/maven-dependencies.yaml mvn-src-x64/maven-dependencies.yaml + + create-pr: + name: Create PR for flathub + runs-on: ubuntu-latest + needs: [get-version, verify-maven-sources] + if: (github.event_name == 'workflow_dispatch' && inputs.create-pr ) || (github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable') + permissions: + contents: write + env: + TARBALL_URL: 'https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name || inputs.src-tag }}.tar.gz' + steps: + - name: Check that input "src-tag" is actually a tag + if: github.event_name == 'workflow_dispatch' + run: | + if [ -z "$SRC_TAG" ]; then + echo '::error::Input "src-tag" must be set to create a Flathub PR' + exit 1 + fi + env: + SRC_TAG: ${{ inputs.src-tag }} + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: flathub/org.cryptomator.Cryptomator + submodules: true #TODO: Update submodule! + token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} + - name: Checkout release branch + run: | + git checkout -b release/${{ needs.get-version.outputs.semVerStr }} + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + path: cryptomator + - name: Download source tarball and compute checksum + id: sha512 + run: | + curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" ${TARBALL_URL} --output cryptomator.tar.gz + TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1) + echo "value=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT" + - name: Download updated maven aarch64 dependencies + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: maven-sources-aarch64 + path: mvn-src-aarch64 + - name: Download updated maven x86_64 dependencies + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: maven-sources-x86_64 + path: mvn-src-x64 + - name: Determine revision + id: revision + run: | + CURRENT_VERSION="$(yq '(.modules[] | select(.name == "cryptomator") | .build-options.env.VERSION)' org.cryptomator.Cryptomator.yaml)" + CURRENT_REVISION="$(yq '(.modules[] | select(.name == "cryptomator") | .build-options.env.REVISION_NO)' org.cryptomator.Cryptomator.yaml)" + + if [[ "$CURRENT_VERSION" == "$TARGET_VERSION" && "$CURRENT_REVISION" =~ ^[0-9]+$ ]]; then + NEXT_REVISION=$((CURRENT_REVISION + 1)) + else + NEXT_REVISION=1 + fi + + echo "value=${NEXT_REVISION}" >> "$GITHUB_OUTPUT" + env: + TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }} + - name: Update build files + run: | + cp -r -f cryptomator/dist/linux/flatpak/* . + cp -r -f mvn-src-x64/* . + cp -r -f mvn-src-aarch64/* . + envsubst '$FLATPAK_VERSION $FLATPAK_REVISION $CRYPTOMATOR_SOURCE' < org.cryptomator.Cryptomator.TEMPLATE.yaml > org.cryptomator.Cryptomator.yaml + yq -i 'del(.modules[] | select(.name == "cryptomator") | .build-options.build-args)' org.cryptomator.Cryptomator.yaml + yq -i '(.modules[] | select(.name == "cryptomator") | .sources) += ["maven-dependencies.yaml", "javafx-maven-dependencies-x86_64.yaml", "javafx-maven-dependencies-aarch64.yaml"]' org.cryptomator.Cryptomator.yaml + env: + FLATPAK_VERSION: ${{ needs.get-version.outputs.semVerNum }} + FLATPAK_REVISION: ${{ steps.revision.outputs.value}} + CRYPTOMATOR_SOURCE: |- + type: archive + sha512: ${{steps.sha512.outputs.value}} + url: ${{ env.TARBALL_URL }} + - 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 org.cryptomator.Cryptomator.yaml maven-dependencies.yaml javafx-maven-dependencies-aarch64.yaml javafx-maven-dependencies-x86_64.yaml + git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}" + git push + - 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 ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md) + echo "FLATHUB_PR_URL=$PR_URL" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} + - name: Slack Notification + uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0 + if: github.event_name == 'release' + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }} + SLACK_USERNAME: 'Cryptobot' + SLACK_ICON: '' + SLACK_ICON_EMOJI: ':bot:' + SLACK_CHANNEL: 'cryptomator-desktop' + SLACK_TITLE: "Flathub release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created." + SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.FLATHUB_PR_URL }}|PR> on how to proceed." + SLACK_FOOTER: '' + MSG_MINIMAL: true diff --git a/.github/workflows/linux-makepkg.yml b/.github/workflows/linux-makepkg.yml index 1b2fb3fdb..dfb6ff425 100644 --- a/.github/workflows/linux-makepkg.yml +++ b/.github/workflows/linux-makepkg.yml @@ -3,6 +3,8 @@ name: Build Arch package on: release: types: [published] + schedule: + - cron: '0 21 20 * *' workflow_dispatch: inputs: version: @@ -42,7 +44,7 @@ jobs: 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 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: path: cryptomator - name: Create build user @@ -67,13 +69,13 @@ jobs: sudo -u builder env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST" makepkg --syncdeps --cleanbuild --noconfirm --log - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: arch-package if-no-files-found: error path: | ${{ env.PKGDEST }}/*.pkg.tar.zst - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: pkgbuild-file if-no-files-found: error @@ -106,7 +108,7 @@ jobs: env: TAG: ${{ needs.get-version.outputs.semVerStr || github.event.release.tag_name }} - name: Checkout cryptomator/aur repo - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: 'cryptomator/aur' token: ${{ secrets.CRYPTOBOT_PR_TOKEN }} @@ -130,7 +132,6 @@ jobs: - 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)" @@ -141,11 +142,11 @@ jobs: fi echo "value=${NEXT_REL}" >> "$GITHUB_OUTPUT" - echo "dist-version=${VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT" + echo "dist-version=${TARGET_VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT" env: - VERSION: ${{ needs.get-version.outputs.semVerStr }} + TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }} - name: Download PKGBUILD template - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: pkgbuild-file - name: Prepare PKGBUILD @@ -187,14 +188,14 @@ jobs: GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - name: Slack Notification if: github.event_name == 'release' - uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 + uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }} SLACK_USERNAME: 'Cryptobot' - SLACK_ICON: false + SLACK_ICON: '' 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 + SLACK_FOOTER: '' MSG_MINIMAL: true diff --git a/.github/workflows/mac-dmg-x64.yml b/.github/workflows/mac-dmg-x64.yml index 8454fed14..96455be3d 100644 --- a/.github/workflows/mac-dmg-x64.yml +++ b/.github/workflows/mac-dmg-x64.yml @@ -9,13 +9,45 @@ name: Build macOS .dmg for x64 ####################################### on: - release: - types: [published] + schedule: + - cron: '0 20 20 * *' + workflow_call: + inputs: + semVerNum: + type: string + description: 'The Major.Minor.Patch part of the version' + required: true + revisionNum: + type: string + description: 'The revision number' + required: true + semVerSuffix: + type: string + description: 'The suffix of the version, including dash' + required: true + notarize: + description: 'Notarize' + default: true + type: boolean + upload-to-draft: + type: boolean + default: true + outputs: + sha256-dmg: + description: "SHA256 sum of the x64 dmg" + value: ${{ jobs.build.outputs.sha256sum}} workflow_dispatch: inputs: - version: - description: 'Version' + semVerNum: + description: 'The Major.Minor.Patch part of the version' required: false + revisionNum: + description: 'The revision number' + required: false + semVerSuffix: + description: 'The suffix of the version, including dash' + required: false + default: '-SNAPSHOT' notarize: description: 'Notarize' required: true @@ -24,18 +56,18 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: '25.0.2+10.0.LTS' + JAVA_VERSION: '26.0.1+8' + VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}} + REVISION_NUM: ${{ inputs.revisionNum || '0' }} + VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}} + jobs: - get-version: - uses: ./.github/workflows/get-version.yml - with: - version: ${{ inputs.version }} - - build-arm: + build: name: Build Cryptomator.app for ${{ matrix.output-suffix }} runs-on: ${{ matrix.os }} - needs: [get-version] + outputs: + sha256sum: ${{ steps.sha256sum.outputs.value }} strategy: fail-fast: false matrix: @@ -44,12 +76,12 @@ jobs: architecture: x64 output-suffix: x64 fuse-lib: macFUSE - openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_osx-x64_bin-jmods.zip' - openjfx-sha: '0b4d8463f03901b7425d94628e4116b7078abb8dd540fbec415266fac20bda5c' + openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_osx-x64_bin-jmods.zip' + openjfx-sha: '3512fabe43aee467538d329cfbbaab3c53dff2a810f0d54e381f461d5e0fac43' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Java - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ env.JAVA_DIST }} java-version: ${{ env.JAVA_VERSION }} @@ -68,7 +100,7 @@ jobs: JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1) JMOD_VERSION=${JMOD_VERSION#*@} JMOD_VERSION=${JMOD_VERSION%%.*} - POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) + POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) POM_JFX_VERSION=${POM_JFX_VERSION#*@} POM_JFX_VERSION=${POM_JFX_VERSION%%.*} @@ -77,9 +109,9 @@ jobs: exit 1 fi - name: Set version - run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} + run : ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}" - name: Run maven - run: mvn -B clean package -Pmac -DskipTests + run: ./mvnw -B clean package -Pmac -DskipTests - name: Patch target dir run: | cp LICENSE.txt target @@ -117,8 +149,8 @@ jobs: --dest appdir --name Cryptomator --vendor "Skymatic GmbH" - --copyright "(C) 2016 - 2025 Skymatic GmbH" - --app-version "${{ needs.get-version.outputs.semVerNum }}" + --copyright "(C) 2016 - 2026 Skymatic GmbH" + --app-version "${VERSION_NUM}" --java-options "--enable-preview" --java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac" --java-options "-Xss5m" @@ -127,7 +159,7 @@ jobs: --java-options "-Djava.net.useSystemProxies=true" --java-options "-Dapple.awt.enableTemplateImages=true" --java-options "-Dsun.java2d.metal=true" - --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\"" + --java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\"" --java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\"" --java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\"" --java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\"" @@ -137,7 +169,7 @@ jobs: --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\"" --java-options "-Dcryptomator.showTrayIcon=true" --java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" - --java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\"" + --java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NUM}\"" --java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" --mac-package-identifier org.cryptomator --resource-dir dist/mac/resources @@ -146,16 +178,14 @@ jobs: 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 + sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NUM}|g" Cryptomator.app/Contents/Info.plist + sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile env: - VERSION_NO: ${{ needs.get-version.outputs.semVerNum }} - REVISION_NO: ${{ needs.get-version.outputs.revNum }} PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }} - name: Generate license for dmg run: > - mvn -B license:add-third-party + ./mvnw -B license:add-third-party -Dlicense.thirdPartyFilename=license.rtf -Dlicense.outputDirectory=dist/mac/dmg/resources -Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl @@ -240,16 +270,14 @@ jobs: --eula "dist/mac/dmg/resources/license.rtf" --icon ".background" 128 758 --icon ".VolumeIcon.icns" 512 758 - Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg - env: - VERSION_NO: ${{ needs.get-version.outputs.semVerNum }} + Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg - name: Codesign .dmg run: | codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg env: CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }} - name: Notarize .dmg - if: startsWith(github.ref, 'refs/tags/') || inputs.notarize + if: inputs.notarize || github.event_name == 'schedule' uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3 with: app-path: 'Cryptomator-*.dmg' @@ -257,8 +285,12 @@ jobs: password: ${{ secrets.MACOS_NOTARIZATION_PW }} team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }} xcode-path: '/Applications/Xcode_16.app' + - id: sha256sum + run: | + read -ra CMD_OUTPUT < <(shasum -a256 Cryptomator-*.dmg) + echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT - name: Add possible alpha/beta tags to installer name - run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg + run: mv Cryptomator-*.dmg "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg" - name: Create detached GPG signature with key 615D449FE6E6A235 run: | echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import @@ -271,7 +303,7 @@ jobs: run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db continue-on-error: true - name: Upload artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: dmg-${{ matrix.output-suffix }} path: | @@ -279,9 +311,10 @@ jobs: Cryptomator-*.asc if-no-files-found: error - name: Publish dmg on GitHub Releases - if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published' - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 + if: inputs.upload-to-draft + uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 with: + draft: true fail_on_unmatched_files: true token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} files: | diff --git a/.github/workflows/mac-dmg.yml b/.github/workflows/mac-dmg.yml index b4a456782..2e2feafc8 100644 --- a/.github/workflows/mac-dmg.yml +++ b/.github/workflows/mac-dmg.yml @@ -1,13 +1,45 @@ name: Build macOS .dmg for arm64 on: - release: - types: [published] + schedule: + - cron: '0 20 20 * *' + workflow_call: + inputs: + semVerNum: + type: string + description: 'The Major.Minor.Patch part of the version' + required: true + revisionNum: + type: string + description: 'The revision number' + required: true + semVerSuffix: + type: string + description: 'The suffix of the version, including dash' + required: true + notarize: + description: 'Notarize' + default: true + type: boolean + upload-to-draft: + type: boolean + default: true + outputs: + sha256-dmg: + description: "SHA256 sum of the arm64 dmg" + value: ${{ jobs.build.outputs.sha256sum}} workflow_dispatch: inputs: - version: - description: 'Version' + semVerNum: + description: 'The Major.Minor.Patch part of the version' required: false + revisionNum: + description: 'The revision number' + required: false + semVerSuffix: + description: 'The suffix of the version, including dash' + required: false + default: '-SNAPSHOT' notarize: description: 'Notarize' required: true @@ -22,18 +54,18 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: '25.0.2+10.0.LTS' + JAVA_VERSION: '26.0.1+8' + VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}} + REVISION_NUM: ${{ inputs.revisionNum || '0' }} + VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}} + jobs: - get-version: - uses: ./.github/workflows/get-version.yml - with: - version: ${{ inputs.version }} - build: name: Build Cryptomator.app for ${{ matrix.output-suffix }} runs-on: ${{ matrix.os }} - needs: [get-version] + outputs: + sha256sum: ${{ steps.sha256sum.outputs.value }} strategy: fail-fast: false matrix: @@ -42,12 +74,12 @@ jobs: architecture: aarch64 output-suffix: arm64 fuse-lib: FUSE-T - openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_osx-aarch64_bin-jmods.zip' - openjfx-sha: '4cd258001c75af7047005c5c891e2400ed11d24fbb09412324c0cbaf8b503c5a' + openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_osx-aarch64_bin-jmods.zip' + openjfx-sha: 'a52014d625b8b04e57fd71650f881c1397542b4018e1b04f1b4e66c8800a1f34' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Java - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ env.JAVA_DIST }} java-version: ${{ env.JAVA_VERSION }} @@ -66,7 +98,7 @@ jobs: JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1) JMOD_VERSION=${JMOD_VERSION#*@} JMOD_VERSION=${JMOD_VERSION%%.*} - POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) + POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) POM_JFX_VERSION=${POM_JFX_VERSION#*@} POM_JFX_VERSION=${POM_JFX_VERSION%%.*} @@ -75,9 +107,9 @@ jobs: exit 1 fi - name: Set version - run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} + run : ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}" - name: Run maven - run: mvn -B clean package -Pmac -DskipTests + run: ./mvnw -B clean package -Pmac -DskipTests - name: Patch target dir run: | cp LICENSE.txt target @@ -115,8 +147,8 @@ jobs: --dest appdir --name Cryptomator --vendor "Skymatic GmbH" - --copyright "(C) 2016 - 2025 Skymatic GmbH" - --app-version "${{ needs.get-version.outputs.semVerNum }}" + --copyright "(C) 2016 - 2026 Skymatic GmbH" + --app-version "${VERSION_NUM}" --java-options "--enable-preview" --java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac" --java-options "-Xss5m" @@ -125,7 +157,7 @@ jobs: --java-options "-Djava.net.useSystemProxies=true" --java-options "-Dapple.awt.enableTemplateImages=true" --java-options "-Dsun.java2d.metal=true" - --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\"" + --java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\"" --java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\"" --java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\"" --java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\"" @@ -135,7 +167,7 @@ jobs: --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\"" --java-options "-Dcryptomator.showTrayIcon=true" --java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" - --java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\"" + --java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NUM}\"" --java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" --java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" --mac-package-identifier org.cryptomator @@ -145,16 +177,14 @@ jobs: 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 + sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NUM}|g" Cryptomator.app/Contents/Info.plist + sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile env: - VERSION_NO: ${{ needs.get-version.outputs.semVerNum }} - REVISION_NO: ${{ needs.get-version.outputs.revNum }} PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }} - name: Generate license for dmg run: > - mvn -B license:add-third-party + ./mvnw -B license:add-third-party -Dlicense.thirdPartyFilename=license.rtf -Dlicense.outputDirectory=dist/mac/dmg/resources -Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl @@ -239,16 +269,14 @@ jobs: --eula "dist/mac/dmg/resources/license.rtf" --icon ".background" 128 758 --icon ".VolumeIcon.icns" 512 758 - Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg - env: - VERSION_NO: ${{ needs.get-version.outputs.semVerNum }} + Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg - name: Codesign .dmg run: | codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg env: CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }} - name: Notarize .dmg - if: startsWith(github.ref, 'refs/tags/') || inputs.notarize + if: inputs.notarize || github.event_name == 'schedule' uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3 with: app-path: 'Cryptomator-*.dmg' @@ -256,8 +284,12 @@ jobs: password: ${{ secrets.MACOS_NOTARIZATION_PW }} team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }} xcode-path: '/Applications/Xcode_16.app' + - id: sha256sum + run: | + read -ra CMD_OUTPUT < <(shasum -a256 Cryptomator-*.dmg) + echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT - name: Add possible alpha/beta tags to installer name - run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg + run: mv Cryptomator-*.dmg "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg" - name: Create detached GPG signature with key 615D449FE6E6A235 run: | echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import @@ -270,7 +302,7 @@ jobs: run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db continue-on-error: true - name: Upload artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: dmg-${{ matrix.output-suffix }} path: | @@ -278,9 +310,10 @@ jobs: Cryptomator-*.asc if-no-files-found: error - name: Publish dmg on GitHub Releases - if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published' - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 + if: inputs.upload-to-draft + uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 with: + draft: true fail_on_unmatched_files: true token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} files: | diff --git a/.github/workflows/no-response.yml b/.github/workflows/no-response.yml index 6585256bb..8a4caeb87 100644 --- a/.github/workflows/no-response.yml +++ b/.github/workflows/no-response.yml @@ -7,12 +7,12 @@ on: jobs: no-response: - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: issues: write pull-requests: write steps: - - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.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 619f0f607..24cb9cb16 100644 --- a/.github/workflows/post-publish.yml +++ b/.github/workflows/post-publish.yml @@ -5,35 +5,141 @@ on: types: [published] jobs: - get-version: - runs-on: ubuntu-latest + notify: + runs-on: ubuntu steps: - - name: Download source tarball - run: | - 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 - echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz - env: - GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} - - name: Publish asc on GitHub Releases - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 - with: - fail_on_unmatched_files: true - token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} - files: | - cryptomator-*.tar.gz.asc - - name: Slack Notification - uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3 + - name: Notify about DEB build + uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }} SLACK_USERNAME: 'Cryptobot' - SLACK_ICON: false + SLACK_ICON: '' SLACK_ICON_EMOJI: ':bot:' SLACK_CHANNEL: 'cryptomator-desktop' SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published." SLACK_MESSAGE: "Ready to ." - SLACK_FOOTER: false - MSG_MINIMAL: true \ No newline at end of file + SLACK_FOOTER: '' + MSG_MINIMAL: true + - name: Notify about latest-version update + uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }} + SLACK_USERNAME: 'Cryptobot' + SLACK_ICON: '' + SLACK_ICON_EMOJI: ':bot:' + SLACK_CHANNEL: 'cryptomator-desktop' + SLACK_TITLE: "Requiring version check source update for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}." + SLACK_MESSAGE: 'Check S3 bucket for .' + SLACK_FOOTER: '' + MSG_MINIMAL: true + + get-asset-urls: + name: Get release asset URLs + runs-on: ubuntu-slim + outputs: + is-windows-release: ${{ steps.urls.outputs.urls-present }} + msi-url: ${{ steps.urls.outputs.msi }} + exe-url: ${{ steps.urls.outputs.exe }} + steps: + - name: Extract MSI and EXE download URLs + id: urls + run: | + MSI_URL=$(jq -r '[.[] | select(.name | endswith("-x64.msi"))][0].browser_download_url // "null"' <<< "$RELEASE_ASSETS") + EXE_URL=$(jq -r '[.[] | select(.name | endswith("-x64.exe"))][0].browser_download_url // "null"' <<< "$RELEASE_ASSETS") + if [[ "$MSI_URL" == "null" || -z "$MSI_URL" || "$EXE_URL" == "null" || -z "$EXE_URL" ]]; then + echo "urls-present=false" >> $GITHUB_OUTPUT + else + echo "urls-present=true" >> $GITHUB_OUTPUT + echo "msi=${MSI_URL}" >> $GITHUB_OUTPUT + echo "exe=${EXE_URL}" >> $GITHUB_OUTPUT + fi + env: + RELEASE_ASSETS: ${{ toJson(github.event.release.assets) }} + + allowlist-msi-x64: + needs: [get-asset-urls] + if: needs.get-asset-urls.outputs.is-windows-release == 'true' + uses: ./.github/workflows/av-whitelist.yml + with: + url: ${{ needs.get-asset-urls.outputs.msi-url }} + secrets: inherit + + allowlist-exe-x64: + needs: [get-asset-urls, allowlist-msi-x64] + if: needs.get-asset-urls.outputs.is-windows-release == 'true' + uses: ./.github/workflows/av-whitelist.yml + with: + url: ${{ needs.get-asset-urls.outputs.exe-url }} + secrets: inherit + + check-release: + name: Analyzes the release for certain properties + runs-on: ubuntu-slim + outputs: + release-kind: ${{steps.determine-kind.outputs.value}} # Possible values are [alpha, beta, rc, stable, unknown] + steps: + - id: determine-kind + run: | + SEM_VER_NUM=$(echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/') + SEM_VER_SUFFIX="${SEM_VER_STR#"$SEM_VER_NUM"}" + + TYPE="unknown" + if [[ -z $SEM_VER_SUFFIX ]]; then + TYPE="stable" + elif [[ $SEM_VER_SUFFIX =~ -alpha[1-9]+$ ]]; then + TYPE="alpha" + elif [[ $SEM_VER_SUFFIX =~ -beta[1-9]+$ ]]; then + TYPE="beta" + elif [[ $SEM_VER_SUFFIX =~ -rc[1-9]+$ ]]; then + TYPE="rc" + fi + echo "value=${TYPE}" >> $GITHUB_OUTPUT + env: + SEM_VER_STR: ${{ github.event.release.tag_name }} + + + notify-winget: + name: Notify for winget-release + if: needs.get-asset-urls.outputs.is-windows-release == 'true' && needs.check-release.outputs.release-kind == 'stable' + needs: [check-release, get-asset-urls] + runs-on: ubuntu + steps: + - name: Slack Notification + uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }} + SLACK_USERNAME: 'Cryptobot' + SLACK_ICON: '' + SLACK_ICON_EMOJI: ':bot:' + SLACK_CHANNEL: 'cryptomator-desktop' + SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published." + SLACK_MESSAGE: "Ready to ." + SLACK_FOOTER: '' + MSG_MINIMAL: true + + trigger-website-update: + needs: [check-release] + runs-on: ubuntu-slim + if: needs.check-release.outputs.release-kind == 'stable' + steps: + - name: Start website update workflow + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 + with: + event-type: desktop-release + token: ${{ secrets.CRYPTOBOT_WORKFLOW_DISPATCH_TOKEN }} + repository: cryptomator/cryptomator.github.io + client-payload: '{ "version": "${{ github.event.release.tag_name }}", "release": ${{ toJson(github.event.release.assets) }} }' + + trigger-docs-update: + needs: [check-release, get-asset-urls] + runs-on: ubuntu-slim + if: needs.get-asset-urls.outputs.is-windows-release == 'true' && needs.check-release.outputs.release-kind == 'stable' + steps: + - name: Start docs update workflow + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 + with: + event-type: desktop-release + token: ${{ secrets.CRYPTOBOT_WORKFLOW_DISPATCH_TOKEN }} + repository: cryptomator/docs + client-payload: '{ "version": "${{ github.event.release.tag_name }}", "release": ${{ toJson(github.event.release.assets) }} }' + diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index a0f6beefe..ac62a8ed4 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -5,7 +5,7 @@ on: env: JAVA_DIST: 'temurin' - JAVA_VERSION: 25 + JAVA_VERSION: 26 defaults: run: @@ -16,11 +16,11 @@ jobs: name: Compile and Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ env.JAVA_DIST }} java-version: ${{ env.JAVA_VERSION }} cache: 'maven' - name: Build and Test - run: xvfb-run mvn -B clean install jacoco:report -Pcoverage \ No newline at end of file + run: xvfb-run ./mvnw -B clean install jacoco:report -Pcoverage \ No newline at end of file diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml index 2e6779093..bb33c73b6 100644 --- a/.github/workflows/release-check.yml +++ b/.github/workflows/release-check.yml @@ -12,16 +12,16 @@ defaults: env: JAVA_DIST: 'temurin' - JAVA_VERSION: 25 + JAVA_VERSION: 26 jobs: check-preconditions: name: Validate commits pushed to release/hotfix branch to fulfill release requirements runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Java - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ env.JAVA_DIST }} java-version: ${{ env.JAVA_VERSION }} @@ -36,7 +36,7 @@ jobs: exit 1 fi - if [[ ${SEM_VER_STR} == `mvn help:evaluate -Dexpression=project.version -q -DforceStdout` ]]; then + if [[ ${SEM_VER_STR} == `./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout` ]]; then echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT else echo "Version not set in POM" @@ -48,19 +48,4 @@ jobs: 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" exit 1 - fi - - name: Cache NVD DB - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 - with: - path: ~/.m2/repository/org/owasp/dependency-check-data/ - key: dependency-check-${{ github.run_id }} - restore-keys: | - dependency-check - env: - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5 - - name: Run org.owasp:dependency-check plugin - id: dependency-check - continue-on-error: true - run: mvn -B verify -Pdependency-check -DskipTests - env: - NVD_API_KEY: ${{ secrets.NVD_API_KEY }} \ No newline at end of file + fi \ No newline at end of file diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 9f30d89a5..f44307060 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,12 +7,12 @@ on: jobs: stale: - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: issues: write pull-requests: write steps: - - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.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 b452acaf3..4a08409df 100644 --- a/.github/workflows/win-exe.yml +++ b/.github/workflows/win-exe.yml @@ -1,13 +1,48 @@ name: Build Windows Installer on: - release: - types: [published] + schedule: + - cron: '0 19 20 * *' + workflow_call: + inputs: + semVerNum: + type: string + description: 'The Major.Minor.Patch part of the version' + required: true + revisionNum: + type: string + description: 'The revision number' + required: true + semVerSuffix: + type: string + description: 'The suffix of the version, including dash' + required: true + sign: + description: 'Sign binaries' + default: true + type: boolean + upload-to-draft: + type: boolean + default: true + outputs: + sha256-msi: + description: "SHA256 sum of the x64 msi" + value: ${{ jobs.build-msi.outputs.sha256sum}} + sha256-exe: + description: "SHA256 sum of the x64 exe" + value: ${{ jobs.build-exe.outputs.sha256sum}} workflow_dispatch: inputs: - version: - description: 'Version' + semVerNum: + description: 'The Major.Minor.Patch part of the version' required: false + revisionNum: + description: 'The revision number' + required: false + semVerSuffix: + description: 'The suffix of the version, including dash' + required: false + default: '-SNAPSHOT' sign: description: 'Sign binaries' required: false @@ -22,8 +57,11 @@ on: env: - OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_windows-x64_bin-jmods.zip' - OPENJFX_JMODS_AMD64_HASH: '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3' + VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}} + REVISION_NUM: ${{ inputs.revisionNum || '0' }} + VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}} + OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_windows-x64_bin-jmods.zip' + OPENJFX_JMODS_AMD64_HASH: '0bf9b83260b85607a9ba200124debabd9cdb013cbc0d659e62a20192a7137907' WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi' WINFSP_MSI_HASH: '073a70e00f77423e34bed98b86e600def93393ba5822204fac57a29324db9f7a' WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe' @@ -34,27 +72,23 @@ defaults: shell: bash jobs: - get-version: - uses: ./.github/workflows/get-version.yml - with: - version: ${{ inputs.version }} - build-msi: name: Build .msi Installer runs-on: ${{ matrix.os }} - needs: [ get-version ] + outputs: + sha256sum: ${{ steps.sha256sum.outputs.value }} strategy: matrix: include: - arch: x64 os: windows-latest - java-dist: 'zulu' #cannot use temurin, see https://github.com/cryptomator/cryptomator/issues/3824#issuecomment-2829827427 - java-version: '25.0.1+8' + java-dist: 'temurin' + java-version: '26.0.1+8' java-package: 'jdk' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Setup Java - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ matrix.java-dist }} java-version: ${{ matrix.java-version }} @@ -85,7 +119,7 @@ jobs: JMOD_VERSION_AMD64=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1) JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@} JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*} - POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) + POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) POM_JFX_VERSION=${POM_JFX_VERSION#*@} POM_JFX_VERSION=${POM_JFX_VERSION%%.*} @@ -94,9 +128,9 @@ jobs: exit 1 fi - name: Set version - run: mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }} + run: ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}" - name: Run maven - run: mvn -B clean package -Pwin -DskipTests + run: ./mvnw -B clean package -Pwin -DskipTests - name: Patch target dir run: | cp LICENSE.txt target @@ -134,13 +168,13 @@ jobs: --dest appdir --name Cryptomator --vendor "Skymatic GmbH" - --copyright "(C) 2016 - 2025 Skymatic GmbH" - --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}" + --copyright "(C) 2016 - 2026 Skymatic GmbH" + --app-version "${VERSION_NUM}.${REVISION_NUM}" --java-options "--enable-preview" --java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win" --java-options "-Xss5m" --java-options "-Xmx256m" - --java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\"" + --java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\"" --java-options "-Dfile.encoding=\"utf-8\"" --java-options "-Djava.net.useSystemProxies=true" --java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\"" @@ -151,7 +185,7 @@ jobs: --java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\"" --java-options "-Dcryptomator.loopbackAlias=\"cryptomator-vault\"" --java-options "-Dcryptomator.showTrayIcon=true" - --java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\"" + --java-options "-Dcryptomator.buildNumber=\"msi-${REVISION_NUM}\"" --java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\"" --java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\"" --java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json\"" @@ -186,33 +220,32 @@ jobs: } $jar.Dispose() } - - name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130 - shell: pwsh + - name: Get msi helper dll for code signing run: | - New-Item -Path appdir/jpackage-jmod -ItemType Directory - & $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod" - Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir" + ${JAVA_HOME}/bin/jpackage --type msi --win-upgrade-uuid bda45523-42b1-4cae-9354-a45475ed4775 --app-image appdir/Cryptomator --dest /tmp/ --name Test --vendor Test --copyright "None" --app-version "1.0" --temp msi-helper + find ./msi-helper/ -type f -name msica.dll -exec mv {} ./appdir \; - name: Sign DLLs with Azure Trusted Signing - if: inputs.sign || github.event_name == 'release' + if: inputs.sign || github.event_name == 'schedule' uses: ./.github/actions/win-sign-action with: base-dir: ${{ github.workspace }}\appdir + file-extensions: 'exe,dll' recursive: true append-signature: true tenant-id: ${{ secrets.AZURE_TENANT_ID }} client-id: ${{ secrets.AZURE_CLIENT_ID }} client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - - name: Sign DLLs with Actalis CodeSigner - if: inputs.sign || github.event_name == 'release' - uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version + - name: Sign Scripts with Azure Trusted Signing + if: inputs.sign || github.event_name == 'schedule' + uses: ./.github/actions/win-sign-action with: - base-dir: 'appdir' - file-extensions: 'dll,exe,ps1' - recursive: true - sign-description: 'Cryptomator' - sign-url: 'https://cryptomator.org' - username: ${{ secrets.WIN_CODESIGN_USERNAME }} - password: ${{ secrets.WIN_CODESIGN_PW }} + base-dir: ${{ github.workspace }}\appdir\Cryptomator + file-extensions: 'ps1' + recursive: false + append-signature: false # Powershell scripts cannot be signed in append mode, see #4260 + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + client-id: ${{ secrets.AZURE_CLIENT_ID }} + client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - name: Replace DLLs inside jars with signed ones shell: pwsh run: | @@ -229,7 +262,7 @@ jobs: } - name: Generate license for MSI run: > - mvn -B license:add-third-party + ./mvnw -B license:add-third-party "-Dlicense.thirdPartyFilename=license.rtf" "-Dlicense.outputDirectory=dist/win/resources" "-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl" @@ -238,6 +271,16 @@ jobs: "-Dlicense.failOnMissing=true" "-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges" shell: pwsh + - name: Create file association file from template + working-directory: dist/win + run: | + $Env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT = "${Env:JP_WIXWIZARD_RESOURCES}".Replace('\', '\\'); + Get-Content .\resources\FAvaultFile.template.properties ` + | ForEach-Object { $ExecutionContext.InvokeCommand.ExpandString($_) } ` + | Out-File -FilePath .\resources\FAvaultFile.properties + env: + JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources/ # requires abs path, used in resources/main.wxs + shell: pwsh - name: Create MSI run: > ${JAVA_HOME}/bin/jpackage @@ -248,21 +291,21 @@ jobs: --dest installer --name Cryptomator --vendor "Skymatic GmbH" - --copyright "(C) 2016 - 2025 Skymatic GmbH" - --app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}" + --copyright "(C) 2016 - 2026 Skymatic GmbH" + --app-version "${VERSION_NUM}.${REVISION_NUM}" --win-menu --win-dir-chooser --win-shortcut-prompt - --win-update-url "https:\\cryptomator.org\downloads" + --win-update-url "https://cryptomator.org/downloads" --win-menu-group Cryptomator --resource-dir dist/win/resources --license-file dist/win/resources/license.rtf --file-associations dist/win/resources/FAvaultFile.properties env: - JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs - JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir + JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources/ # requires abs path, used in resources/main.wxs + JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir\ - name: Sign MSI with Azure Trusted Signing - if: inputs.sign || github.event_name == 'release' + if: inputs.sign || github.event_name == 'schedule' uses: ./.github/actions/win-sign-action with: base-dir: ${{ github.workspace }}\installer @@ -271,8 +314,12 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} client-id: ${{ secrets.AZURE_CLIENT_ID }} client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + - id: sha256sum + run: | + read -ra CMD_OUTPUT < <(sha256sum installer/Cryptomator-*.msi) + echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT - name: Add possible alpha/beta tags and architecture to installer name - run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.arch }}.msi + run: mv installer/Cryptomator-*.msi "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.msi" - name: Create detached GPG signature with key 615D449FE6E6A235 run: | echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import @@ -281,7 +328,7 @@ jobs: GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} - name: Upload artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: msi-${{ matrix.arch }} path: | @@ -292,18 +339,20 @@ jobs: build-exe: name: Build .exe installer runs-on: ${{ matrix.os }} - needs: [ get-version, build-msi ] + needs: [ build-msi ] + outputs: + sha256sum: ${{ steps.sha256sum.outputs.value }} strategy: matrix: include: - arch: x64 os: windows-latest executable-suffix: x64 - java-dist: 'zulu' - java-version: '24.0.1+9' + java-dist: 'temurin' + java-version: '26.0.1+8' java-package: 'jdk' steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Install wix and extensions run: | dotnet tool install --global wix --version ${WIX_VERSION} @@ -312,14 +361,14 @@ jobs: env: WIX_VERSION: ${{ env.WIX_VERSION }} - name: Download .msi - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: msi-${{ matrix.arch }} path: dist/win/bundle/resources - name: Strip version info from msi file name run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi - name: Setup Java - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0 with: distribution: ${{ matrix.java-dist }} java-version: ${{ matrix.java-version }} @@ -328,7 +377,7 @@ jobs: cache: 'maven' - name: Generate license for exe run: > - mvn -B license:add-third-party + ./mvnw -B license:add-third-party "-Dlicense.thirdPartyFilename=license.rtf" "-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl" "-Dlicense.outputDirectory=dist/win/bundle/resources" @@ -339,10 +388,10 @@ jobs: shell: pwsh - name: Download WinFsp run: | - 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)." + 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)." } env: WINFSP_PATH: 'dist/win/bundle/resources/winfsp.msi' @@ -356,21 +405,22 @@ jobs: run: > wix build -define BundleName="Cryptomator" - -define BundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}" + -define BundleVersion="${VERSION_NUM}.${REVISION_NUM}" -define BundleVendor="Skymatic GmbH" - -define BundleCopyright="(C) 2016 - 2025 Skymatic GmbH" + -define BundleCopyright="(C) 2016 - 2026 Skymatic GmbH" -define AboutUrl="https://cryptomator.org" -define HelpUrl="https://cryptomator.org/contact" -define UpdateUrl="https://cryptomator.org/downloads/" -ext "WixToolset.Util.wixext" -ext "WixToolset.BootstrapperApplications.wixext" ./bundle/bundleWithWinfsp.wxs - -out "../../installer/unsigned/Cryptomator-Installer.exe" + -out "../../installer/Cryptomator-Installer.exe" - name: Detach burn engine in preparation to sign + if: inputs.sign || github.event_name == 'schedule' run: > - wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe + wix burn detach installer/Cryptomator-Installer.exe -engine tmp/engine.exe - name: Sign WiX burn engine with Azure Trusted Signing - if: inputs.sign || github.event_name == 'release' + if: inputs.sign || github.event_name == 'schedule' uses: ./.github/actions/win-sign-action with: base-dir: ${{ github.workspace }}\tmp @@ -380,21 +430,14 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} client-id: ${{ secrets.AZURE_CLIENT_ID }} client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - - name: Sign burn engine with Actalis CodeSigner - if: inputs.sign || github.event_name == 'release' - uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version - with: - base-dir: 'tmp' - file-extensions: 'exe' - sign-description: 'Cryptomator Bundle Installer' - sign-url: 'https://cryptomator.org' - username: ${{ secrets.WIN_CODESIGN_USERNAME }} - password: ${{ secrets.WIN_CODESIGN_PW }} - name: Reattach signed burn engine to installer - run: > - wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe + if: inputs.sign || github.event_name == 'schedule' + shell: pwsh + run: | + Move-Item -Path installer/Cryptomator-Installer.exe -Destination tmp/Cryptomator-Installer.exe + wix burn reattach tmp/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe - name: Sign EXE installer with Azure Trusted Signing - if: inputs.sign || github.event_name == 'release' + if: inputs.sign || github.event_name == 'schedule' uses: ./.github/actions/win-sign-action with: base-dir: ${{ github.workspace }}\installer @@ -404,18 +447,12 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} client-id: ${{ secrets.AZURE_CLIENT_ID }} client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - - name: Sign installer with Actalis CodeSigner - if: inputs.sign || github.event_name == 'release' - uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version - with: - base-dir: 'installer' - file-extensions: 'exe' - sign-description: 'Cryptomator Bundle Installer' - sign-url: 'https://cryptomator.org' - username: ${{ secrets.WIN_CODESIGN_USERNAME }} - password: ${{ secrets.WIN_CODESIGN_PW }} + - id: sha256sum + run: | + read -ra CMD_OUTPUT < <(sha256sum installer/Cryptomator-*.exe) + echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT - name: Add possible alpha/beta tags to installer name - run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.executable-suffix }}.exe + run: mv installer/Cryptomator-Installer.exe "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.executable-suffix }}.exe" - name: Create detached GPG signature with key 615D449FE6E6A235 run: | echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import @@ -424,7 +461,7 @@ jobs: GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }} - name: Upload artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: exe-${{ matrix.executable-suffix }} path: | @@ -434,58 +471,22 @@ jobs: publish: name: Publish installers to the github release - if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published' + if: inputs.upload-to-draft runs-on: ubuntu-latest needs: [ build-msi, build-exe ] - outputs: - download-url-msi-x64: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }} - download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }} steps: - name: Download installers - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: merge-multiple: true - name: Publish installers on GitHub Releases id: publish - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 + uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 with: + draft: true fail_on_unmatched_files: true token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} - # do not change ordering of filelist, required for correct job output files: | *x64.msi *x64.exe *.asc - - allowlist-msi-x64: - uses: ./.github/workflows/av-whitelist.yml - needs: [ publish ] - with: - url: ${{ needs.publish.outputs.download-url-msi-x64 }} - secrets: inherit - - allowlist-exe-x64: - uses: ./.github/workflows/av-whitelist.yml - needs: [ publish, allowlist-msi-x64 ] - with: - url: ${{ needs.publish.outputs.download-url-exe-x64 }} - secrets: inherit - - notify-winget: - name: Notify for winget-release - if: needs.get-version.outputs.versionType == 'stable' - needs: [publish, get-version] - runs-on: ubuntu-latest - steps: - - name: Slack Notification - 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: "MSI packages of ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published." - SLACK_MESSAGE: "Ready to ." - SLACK_FOOTER: false - MSG_MINIMAL: true diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 5ab150229..e5a994243 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -18,7 +18,7 @@ jobs: env: GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }} - name: Submit package - uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # no_specific_version + uses: vedantmgoyal2009/winget-releaser@7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 # no_specific_version with: identifier: Cryptomator.Cryptomator version: ${{ inputs.tag }} diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000..e60559f66 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,4 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip +distributionSha256Sum=5af3b743dd8b876b5c45da33b676251e5f1687712644abb4ee519ca56e1d89ce \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b9d1a6ed..e70629c9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The changelog starts with version 1.19.0. Changes to prior versions can be found on the [Github release page](https://github.com/cryptomator/cryptomator/releases). + +## [1.19.3](https://github.com/cryptomator/cryptomator/releases/1.19.3) - 2026-06-29 +### Added +* New error dialog if importing a vault fails ([#4243](https://github.com/cryptomator/cryptomator/pull/4243)) + +### Fixed +* Fixed Cryptomator file extensions were not registered on Windows ([#4219](https://github.com/cryptomator/cryptomator/issues/4219)) +* Fixed warning was displayed when accessing update tab in settings even though an update check did not ran ([#4199](https://github.com/cryptomator/cryptomator/pull/4199)) +* Fixed several Decrypt Name dialogs could be opened on the same vault ([#4164](https://github.com/cryptomator/cryptomator/pull/4164)) +* Fixed not all mount options in vault specific settings could be displayed ([#4227](https://github.com/cryptomator/cryptomator/pull/4227)) +* Fixed localhost alias on Windows was not removed on uninstall ([#3993](https://github.com/cryptomator/cryptomator/issues/3993)) + +### Changed +* Refactored release pipeline to allow immutable releases ([#4205](https://github.com/cryptomator/cryptomator/pull/4205)) +* Updated to JDK 26.0.1 ([#4244](https://github.com/cryptomator/cryptomator/pull/4244)) +* Updated to JavaFX 25.0.3 ([#4255](https://github.com/cryptomator/cryptomator/pull/4255)) +* Drop signing with Actalis issued certificate ([#4169](https://github.com/cryptomator/cryptomator/pull/4169), [#4262](https://github.com/cryptomator/cryptomator/pull/4262)) +* Fix dagger binding graph issues ([#4147](https://github.com/cryptomator/cryptomator/pull/4147)) +* Added flatpak build to CI ([#4199](https://github.com/cryptomator/cryptomator/pull/4199)) +* Updated dependencies: + - `org.cryptomator:webdav-nio-adapter` from 3.0.1 to 3.0.2 + - `org.cryptomator:integrations-api` from 1.8.0 to 1.9.0 + - `org.slf4j:slf4j-api` from 2.0.17 to 2.0.18 + - `ch.qos.logback:logback-core` from 1.5.32 to 1.5.35 + - `ch.qos.logback:logback-classic` from 1.5.32 to 1.5.35 + - `com.auth0:java-jwt` from 4.5.1 4.5.2 + - `com.fasterxml.jackson.core:jackson-databind` from 2.21.1 to 2.21.4 + - `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.21.1 to 2.21.4 + - `com.github.ben-manes.caffeine:caffeine` from 3.2.3 to 3.2.4 + + ## [1.19.2](https://github.com/cryptomator/cryptomator/releases/1.19.2) - 2026-03-20 ### Security diff --git a/README.md b/README.md index e92262a79..00415668e 100644 --- a/README.md +++ b/README.md @@ -79,15 +79,11 @@ For more information on the security details visit [cryptomator.org](https://doc ### Dependencies * JDK 25 (e.g. temurin, zulu) -* Maven 3 ### Run Maven ``` -mvn clean install -# or mvn clean install -Pwin -# or mvn clean install -Pmac -# or mvn clean install -Plinux +./mvnw clean install ``` This will build all the jars and bundle them together with their OS-specific dependencies under `target`. This can now be used to build native packages. diff --git a/dist/linux/appimage/build.sh b/dist/linux/appimage/build.sh index 8beb229de..6c6cfd595 100755 --- a/dist/linux/appimage/build.sh +++ b/dist/linux/appimage/build.sh @@ -6,29 +6,29 @@ REVISION_NO=`git rev-list --count HEAD` # check preconditions if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi -command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found."; exit 1; } +[ -x ../../../mvnw ] || { echo >&2 "mvnw not found at ../../../mvnw."; exit 1; } command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; } command -v unzip >/dev/null 2>&1 || { echo >&2 "unzip not found."; exit 1; } -VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout) +VERSION=$(../../../mvnw -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout) SEMVER_STR=${VERSION} CPU_ARCH=$(uname -m) if [[ ! "${CPU_ARCH}" =~ x86_64|aarch64 ]]; then echo "Platform ${CPU_ARCH} not supported"; exit 1; fi -mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR} +../../../mvnw -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR} # compile -mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests +../../../mvnw -B -f ../../../pom.xml clean package -DskipTests cp ../../../LICENSE.txt ../../../target cp ../../../target/cryptomator-*.jar ../../../target/mods -JAVAFX_VERSION=25.0.2 +JAVAFX_VERSION=25.0.3 JAVAFX_ARCH="x64" -JAVAFX_JMODS_SHA256='e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b' +JAVAFX_JMODS_SHA256='47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50' if [ "${CPU_ARCH}" = "aarch64" ]; then JAVAFX_ARCH="aarch64" - JAVAFX_JMODS_SHA256='c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646' + JAVAFX_JMODS_SHA256='e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f' fi # download javaFX jmods @@ -42,7 +42,7 @@ unzip -o -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/jav JMOD_VERSION=$(jmod describe ./openjfx-jmods/javafx.base.jmod | head -1) JMOD_VERSION=${JMOD_VERSION#*@} JMOD_VERSION=${JMOD_VERSION%%.*} -POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout -B -f ../../../pom.xml) +POM_JFX_VERSION=$(../../../mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout -B -f ../../../pom.xml) POM_JFX_VERSION=${POM_JFX_VERSION#*@} POM_JFX_VERSION=${POM_JFX_VERSION%%.*} if [ $POM_JFX_VERSION -ne $JMOD_VERSION ]; then @@ -82,7 +82,7 @@ ${JAVA_HOME}/bin/jpackage \ --vendor "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" \ - --copyright "(C) 2016 - 2025 Skymatic GmbH" \ + --copyright "(C) 2016 - 2026 Skymatic GmbH" \ --java-options "-Xss5m" \ --java-options "-Xmx256m" \ --app-version "${VERSION}.${REVISION_NO}" \ diff --git a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml index 8a788e537..c32020758 100644 --- a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml +++ b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml @@ -84,6 +84,9 @@ + + https://github.com/cryptomator/cryptomator/releases/1.19.3 + https://github.com/cryptomator/cryptomator/releases/1.19.2 diff --git a/dist/linux/debian/control b/dist/linux/debian/control index 713f03972..5d6535a4b 100644 --- a/dist/linux/debian/control +++ b/dist/linux/debian/control @@ -2,7 +2,7 @@ Source: cryptomator Maintainer: Cryptobot Section: utils Priority: optional -Build-Depends: debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1 +Build-Depends: debhelper (>=10), coffeelibs-jdk-26 (>= 26.0.1+8-0ppa1), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1 Standards-Version: 4.5.0 Homepage: https://cryptomator.org Vcs-Git: https://github.com/cryptomator/cryptomator.git diff --git a/dist/linux/debian/rules b/dist/linux/debian/rules index 0fefef2e0..d2acc4cd4 100755 --- a/dist/linux/debian/rules +++ b/dist/linux/debian/rules @@ -4,12 +4,11 @@ # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 +JAVA_HOME = /usr/lib/jvm/java-26-coffeelibs DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH) ifeq ($(DEB_BUILD_ARCH),amd64) -JAVA_HOME = /usr/lib/jvm/java-25-openjdk-amd64 JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods else ifeq ($(DEB_BUILD_ARCH),arm64) -JAVA_HOME = /usr/lib/jvm/java-25-openjdk-arm64 JMODS_PATH = jmods/aarch64:${JAVA_HOME}/jmods endif @@ -46,7 +45,7 @@ override_dh_auto_build: --vendor "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" \ - --copyright "(C) 2016 - 2025 Skymatic GmbH" \ + --copyright "(C) 2016 - 2026 Skymatic GmbH" \ --java-options "-Xss5m" \ --java-options "-Xmx256m" \ --java-options "-Dfile.encoding=\"utf-8\"" \ diff --git a/dist/linux/flatpak/build-aux/fusermount-wrapper.sh b/dist/linux/flatpak/build-aux/fusermount-wrapper.sh new file mode 100644 index 000000000..8df901d35 --- /dev/null +++ b/dist/linux/flatpak/build-aux/fusermount-wrapper.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# From: https://gitlab.gnome.org/GNOME/gnome-builder/-/blob/main/build-aux/flatpak/fusermount-wrapper.sh + +if [ -z "$_FUSE_COMMFD" ]; then + FD_ARGS= +else + FD_ARGS="--env=_FUSE_COMMFD=${_FUSE_COMMFD} --forward-fd=${_FUSE_COMMFD}" +fi + +if [ -e /proc/self/fd/3 ] && [ 3 != "$_FUSE_COMMFD" ]; then + FD_ARGS="$FD_ARGS --forward-fd=3" +fi + +exec flatpak-spawn --host --forward-fd=1 --forward-fd=2 $FD_ARGS fusermount3 "$@" diff --git a/dist/linux/flatpak/org.cryptomator.Cryptomator.TEMPLATE.yaml b/dist/linux/flatpak/org.cryptomator.Cryptomator.TEMPLATE.yaml new file mode 100644 index 000000000..d65f6f1ea --- /dev/null +++ b/dist/linux/flatpak/org.cryptomator.Cryptomator.TEMPLATE.yaml @@ -0,0 +1,182 @@ +app-id: org.cryptomator.Cryptomator +command: cryptomator +runtime: org.freedesktop.Platform +runtime-version: '25.08' +sdk: org.freedesktop.Sdk +separate-locales: false +finish-args: + # Required for FUSE, see https://github.com/flathub/org.cryptomator.Cryptomator/pull/68#issuecomment-1935136502 + - --device=all + # Set the PATH environment variable in the application, as flatpak is resetting the shell's PATH + - --env=PATH=/app/bin/:/usr/bin/ + # Allow filesystem access to the user's home dir + # Needed to manage vaults there + - --filesystem=home + # Reading system certificates + - --filesystem=host-etc:ro + # Allow access to the XDG data directory + # Needed to connect to KeePassXC's UNIX domain socket + - --filesystem=xdg-run/org.keepassxc.KeePassXC.BrowserServer + - --filesystem=xdg-run/app/org.keepassxc.KeePassXC/ + # Share IPC namespace with the host, without it the X11 shared memory extension will not work + - --share=ipc + # Allow access to the network + - --share=network + # Show windows using X11 + - --socket=x11 + # Needed to reveal encrypted files + - --talk-name=org.freedesktop.FileManager1 + # Run any command on the host + # Needed to spawn fusermount on the host + - --talk-name=org.freedesktop.Flatpak + # Allow desktop notifications + - --talk-name=org.freedesktop.Notifications + # Allow access to the GNOME secret service API and to talk to the GNOME keyring daemon + - --talk-name=org.freedesktop.secrets + - --talk-name=org.gnome.keyring + # Allow to talk to the KDE kwallet daemon + - --talk-name=org.kde.kwalletd5 + - --talk-name=org.kde.kwalletd6 + # Needed to talk to the gvfs daemons over D-Bus and list mounts using the GIO APIs + - --talk-name=org.gtk.vfs.* + # Allow access to appindicator icons + - --talk-name=org.ayatana + # Allow access to appindicator icons on KDE + - --talk-name=org.kde.StatusNotifierWatcher +cleanup: + - /include + - /lib/pkgconfig +modules: + - shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json + - name: libfuse + buildsystem: meson + config-opts: + - -Dexamples=false + - -Dinitscriptdir= + - -Duseroot=false + - -Dtests=false + # don't install rules on the host + - -Dudevrulesdir=/tmp/ + sources: + - type: archive + url: https://github.com/libfuse/libfuse/releases/download/fuse-3.16.2/fuse-3.16.2.tar.gz + sha256: f797055d9296b275e981f5f62d4e32e089614fc253d1ef2985851025b8a0ce87 + x-checker-data: + type: anitya + project-id: 861 + url-template: https://github.com/libfuse/libfuse/releases/download/fuse-$version/fuse-$version.tar.gz + versions: {<: '3.17.0'} + - name: host-command-wrapper + buildsystem: simple + build-commands: + - install fusermount-wrapper.sh /app/bin/fusermount3 + sources: + - type: file + path: build-aux/fusermount-wrapper.sh + - name: cryptomator + buildsystem: simple + build-options: + build-args: + - --share=network + env: + PATH: /app/bin:/usr/bin + MAVEN_OPTS: -Dmaven.repo.local=.m2/repository + JAVA_HOME: jdk + JMODS_PATH: jmods + VERSION: $FLATPAK_VERSION + REVISION_NO: '$FLATPAK_REVISION' + build-commands: + # Setup Java + - tar xvfz jdk.tar.gz --transform 's!^[^/]*!jdk!' + - mkdir jmods + - unzip -j openjfx.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods + # Setup Maven + - mkdir maven + - tar xf maven.tar.gz --strip-components=1 --exclude=jansi-native --directory=maven + # Build project + - maven/bin/mvn clean package -DskipTests + - 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 + --no-header-files + --no-man-pages + --strip-debug + --compress=zip-0 + - $JAVA_HOME/bin/jpackage + --type app-image + --runtime-image runtime + --input target/libs + --module-path target/mods + --module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator + --dest . + --name Cryptomator + --vendor 'Skymatic GmbH' + --copyright '(C) 2016 - 2026 Skymatic GmbH' + --java-options '--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator' + --java-options "--sun-misc-unsafe-memory-access=allow" + --java-options '-Xss5m' + --java-options '-Xmx256m' + --java-options '-Dfile.encoding='utf-8'' + --java-options '-Djava.net.useSystemProxies=true' + --java-options "-Dcryptomator.appVersion='${VERSION}'" + --java-options "-Dcryptomator.buildNumber='flatpak-${REVISION_NO}'" + --java-options '-Dcryptomator.ipcSocketPath='@{userhome}/.config/Cryptomator/ipc.socket'' + --java-options '-Dcryptomator.adminConfigPath='/run/host/etc/cryptomator/config.properties'' + --java-options '-Dcryptomator.logDir='@{userhome}/.local/share/Cryptomator/logs'' + --java-options '-Dcryptomator.mountPointsDir='@{userhome}/.local/share/Cryptomator/mnt'' + --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' + --java-options '-Dcryptomator.updateMechanism=org.cryptomator.linux.update.FlatpakUpdater' + --java-options '-Dcryptomator.networking.truststore.p12Path='/run/host/etc/cryptomator/certs.p12'' + --java-options '-Dcryptomator.hub.enableTrustOnFirstUse=true' + --app-version "${VERSION}.${REVISION_NO}" + --verbose + - cp -R Cryptomator /app/ + - ln -s /app/Cryptomator/bin/Cryptomator /app/bin/cryptomator + - cp -R /app/lib/* /app/Cryptomator/lib/app/ + - install -D -m0644 -t /app/share/applications/ dist/linux/common/org.cryptomator.Cryptomator.desktop + - install -D -m0644 -t /app/share/icons/hicolor/scalable/apps/ dist/linux/common/org.cryptomator.Cryptomator.svg + - install -D -m0644 -T dist/linux/common/org.cryptomator.Cryptomator.tray.svg /app/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg + - install -D -m0644 -T dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg /app/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg + - install -D -m0644 -t /app/share/metainfo/ dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml + sources: + - $CRYPTOMATOR_SOURCE + - type: file + dest-filename: jdk.tar.gz + only-arches: + - x86_64 + url: https://github.com/adoptium/temurin26-binaries/releases/download/jdk-26.0.1%2B8/OpenJDK26U-jdk_x64_linux_hotspot_26.0.1_8.tar.gz + sha512: eb46cda97ffd46e2e0c1f6f977dc204d9cc969b958946287b7e7d0bfe859fd92faccb2f6ef79995421a963c6de140c436af559403e0a2cd27c90b06c20260d5c + - type: file + dest-filename: jdk.tar.gz + only-arches: + - aarch64 + url: https://github.com/adoptium/temurin26-binaries/releases/download/jdk-26.0.1%2B8/OpenJDK26U-jdk_aarch64_linux_hotspot_26.0.1_8.tar.gz + sha512: 3c31671552712a8f0df96df2eeae7e9ad5156c0e98ebd5bf4fa04c14bba58bd5e19ff567ddcdc7aa478f6f4b0a852762a09bd88d3132acb121e667cfe0397034 + - type: file + dest-filename: openjfx.zip + only-arches: + - x86_64 + url: https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-x64_bin-jmods.zip + sha512: 75605440f13d0337e70ada1220f77d0d9780fb4602e676f1f07674d8b2e296b25080549d07edcca8f66733e15388860e3472d4ff6d51fd72b0452ed8bbe78022 + - type: file + dest-filename: openjfx.zip + only-arches: + - aarch64 + url: https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-aarch64_bin-jmods.zip + sha512: 30c509b880ced1b29e0fafa41073d2350a16296d9439124b9a0e3bb391bf7f27e34d5abcfb338df11e5e898175699bceac40574bc9ad123ff071a5964f8fb14d + - type: file + dest-filename: maven.tar.gz + url: https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.9.13/apache-maven-3.9.13-bin.tar.gz + sha512: d9ccd44ba2991586e359c29eb86780ae8ff4ec1b88b0b8af3af074803472690cf2017782a9c4401343c62cbcd056231db9612e1e551cbd9747c21746d732c015 + x-checker-data: + type: anitya + project-id: 1894 + stable-only: true + url-template: https://repo1.maven.org/maven2/org/apache/maven/apache-maven/$version/apache-maven-$version-bin.tar.gz + versions: {<: '4.0'} diff --git a/dist/linux/makepkg/PKGBUILD.template b/dist/linux/makepkg/PKGBUILD.template index e29a4396c..cad044b1d 100644 --- a/dist/linux/makepkg/PKGBUILD.template +++ b/dist/linux/makepkg/PKGBUILD.template @@ -14,8 +14,8 @@ 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 +_jdkver=26.0.1+8 +_jfxver=25.0.3 _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" @@ -24,10 +24,10 @@ source_aarch64=("jdk-${_jdkver}.tar.gz::https://github.com/adoptium/temurin${_jd "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') +sha256sums_x86_64=('8e512f13e575a43655fc92319436c94890c137b9035cc6bd6f9cf24239704d3a' + '47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50') +sha256sums_aarch64=('613f9b2861dea937b24d5eca745ef8567733b377d0bb612195acaad0e3f61360' + 'e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f') options=('!strip') validpgpkeys=('58117AFA1F85B3EEC154677D615D449FE6E6A235') @@ -47,7 +47,7 @@ build() { cd "${srcdir}/${_src_app_dir}" - mvn -B clean package -DskipTests -Plinux + mvn -B clean package -DskipTests cp LICENSE.txt target cp target/cryptomator-*.jar target/mods diff --git a/dist/mac/dmg/build.sh b/dist/mac/dmg/build.sh index 53abb794f..b90d2b033 100755 --- a/dist/mac/dmg/build.sh +++ b/dist/mac/dmg/build.sh @@ -24,29 +24,29 @@ rm -rf runtime dmg *.app *.dmg # set variables APP_NAME="Cryptomator" VENDOR="Skymatic GmbH" -COPYRIGHT_YEARS="2016 - 2025" +COPYRIGHT_YEARS="2016 - 2026" PACKAGE_IDENTIFIER="org.cryptomator" MAIN_JAR_GLOB="cryptomator-*.jar" MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator" REVISION_NO=`git rev-list --count HEAD` -VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'` +VERSION_NO=`../../../mvnw -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'` FUSE_LIB="FUSE-T" -JAVAFX_VERSION=25.0.2 +JAVAFX_VERSION=25.0.3 JAVAFX_ARCH="undefined" JAVAFX_JMODS_SHA256="undefined" if [ "$(machine)" = "arm64e" ]; then JAVAFX_ARCH="aarch64" - JAVAFX_JMODS_SHA256="4cd258001c75af7047005c5c891e2400ed11d24fbb09412324c0cbaf8b503c5a" + JAVAFX_JMODS_SHA256="a52014d625b8b04e57fd71650f881c1397542b4018e1b04f1b4e66c8800a1f34" else JAVAFX_ARCH="x64" - JAVAFX_JMODS_SHA256="0b4d8463f03901b7425d94628e4116b7078abb8dd540fbec415266fac20bda5c" + JAVAFX_JMODS_SHA256="3512fabe43aee467538d329cfbbaab3c53dff2a810f0d54e381f461d5e0fac43" fi JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip" # check preconditions if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi -command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found. Fix by 'brew install maven'."; exit 1; } +[ -x ../../../mvnw ] || { echo >&2 "mvnw not found at ../../../mvnw."; exit 1; } command -v create-dmg >/dev/null 2>&1 || { echo >&2 "create-dmg not found. Fix by 'brew install create-dmg'."; exit 1; } if [ -n "${CODESIGN_IDENTITY}" ]; then command -v codesign >/dev/null 2>&1 || { echo >&2 "codesign not found. Fix by 'xcode-select --install'."; exit 1; } @@ -61,7 +61,7 @@ unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javaf JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1) JMOD_VERSION=${JMOD_VERSION#*@} JMOD_VERSION=${JMOD_VERSION%%.*} -POM_JFX_VERSION=$(mvn -f../../../pom.xml help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) +POM_JFX_VERSION=$(../../../mvnw -f../../../pom.xml help:evaluate "-Dexpression=javafx.version" -q -DforceStdout) POM_JFX_VERSION=${POM_JFX_VERSION#*@} POM_JFX_VERSION=${POM_JFX_VERSION%%.*} @@ -71,7 +71,7 @@ if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then fi # compile -mvn -B -f../../../pom.xml clean package -DskipTests -Pmac +../../../mvnw -B -f../../../pom.xml clean package -DskipTests -Pmac cp ../../../LICENSE.txt ../../../target cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods @@ -137,7 +137,7 @@ sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Inf cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/ # generate license -mvn -B -f../../../pom.xml license:add-third-party \ +../../../mvnw -B -f../../../pom.xml license:add-third-party \ -Dlicense.thirdPartyFilename=license.rtf \ -Dlicense.outputDirectory=dist/mac/dmg/resources \ -Dlicense.fileTemplate=resources/licenseTemplate.ftl \ diff --git a/dist/win/build.ps1 b/dist/win/build.ps1 index cd5fa805d..246360224 100644 --- a/dist/win/build.ps1 +++ b/dist/win/build.ps1 @@ -16,6 +16,19 @@ Param( # Function Definitions Section # ============================ +function Invoke-CommandWithExitCheck { + param ( + [string]$Command, + [string[]]$Arguments + ) + + & $Command @Arguments + if ($LASTEXITCODE -ne 0) { + Write-Error "Command '$Command' failed with exit code $LASTEXITCODE" + exit $LASTEXITCODE + } +} + function Main { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 @@ -65,7 +78,8 @@ Write-Host "`$Env:JAVA_HOME=$Env:JAVA_HOME" $copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor" # compile -&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin +Invoke-CommandWithExitCheck -Command ` + "mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "clean", "package", "-DskipTests", "-Pwin") Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods" # add runtime @@ -93,9 +107,9 @@ switch ($archName) { $jmodPaths = "$Env:JAVA_HOME/jmods" } 'x64' { - $javaFxVersion='25.0.2' + $javaFxVersion='25.0.3' $javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip" - $javaFxJmodsSHA256 = '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3' + $javaFxJmodsSHA256 = '0bf9b83260b85607a9ba200124debabd9cdb013cbc0d659e62a20192a7137907' $javaFxJmods = '.\resources\jfxJmods.zip' if( !(Test-Path -Path $javaFxJmods) ) { @@ -129,16 +143,18 @@ if ((& "$Env:JAVA_HOME\bin\jlink" --help | Select-String -Pattern "Linking from } ### create runtime -& "$Env:JAVA_HOME\bin\jlink" ` - --verbose ` - --output runtime ` - --module-path $jmodPaths ` - --add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml ` - --strip-native-commands ` - --no-header-files ` - --no-man-pages ` - --strip-debug ` - --compress "zip-0" #do not compress and use msi compression +Invoke-CommandWithExitCheck -Command ` + "$Env:JAVA_HOME\bin\jlink" -Arguments @( + "--verbose", + "--output", "runtime", + "--module-path", $jmodPaths, + "--add-modules", "java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml", + "--strip-native-commands", + "--no-header-files", + "--no-man-pages", + "--strip-debug", + "--compress", "zip-0" #do not compress and use msi compression + ) $appPath = ".\$AppName" if ($clean -and (Test-Path -Path $appPath)) { @@ -195,14 +211,15 @@ if ($LASTEXITCODE -ne 0) { } #Create RTF license for msi -&mvn -B -f $buildDir/../../pom.xml license:add-third-party ` - "-Dlicense.thirdPartyFilename=license.rtf" ` - "-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" ` - "-Dlicense.outputDirectory=$buildDir\resources\" ` - "-Dlicense.includedScopes=compile" ` - "-Dlicense.excludedGroups=^org\.cryptomator" ` - "-Dlicense.failOnMissing=true" ` - "-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges" +Invoke-CommandWithExitCheck -Command ` + "mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "license:add-third-party", ` + "-Dlicense.thirdPartyFilename=license.rtf", ` + "-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl", ` + "-Dlicense.outputDirectory=$buildDir\resources\", ` + "-Dlicense.includedScopes=compile", ` + "-Dlicense.excludedGroups=^org\.cryptomator", ` + "-Dlicense.failOnMissing=true", ` + "-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges") # patch app dir Copy-Item "contrib\*" -Destination "$AppName" @@ -210,42 +227,46 @@ attrib -r "$AppName\$AppName.exe" attrib -r "$AppName\${AppName} (Debug).exe" # create .msi -$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources" -$Env:JP_WIXHELPER_DIR = "." -& "$Env:JAVA_HOME\bin\jpackage" ` - --verbose ` - --type msi ` - --win-upgrade-uuid $UpgradeUUID ` - --app-image $AppName ` - --dest installer ` - --name $AppName ` - --vendor $Vendor ` - --copyright $copyright ` - --app-version "$semVerNo.$revisionNo" ` - --win-menu ` - --win-dir-chooser ` - --win-shortcut-prompt ` - --win-menu-group $AppName ` - --resource-dir resources ` - --license-file resources/license.rtf ` - --win-update-url $UpdateUrl ` - --about-url $AboutUrl ` - --file-associations resources/FAvaultFile.properties +$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources\" +$Env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT = "${Env:JP_WIXWIZARD_RESOURCES}".Replace('\', '\\'); +$Env:JP_WIXHELPER_DIR = "" -if ($LASTEXITCODE -ne 0) { - Write-Error "jpackage MSI failed with exit code $LASTEXITCODE" - return 1; -} +Get-Content .\resources\FAvaultFile.template.properties ` # Similar to envsubst + | ForEach-Object { $ExecutionContext.InvokeCommand.ExpandString($_) } ` + | Out-File -FilePath .\resources\FAvaultFile.properties + +Invoke-CommandWithExitCheck -Command ` + "$Env:JAVA_HOME\bin\jpackage" -Arguments @( + "--verbose", + "--type", "msi", + "--win-upgrade-uuid", $UpgradeUUID, + "--app-image", $AppName, + "--dest", "installer", + "--name", $AppName, + "--vendor", $Vendor, + "--copyright", $copyright, + "--app-version", "$semVerNo.$revisionNo", + "--win-menu", + "--win-dir-chooser", + "--win-shortcut-prompt", + "--win-menu-group", $AppName, + "--resource-dir", "resources", + "--license-file", "resources/license.rtf", + "--win-update-url", $UpdateUrl, + "--about-url", $AboutUrl, + "--file-associations", "resources/FAvaultFile.properties" + ) #Create RTF license for bundle -&mvn -B -f $buildDir/../../pom.xml license:add-third-party ` - "-Dlicense.thirdPartyFilename=license.rtf" ` - "-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" ` - "-Dlicense.outputDirectory=$buildDir\bundle\resources\" ` - "-Dlicense.includedScopes=compile" ` - "-Dlicense.excludedGroups=^org\.cryptomator" ` - "-Dlicense.failOnMissing=true" ` - "-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges" +Invoke-CommandWithExitCheck -Command ` + "mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "license:add-third-party", ` + "-Dlicense.thirdPartyFilename=license.rtf", ` + "-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl", ` + "-Dlicense.outputDirectory=$buildDir\bundle\resources\", ` + "-Dlicense.includedScopes=compile", ` + "-Dlicense.excludedGroups=^org\.cryptomator", ` + "-Dlicense.failOnMissing=true", ` + "-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges") # download Winfsp $winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi' @@ -271,18 +292,21 @@ Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninsta Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi" -Force # create bundle including winfsp -& wix build ` - -define BundleName="$AppName" ` - -define BundleVersion="$semVerNo.$revisionNo" ` - -define BundleVendor="$Vendor" ` - -define BundleCopyright="$copyright" ` - -define AboutUrl="$AboutUrl" ` - -define HelpUrl="$HelpUrl" ` - -define UpdateUrl="$UpdateUrl" ` - -ext "WixToolset.Util.wixext" ` - -ext "WixToolset.BootstrapperApplications.wixext" ` - .\bundle\bundleWithWinfsp.wxs ` - -out "installer\$AppName-Installer.exe" +Invoke-CommandWithExitCheck -Command ` + "wix" -Arguments @( + "build", + "-define", "BundleName=$AppName", + "-define", "BundleVersion=$semVerNo.$revisionNo", + "-define", "BundleVendor=$Vendor", + "-define", "BundleCopyright=$copyright", + "-define", "AboutUrl=$AboutUrl", + "-define", "HelpUrl=$HelpUrl", + "-define", "UpdateUrl=$UpdateUrl", + "-ext", "WixToolset.Util.wixext", + "-ext", "WixToolset.BootstrapperApplications.wixext", + ".\bundle\bundleWithWinfsp.wxs", + "-out", ".\installer\$AppName-Installer.exe" +) Write-Host "Created EXE installer .\installer\$AppName-Installer.exe" return 0; diff --git a/dist/win/contrib/patchWebDAV.bat b/dist/win/contrib/patchWebDAV.bat index 3a90d3de3..c99f8d10a 100644 --- a/dist/win/contrib/patchWebDAV.bat +++ b/dist/win/contrib/patchWebDAV.bat @@ -4,15 +4,18 @@ :: This file must be located in the INSTALLDIR set "LOOPBACK_ALIAS=%1" +set "ACTION=%2" +if "%ACTION%"=="" set "ACTION=install" :: Log for debugging echo LOOPBACK_ALIAS=%LOOPBACK_ALIAS% +echo ACTION=%ACTION% :: Change to INSTALLDIR cd %~dp0 :: Execute the PowerShell script powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File .\patchWebDAV.ps1^ - -LoopbackAlias %LOOPBACK_ALIAS% + -LoopbackAlias %LOOPBACK_ALIAS% -Action %ACTION% :: Return the exit code from PowerShell exit /b %ERRORLEVEL% \ No newline at end of file diff --git a/dist/win/contrib/patchWebDAV.ps1 b/dist/win/contrib/patchWebDAV.ps1 index 9d79ab900..2a9a8de98 100644 --- a/dist/win/contrib/patchWebDAV.ps1 +++ b/dist/win/contrib/patchWebDAV.ps1 @@ -1,15 +1,17 @@ #Requires -RunAsAdministrator Param( - [Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias + [Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias, + [string] $Action = "install" ) +New-Variable -Name "sysdir" -Value ([Environment]::SystemDirectory) -Option Constant -Scope Global +New-Variable -Name "hostsFile" -Value "$sysdir\drivers\etc\hosts" -Option Constant -Scope Global + # Adds an alias for 127.0.0.1 to the hosts file function Add-AliasToHost { param ( [string]$LoopbackAlias ) - $sysdir = [Environment]::SystemDirectory - $hostsFile = "$sysdir\drivers\etc\hosts" $aliasLine = "127.0.0.1 $LoopbackAlias" foreach ($line in Get-Content $hostsFile) { @@ -18,9 +20,26 @@ function Add-AliasToHost { } } - Add-Content -Path $hostsFile -Encoding ascii -Value "`r`n$aliasLine" + $content = Get-Content $hostsFile + $content += "`r`n$aliasLine" + + $content | Set-Content "$hostsFile.tmp" -Encoding ascii + Move-Item "$hostsFile.tmp" $hostsFile -Force } +# Removes an alias for 127.0.0.1 from the hosts file +function Remove-AliasFromHost { + param ( + [string]$LoopbackAlias + ) + $aliasLine = "127.0.0.1 $LoopbackAlias" + + $content = Get-Content $hostsFile + $newContent = $content | Where-Object { $_ -ne $aliasLine } + + $newContent | Set-Content "$hostsFile.tmp" -Encoding ascii + Move-Item "$hostsFile.tmp" $hostsFile -Force +} # Sets in the registry the webclient file size limit to the maximum value function Set-WebDAVFileSizeLimit { @@ -54,14 +73,20 @@ function Edit-ProviderOrder { New-ItemProperty -Path $RegistryPath -Name $Name -Value $UpdatedOrder -PropertyType String -Force | Out-Null } +if ($Action -eq "install") { + Add-AliasToHost $LoopbackAlias + Write-Output 'Ensured alias exists in hosts file' -Add-AliasToHost $LoopbackAlias -Write-Output 'Ensured alias exists in hosts file' + Set-WebDAVFileSizeLimit + Write-Output 'Set WebDAV file size limit' -Set-WebDAVFileSizeLimit -Write-Output 'Set WebDAV file size limit' - -Edit-ProviderOrder -Write-Output 'Ensured correct provider order' + Edit-ProviderOrder + Write-Output 'Ensured correct provider order' +} elseif ($Action -eq "uninstall") { + Remove-AliasFromHost $LoopbackAlias + Write-Output 'Ensured alias removed from hosts file' +} else { + Write-Error "Invalid action: $Action. Only 'install' or 'uninstall' are valid." +} exit 0 diff --git a/dist/win/resources/FAvaultFile.properties b/dist/win/resources/FAvaultFile.properties index 4d0284e69..e35841acf 100644 --- a/dist/win/resources/FAvaultFile.properties +++ b/dist/win/resources/FAvaultFile.properties @@ -1,4 +1,4 @@ mime-type=application/vnd.cryptomator.vault extension=cryptomator description=Cryptomator Vault File -icon=resources/Cryptomator-Vault.ico \ No newline at end of file +icon=C:\\Users\\Arbeit\\Skymatic\\cryptomator-jdk26-jpackage\\dist\\win\\resources\\Cryptomator-Vault.ico diff --git a/dist/win/resources/FAvaultFile.template.properties b/dist/win/resources/FAvaultFile.template.properties new file mode 100644 index 000000000..39640a9ec --- /dev/null +++ b/dist/win/resources/FAvaultFile.template.properties @@ -0,0 +1,4 @@ +mime-type=application/vnd.cryptomator.vault +extension=cryptomator +description=Cryptomator Vault File +icon=${env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT}Cryptomator-Vault.ico \ No newline at end of file diff --git a/dist/win/resources/main.wxs b/dist/win/resources/main.wxs index 5ec5bcbee..f9d6303e2 100644 --- a/dist/win/resources/main.wxs +++ b/dist/win/resources/main.wxs @@ -68,7 +68,7 @@ - + @@ -158,9 +158,13 @@ - + + + + + @@ -214,9 +218,8 @@ - - + @@ -225,7 +228,7 @@ - - + + - + \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100755 index 000000000..bd8896bf2 --- /dev/null +++ b/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 000000000..92450f932 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml index 9a1d5fe00..004b89378 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cryptomator cryptomator - 1.19.2 + 1.19.3 Cryptomator Desktop App @@ -26,7 +26,7 @@ UTF-8 - 25 + 26 @@ -35,43 +35,42 @@ 2.10.0 2.2.2 - 1.8.0 - 1.6.0 + 1.9.0 + 1.6.1 1.5.0 1.7.0 6.0.1 - 3.0.1 + 3.0.2 1.2.12 - 3.2.3 + 3.2.4 3.20.0 2.59.2 2.2 - 2.21.1 - 25.0.2 - 4.5.1 + 2.21.4 + 25.0.3 + 4.5.2 10.5 - 1.5.32 - 2.0.17 + 1.5.35 + 2.0.18 0.8.1 1.9.0 - 6.0.3 - 5.22.0 + 6.1.0 + 5.23.0 3.0 26.1.0 - 12.2.0 - 0.8.14 + 12.2.2 + 0.8.15 2.7.1 - 1.5.1 3.15.0 3.5.0 - 3.10.0 - 3.5.3 + 3.11.0 + 3.5.6 3.5.0 @@ -356,22 +355,12 @@ org.apache.maven.plugins maven-surefire-plugin - - - me.fabriciorby - maven-surefire-junit5-tree-reporter - ${junit-tree-reporter.version} - - plain true @{surefire.jacoco.args} -javaagent:${org.mockito:mockito-core:jar} --enable-native-access=javafx.graphics - - @@ -513,9 +502,6 @@ mac - - idea.version - @@ -527,15 +513,55 @@ - linux + linux-aarch64 unix - Linux + linux + aarch64 + + + + + org.cryptomator + integrations-linux + ${cryptomator.integrations.linux.version} + + + org.openjfx + javafx-base + ${javafx.version} + linux-aarch64 + + + org.openjfx + javafx-graphics + ${javafx.version} + linux-aarch64 + + + org.openjfx + javafx-controls + ${javafx.version} + linux-aarch64 + + + org.openjfx + javafx-fxml + ${javafx.version} + linux-aarch64 + + + + + + linux-x86_64 + + + unix + linux + amd64 - - idea.version - @@ -552,9 +578,6 @@ windows - - idea.version - diff --git a/src/main/java/org/cryptomator/common/vaults/NotAVaultDirectoryException.java b/src/main/java/org/cryptomator/common/vaults/NotAVaultDirectoryException.java new file mode 100644 index 000000000..a460621d9 --- /dev/null +++ b/src/main/java/org/cryptomator/common/vaults/NotAVaultDirectoryException.java @@ -0,0 +1,32 @@ +package org.cryptomator.common.vaults; + +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; + +public class NotAVaultDirectoryException extends NoSuchFileException { + + public enum Reason { + MISSING_DATA_DIR, + DATA_NOT_A_DIRECTORY, + MISSING_VAULT_CONFIG, + VAULT_CONFIG_ACCESS_DENIED, + UNSUPPORTED_STRUCTURE + } + + private final transient Path path; + private final Reason reason; + + public NotAVaultDirectoryException(Path path, Reason reason) { + super(path.toString(), null, "Not a vault directory: " + reason); + this.path = path; + this.reason = reason; + } + + public Path path() { + return path; + } + + public Reason notAVaultReason() { + return reason; + } +} diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index e73075d0d..a5c799433 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -22,20 +22,31 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; +import javafx.application.Platform; import javafx.collections.ObservableList; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.AccessDeniedException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.Collection; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.ResourceBundle; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME; -import static org.cryptomator.common.vaults.VaultState.Value.*; +import static org.cryptomator.common.vaults.VaultState.Value.ALL_MISSING; +import static org.cryptomator.common.vaults.VaultState.Value.ERROR; +import static org.cryptomator.common.vaults.VaultState.Value.LOCKED; +import static org.cryptomator.common.vaults.VaultState.Value.MISSING; +import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION; +import static org.cryptomator.common.vaults.VaultState.Value.PROCESSING; +import static org.cryptomator.common.vaults.VaultState.Value.UNLOCKED; +import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING; +import static org.cryptomator.cryptofs.common.Constants.DATA_DIR_NAME; @Singleton public class VaultListManager { @@ -72,18 +83,57 @@ public class VaultListManager { return vaultList.stream().anyMatch(v -> vaultPath.equals(v.getPath())); } + /** + * Safe to call from any thread: the IO work runs on the calling thread, but the + * {@code ObservableList} mutation is marshaled to the JavaFX application thread. + */ public Vault add(Path pathToVault) throws IOException { Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath(); - if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) { - throw new NoSuchFileException(normalizedPathToVault.toString(), null, "Not a vault directory"); - } + assertIsVaultDirectory(normalizedPathToVault); - return get(normalizedPathToVault) // - .orElseGet(() -> { - Vault newVault = create(newVaultSettings(normalizedPathToVault)); - vaultList.add(newVault); - return newVault; - }); + return get(normalizedPathToVault).orElseGet(() -> { + Vault newVault = create(newVaultSettings(normalizedPathToVault)); + if (Platform.isFxApplicationThread()) { + addVault(newVault); + } else { + Platform.runLater(() -> addVault(newVault)); + } + return newVault; + }); + } + + public static void assertIsVaultDirectory(Path pathToVault) throws IOException { + if (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) { + checkDataDir(pathToVault); + checkConfigFile(pathToVault); + //if vault is legacy _and_ not readable, just say unsupported + throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.UNSUPPORTED_STRUCTURE); + } + } + + static void checkDataDir(Path pathToVault) throws NotAVaultDirectoryException { + Path dataDir = pathToVault.resolve(DATA_DIR_NAME); + if (!Files.exists(dataDir)) { + throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.MISSING_DATA_DIR); + } + if (!Files.isDirectory(dataDir)) { + throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.DATA_NOT_A_DIRECTORY); + } + } + + static void checkConfigFile(Path pathToVault) throws NotAVaultDirectoryException { + Path vaultConfig = pathToVault.resolve(VAULTCONFIG_FILENAME); + + try (var ch = Files.newByteChannel(vaultConfig, StandardOpenOption.READ)) { + ch.read(ByteBuffer.allocate(1)); + } catch (AccessDeniedException e) { + throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.VAULT_CONFIG_ACCESS_DENIED); + } catch (NoSuchFileException e) { + throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.MISSING_VAULT_CONFIG); + } catch (IOException e) { + LOG.warn("Failed to read vault config: {}", e.getMessage()); + throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.UNSUPPORTED_STRUCTURE); + } } private VaultSettings newVaultSettings(Path path) { @@ -153,7 +203,7 @@ public class VaultListManager { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state vaultSettings.lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME); } - case VAULT_CONFIG_MISSING -> { + case VAULT_CONFIG_MISSING -> { //Nothing to do here, since there is no config to read } case MISSING, ALL_MISSING, ERROR, PROCESSING -> { diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java index 7862c3b20..56efa284b 100644 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java +++ b/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java @@ -2,12 +2,14 @@ package org.cryptomator.ui.addvaultwizard; import dagger.Lazy; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.vaults.NotAVaultDirectoryException; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.integrations.uiappearance.Theme; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; +import org.cryptomator.ui.dialogs.Dialogs; import org.cryptomator.ui.fxapp.FxApplicationStyle; import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.slf4j.Logger; @@ -41,6 +43,7 @@ public class ChooseExistingVaultController implements FxController { private final ObjectProperty vault; private final VaultListManager vaultListManager; private final ResourceBundle resourceBundle; + private final Dialogs dialogs; private final ObservableValue screenshot; @Inject @@ -51,6 +54,7 @@ public class ChooseExistingVaultController implements FxController { @AddVaultWizardWindow ObjectProperty vault, // VaultListManager vaultListManager, // ResourceBundle resourceBundle, // + Dialogs dialogs, // FxApplicationStyle applicationStyle) { this.window = window; this.successScene = successScene; @@ -59,6 +63,7 @@ public class ChooseExistingVaultController implements FxController { this.vault = vault; this.vaultListManager = vaultListManager; this.resourceBundle = resourceBundle; + this.dialogs = dialogs; this.screenshot = applicationStyle.appliedAppThemeProperty().map(this::selectScreenshot); } @@ -87,6 +92,9 @@ public class ChooseExistingVaultController implements FxController { Vault newVault = vaultListManager.add(vaultPath.get()); vault.set(newVault); window.setScene(successScene.get()); + } catch (NotAVaultDirectoryException e) { + LOG.warn("Selected folder is not a vault directory: {}", e.getMessage()); + dialogs.prepareNotAVaultDirectoryDialog(window, e).build().showAndWait(); } catch (IOException e) { LOG.error("Failed to open existing vault.", e); appWindows.showErrorWindow(e, window, window.getScene()); diff --git a/src/main/java/org/cryptomator/ui/decryptname/DecryptFileNamesViewController.java b/src/main/java/org/cryptomator/ui/decryptname/DecryptFileNamesViewController.java index 5450e1d48..b7c14199f 100644 --- a/src/main/java/org/cryptomator/ui/decryptname/DecryptFileNamesViewController.java +++ b/src/main/java/org/cryptomator/ui/decryptname/DecryptFileNamesViewController.java @@ -58,8 +58,6 @@ public class DecryptFileNamesViewController implements FxController { private final Stage window; private final Vault vault; private final ResourceBundle resourceBundle; - private final List initialList; - @FXML public TableColumn ciphertextColumn; @FXML @@ -68,12 +66,11 @@ public class DecryptFileNamesViewController implements FxController { public TableView cipherToCleartextTable; @Inject - public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, @DecryptNameWindow List pathsToDecrypt, ResourceBundle resourceBundle) { + public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, ResourceBundle resourceBundle) { this.window = window; this.vault = vault; this.resourceBundle = resourceBundle; this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList()); - this.initialList = pathsToDecrypt; } @FXML @@ -97,8 +94,7 @@ public class DecryptFileNamesViewController implements FxController { }); cipherToCleartextTable.setOnDragDropped(event -> { if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { - checkAndDecrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList()); - cipherToCleartextTable.setItems(mapping); + decrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList()); } }); cipherToCleartextTable.setOnDragExited(_ -> cipherToCleartextTable.setItems(mapping)); @@ -124,9 +120,7 @@ public class DecryptFileNamesViewController implements FxController { }); } }); - if (!initialList.isEmpty()) { - checkAndDecrypt(initialList); - } + window.setOnHidden(_ -> mapping.clear()); } private void copySingleCelltoClipboard() { @@ -149,10 +143,18 @@ public class DecryptFileNamesViewController implements FxController { fileChooser.setInitialDirectory(vault.getPath().toFile()); var ciphertextNodes = fileChooser.showOpenMultipleDialog(window); if (ciphertextNodes != null) { - checkAndDecrypt(ciphertextNodes.stream().map(File::toPath).toList()); + decrypt(ciphertextNodes.stream().map(File::toPath).toList()); } } + public void decrypt(List pathsToDecrypt) { + if (pathsToDecrypt.isEmpty()) { + return; + } + checkAndDecrypt(pathsToDecrypt); + cipherToCleartextTable.setItems(mapping); + } + private void checkAndDecrypt(List pathsToDecrypt) { mapping.clear(); //Assumption: All files are in the same directory diff --git a/src/main/java/org/cryptomator/ui/decryptname/DecryptNameComponent.java b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameComponent.java index 7684d4286..4abf7ee3f 100644 --- a/src/main/java/org/cryptomator/ui/decryptname/DecryptNameComponent.java +++ b/src/main/java/org/cryptomator/ui/decryptname/DecryptNameComponent.java @@ -28,23 +28,28 @@ public interface DecryptNameComponent { @FxmlScene(FxmlFile.DECRYPTNAMES) Lazy decryptNamesView(); + DecryptFileNamesViewController controller(); + @DecryptNameWindow Vault vault(); - default void showDecryptFileNameWindow() { + default void showDecryptFileNameWindow(List pathsToDecrypt) { Stage s = window(); s.setScene(decryptNamesView().get()); s.sizeToScene(); if (vault().isUnlocked()) { + controller().decrypt(pathsToDecrypt); s.show(); + s.requestFocus(); } else { LOG.error("Aborted showing DecryptFileName window: vault state is not {}, but {}.", VaultState.Value.UNLOCKED, vault().getState()); + s.close(); } } @Subcomponent.Factory interface Factory { - DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner, @BindsInstance @DecryptNameWindow List pathsToDecrypt); + DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner); } } diff --git a/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java b/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java index 452acf02a..4933cefc7 100644 --- a/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java +++ b/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java @@ -1,6 +1,7 @@ package org.cryptomator.ui.dialogs; import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.vaults.NotAVaultDirectoryException; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.StageFactory; @@ -139,6 +140,24 @@ public class Dialogs { .setCancelAction(Stage::close); } + public SimpleDialog.Builder prepareNotAVaultDirectoryDialog(Stage window, NotAVaultDirectoryException e) { + String descriptionKey = switch (e.notAVaultReason()) { + case MISSING_DATA_DIR -> "addvaultwizard.existing.notAVault.description.missingDataDir"; + case DATA_NOT_A_DIRECTORY -> "addvaultwizard.existing.notAVault.description.dataNotADirectory"; + case MISSING_VAULT_CONFIG -> "addvaultwizard.existing.notAVault.description.missingVaultConfig"; + case VAULT_CONFIG_ACCESS_DENIED -> "addvaultwizard.existing.notAVault.description.vaultConfigAccessDenied"; + case UNSUPPORTED_STRUCTURE -> "addvaultwizard.existing.notAVault.description.unsupportedStructure"; + }; + return createDialogBuilder() // + .setOwner(window) // + .setTitleKey("addvaultwizard.existing.notAVault.title") // + .setMessageKey("addvaultwizard.existing.notAVault.message") // + .setDescriptionKey(descriptionKey, e.path().getFileName() != null ? e.path().getFileName().toString() : e.path().toString()) // + .setIcon(FontAwesome5Icon.EXCLAMATION) // + .setOkButtonKey(BUTTON_KEY_CLOSE) // + .setOkAction(Stage::close); + } + public SimpleDialog.Builder prepareNoDDirectorySelectedDialog(Stage window) { return createDialogBuilder() // .setOwner(window) // diff --git a/src/main/java/org/cryptomator/ui/fxapp/AppLaunchEventHandler.java b/src/main/java/org/cryptomator/ui/fxapp/AppLaunchEventHandler.java index 9d0702314..ea7d47752 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/AppLaunchEventHandler.java +++ b/src/main/java/org/cryptomator/ui/fxapp/AppLaunchEventHandler.java @@ -1,15 +1,18 @@ package org.cryptomator.ui.fxapp; +import org.cryptomator.common.vaults.NotAVaultDirectoryException; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.launcher.AppLaunchEvent; import org.cryptomator.ui.common.VaultService; +import org.cryptomator.ui.dialogs.Dialogs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; import javafx.application.Platform; +import javafx.stage.Stage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; @@ -29,14 +32,18 @@ class AppLaunchEventHandler { private final FxApplicationWindows appWindows; private final VaultListManager vaultListManager; private final VaultService vaultService; + private final Stage primaryStage; + private final Dialogs dialogs; @Inject - public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService) { + public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService, @PrimaryStage Stage primaryStage, Dialogs dialogs) { this.launchEventQueue = launchEventQueue; this.executorService = executorService; this.appWindows = appWindows; this.vaultListManager = vaultListManager; this.vaultService = vaultService; + this.primaryStage = primaryStage; + this.dialogs = dialogs; } public void startHandlingLaunchEvents() { @@ -58,31 +65,33 @@ class AppLaunchEventHandler { private void handleLaunchEvent(AppLaunchEvent event) { switch (event.type()) { case REVEAL_APP -> appWindows.showMainWindow(); - case OPEN_FILE -> Platform.runLater(() -> { - event.pathsToOpen().forEach(this::openPotentialVault); - }); + case OPEN_FILE -> event.pathsToOpen().forEach(this::openPotentialVault); default -> LOG.warn("Unsupported event type: {}", event.type()); } } // TODO deduplicate MainWindowController... private void openPotentialVault(Path path) { - assert Platform.isFxApplicationThread(); - try { - Path potentialVaultPath = path.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT) ? path.getParent() : path; - final Optional v = vaultListManager.get(potentialVaultPath); - if (v.isPresent()) { - if (v.get().isUnlocked()) { - vaultService.reveal(v.get()); - } else if (v.get().isLocked()) { - appWindows.startUnlockWorkflow(v.get(), null); + Path potentialVaultPath = path.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT) ? path.getParent() : path; + Optional existing = vaultListManager.get(potentialVaultPath.normalize().toAbsolutePath()); + if (existing.isPresent()) { + Platform.runLater(() -> { + if (existing.get().isUnlocked()) { + vaultService.reveal(existing.get()); + } else if (existing.get().isLocked()) { + appWindows.startUnlockWorkflow(existing.get(), null); } - } else { - vaultListManager.add(potentialVaultPath); - LOG.debug("Added vault {}", potentialVaultPath); - } + }); + return; + } + try { + vaultListManager.add(potentialVaultPath); + LOG.debug("Added vault {}", potentialVaultPath); + } catch (NotAVaultDirectoryException e) { + LOG.warn("Cannot add {}: {}", potentialVaultPath, e.getMessage()); + Platform.runLater(() -> dialogs.prepareNotAVaultDirectoryDialog(primaryStage, e).build().showAndWait()); } catch (IOException e) { - LOG.error("Failed to add vault " + path, e); + LOG.error("Failed to add vault {}", potentialVaultPath, e); } } diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java index 6b19429b2..bb5af478c 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java @@ -64,17 +64,6 @@ abstract class FxApplicationModule { return builder.build(); } - @Provides - @FxApplicationScoped - static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) { - return builder.build(); - } - - @Provides - @FxApplicationScoped - static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) { - return builder.build(); - } @Provides @FxApplicationScoped @@ -88,10 +77,4 @@ abstract class FxApplicationModule { return factory.create(); } - @Provides - @FxApplicationScoped - static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) { - return factory.create(); - } - } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java index 94c4fe330..f80b6de0e 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java @@ -39,6 +39,7 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutorService; +import java.util.function.Supplier; @FxApplicationScoped public class FxApplicationWindows { @@ -47,15 +48,15 @@ public class FxApplicationWindows { private final Stage primaryStage; private final Optional trayIntegration; - private final Lazy mainWindow; - private final Lazy preferencesWindow; + private final CachedLazy mainWindow; + private final CachedLazy preferencesWindow; private final QuitComponent.Builder quitWindowBuilder; private final UnlockComponent.Factory unlockWorkflowFactory; private final UpdateReminderComponent.Factory updateReminderWindowFactory; private final LockComponent.Factory lockWorkflowFactory; private final ErrorComponent.Factory errorWindowFactory; - private final Lazy eventViewWindow; - private final Lazy notificationWindow; + private final CachedLazy eventViewWindow; + private final CachedLazy notificationWindow; private final ExecutorService executor; private final VaultOptionsComponent.Factory vaultOptionsWindow; private final ShareVaultComponent.Factory shareVaultWindow; @@ -65,8 +66,8 @@ public class FxApplicationWindows { @Inject public FxApplicationWindows(@PrimaryStage Stage primaryStage, // Optional trayIntegration, // - Lazy mainWindow, // - Lazy preferencesWindow, // + MainWindowComponent.Builder mainWindowBuilder, // + PreferencesComponent.Builder preferencesWindowBuilder, // QuitComponent.Builder quitWindowBuilder, // UnlockComponent.Factory unlockWorkflowFactory, // UpdateReminderComponent.Factory updateReminderWindowFactory, // @@ -74,21 +75,21 @@ public class FxApplicationWindows { ErrorComponent.Factory errorWindowFactory, // VaultOptionsComponent.Factory vaultOptionsWindow, // ShareVaultComponent.Factory shareVaultWindow, // - Lazy eventViewWindow, // - Lazy notificationWindow, + EventViewComponent.Factory eventViewWindowFactory, // + NotificationComponent.Factory notificationWindowFactory, // ExecutorService executor, // Dialogs dialogs) { this.primaryStage = primaryStage; this.trayIntegration = trayIntegration; - this.mainWindow = mainWindow; - this.preferencesWindow = preferencesWindow; + this.mainWindow = new CachedLazy<>(mainWindowBuilder::build); + this.preferencesWindow = new CachedLazy<>(preferencesWindowBuilder::build); this.quitWindowBuilder = quitWindowBuilder; this.unlockWorkflowFactory = unlockWorkflowFactory; this.updateReminderWindowFactory = updateReminderWindowFactory; this.lockWorkflowFactory = lockWorkflowFactory; this.errorWindowFactory = errorWindowFactory; - this.eventViewWindow = eventViewWindow; - this.notificationWindow = notificationWindow; + this.eventViewWindow = new CachedLazy<>(eventViewWindowFactory::create); + this.notificationWindow = new CachedLazy<>(notificationWindowFactory::create); this.executor = executor; this.vaultOptionsWindow = vaultOptionsWindow; this.shareVaultWindow = shareVaultWindow; @@ -218,4 +219,29 @@ public class FxApplicationWindows { LOG.error("Failed to display stage", error); } } + + private static class CachedLazy implements Lazy { + + private final Supplier supplier; + private volatile T instance = null; + + public CachedLazy(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public T get() { + T value = instance; + if (value == null) { + synchronized (this) { + value = instance; + if (value == null) { + value = supplier.get(); + instance = value; + } + } + } + return instance; + } + } } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java index 742e3baaa..485e1de68 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailUnlockedController.java @@ -1,9 +1,8 @@ package org.cryptomator.ui.mainwindow; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.common.base.Preconditions; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; import com.tobiasdiez.easybind.EasyBind; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Nullable; @@ -58,6 +57,7 @@ public class VaultDetailUnlockedController implements FxController { private final DecryptNameComponent.Factory decryptNameWindowFactory; private final ResourceBundle resourceBundle; private final LoadingCache vaultStats; + private final LoadingCache decryptNameWindows; private final VaultStatisticsComponent.Builder vaultStatsBuilder; private final ObservableValue accessibleViaPath; private final ObservableValue accessibleViaUri; @@ -89,7 +89,8 @@ public class VaultDetailUnlockedController implements FxController { this.revealPathService = revealPathService; this.decryptNameWindowFactory = decryptNameWindowFactory; this.resourceBundle = resourceBundle; - this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats)); + this.vaultStats = Caffeine.newBuilder().weakValues().build(this::buildVaultStats); + this.decryptNameWindows = Caffeine.newBuilder().weakValues().build(this::buildDecryptNameWindow); this.vaultStatsBuilder = vaultStatsBuilder; var mp = vault.flatMap(Vault::mountPointProperty); this.accessibleViaPath = mp.map(m -> m instanceof Mountpoint.WithPath).orElse(false); @@ -161,7 +162,7 @@ public class VaultDetailUnlockedController implements FxController { } private void showDecryptNameWindow(List pathsToDecrypt) { - decryptNameWindowFactory.create(vault.get(), mainWindow, pathsToDecrypt).showDecryptFileNameWindow(); + decryptNameWindows.get(vault.get()).showDecryptFileNameWindow(pathsToDecrypt); } private boolean startsWithVaultAccessPoint(Path path) { @@ -198,6 +199,10 @@ public class VaultDetailUnlockedController implements FxController { return vaultStatsBuilder.vault(vault).build(); } + private DecryptNameComponent buildDecryptNameWindow(Vault vault) { + return decryptNameWindowFactory.create(vault, mainWindow); + } + @FXML public void revealAccessLocation() { vaultService.reveal(vault.get()); @@ -217,7 +222,7 @@ public class VaultDetailUnlockedController implements FxController { @FXML public void showVaultStatistics() { - vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow(); + vaultStats.get(vault.get()).showVaultStatisticsWindow(); } /* Getter/Setter */ diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java index f3c3ccb83..2c28d0fb1 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java @@ -4,6 +4,7 @@ import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.recovery.RecoveryActionType; import org.cryptomator.common.recovery.VaultPreparator; import org.cryptomator.common.settings.Settings; +import org.cryptomator.common.vaults.NotAVaultDirectoryException; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultComponent; import org.cryptomator.common.vaults.VaultListManager; @@ -23,6 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.BooleanProperty; @@ -55,6 +57,7 @@ import java.util.List; import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; +import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT; @@ -90,6 +93,7 @@ public class VaultListController implements FxController { private final VaultComponent.Factory vaultComponentFactory; private final RecoveryKeyComponent.Factory recoveryKeyWindow; private final List mountServices; + private final ExecutorService executor; public ListView vaultList; public StackPane root; @@ -113,7 +117,8 @@ public class VaultListController implements FxController { RecoveryKeyComponent.Factory recoveryKeyWindow, // VaultComponent.Factory vaultComponentFactory, // List mountServices, // - FxFSEventList fxFSEventList) { + FxFSEventList fxFSEventList, // + ExecutorService executor) { this.mainWindow = mainWindow; this.vaults = vaults; this.selectedVault = selectedVault; @@ -127,6 +132,7 @@ public class VaultListController implements FxController { this.recoveryKeyWindow = recoveryKeyWindow; this.vaultComponentFactory = vaultComponentFactory; this.mountServices = mountServices; + this.executor = executor; this.emptyVaultList = Bindings.isEmpty(vaults); this.unreadEvents = fxFSEventList.unreadEventsProperty(); @@ -324,15 +330,18 @@ public class VaultListController implements FxController { } private void addVault(Path pathToVault) { - try { - if (pathToVault.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT)) { - vaultListManager.add(pathToVault.getParent()); - } else { - vaultListManager.add(pathToVault); + Path target = pathToVault.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT) ? pathToVault.getParent() : pathToVault; + executor.execute(() -> { + try { + vaultListManager.add(target); + } catch (NotAVaultDirectoryException e) { + LOG.warn("Cannot add {}: {}", target, e.getMessage()); + Platform.runLater(() -> dialogs.prepareNotAVaultDirectoryDialog(mainWindow, e).build().showAndWait()); + } catch (IOException e) { + LOG.warn("Failed to add vault {}", target, e); + Platform.runLater(() -> appWindows.showErrorWindow(e, mainWindow, null)); } - } catch (IOException e) { - LOG.debug("Not a vault: {}", pathToVault); - } + }); } @FXML diff --git a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java index c21630bde..ba7e032f8 100644 --- a/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/UpdatesPreferencesController.java @@ -260,7 +260,7 @@ public class UpdatesPreferencesController implements FxController { public boolean isProhibitUpdateWhileUnlocked() { // If the result of the last update check was from the fallback mechanism, we don't need to show the warning - return !unlockedVaults.isEmpty() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate()); + return !unlockedVaults.isEmpty() && updateChecker.isUpdateAvailable() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate()); } public BooleanBinding prohibitUpdateWhileUnlockedProperty() { diff --git a/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java b/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java index b0c490a3f..d45dc46cd 100644 --- a/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java +++ b/src/main/java/org/cryptomator/ui/sharevault/ShareVaultController.java @@ -18,7 +18,10 @@ import java.net.URISyntaxException; public class ShareVaultController implements FxController { private static final String SCHEME_PREFIX = "hub+"; - private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/"; + private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/" // + + "?utm_source=cryptomator-desktop" // + + "&utm_medium=app" // + + "&utm_campaign=share-vault"; private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/security/best-practices/#sharing-of-vaults"; private final Stage window; diff --git a/src/main/resources/fxml/preferences_about.fxml b/src/main/resources/fxml/preferences_about.fxml index b3142e301..96714e08d 100644 --- a/src/main/resources/fxml/preferences_about.fxml +++ b/src/main/resources/fxml/preferences_about.fxml @@ -22,7 +22,7 @@ - diff --git a/src/main/resources/fxml/vault_options_mount.fxml b/src/main/resources/fxml/vault_options_mount.fxml index fd65536cc..b451e0cd7 100644 --- a/src/main/resources/fxml/vault_options_mount.fxml +++ b/src/main/resources/fxml/vault_options_mount.fxml @@ -9,90 +9,94 @@ + - - - - - - - - - - - - - - +