Compare commits

..
Author SHA1 Message Date
Sebastian Stenzel 8dae4c55ea check license's x5c claim 2026-03-06 13:28:44 +01:00
150 changed files with 1377 additions and 4450 deletions
+1 -1
View File
@@ -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@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0
uses: azure/artifact-signing-action@87c2e83e6868da99d3380aa309851b32ed9a8346 # v1.1.0
with:
files-folder: ${{ inputs.base-dir }}
files-folder-filter: ${{ inputs.file-extensions }}
+36 -4
View File
@@ -3,19 +3,51 @@ updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "monthly"
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "Etc/UTC"
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:*"
- dependency-name: "com.fasterxml.jackson.*"
versions: [ "[2.22,)" ] # 2.21.x is LTS, see https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.21
update-types: ["version-update:semver-major"]
# due to https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter/issues/68
- dependency-name: "org.apache.maven.plugins:maven-surefire-plugin"
versions: [ "3.5.4", "3.5.5" ]
groups:
maven-dependencies:
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:
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`
-34
View File
@@ -1,34 +0,0 @@
<!-- HEADER -->
> [!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.
<!-- /HEADER -->
<!--REPLACE with auto-generated release notes (see below)
### What's New 🎉
### Bugfixes 🐛
### Other Changes 📎
END REPLACE-->
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).
<!-- Auto-Generated Release Notes: -->
-189
View File
@@ -1,189 +0,0 @@
# 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-<ver>.tar.gz.asc` | Source (GPG signature) |
| `Cryptomator-<ver>-x64.msi` + `.asc` | Windows |
| `Cryptomator-<ver>-x64.exe` + `.asc` | Windows |
| `Cryptomator-<ver>-arm64.dmg` + `.asc` | macOS (Apple Silicon) |
| `Cryptomator-<ver>-x64.dmg` + `.asc` | macOS (Intel) |
| `cryptomator-<ver>-x86_64.AppImage` + `.zsync` + `.asc` | Linux (x86_64) |
| `cryptomator-<ver>-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.
+110 -81
View File
@@ -1,44 +1,17 @@
name: Build AppImage
on:
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}}
release:
types: [published]
workflow_dispatch:
inputs:
semVerNum:
description: 'The Major.Minor.Patch part of the version'
version:
description: 'Version'
required: false
revisionNum:
description: 'The revision number'
required: false
semVerSuffix:
description: 'The suffix of the version, including dash'
required: false
default: '-SNAPSHOT'
create-pr:
description: 'Create a PR for aur-bin repo'
type: boolean
default: false
push:
branches-ignore:
- 'dependabot/**'
@@ -50,34 +23,38 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '26.0.1+8'
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
JAVA_VERSION: '25.0.2+10.0.LTS'
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.3/openjfx-25.0.3_linux-x64_bin-jmods.zip'
openjfx-sha: '47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50'
appimagetool-sha: 'ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0'
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
openjfx-sha: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
- os: ubuntu-24.04-arm
arch: aarch64
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'
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -95,7 +72,7 @@ jobs:
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
JMOD_VERSION=${JMOD_VERSION#*@}
JMOD_VERSION=${JMOD_VERSION%%.*}
POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
@@ -104,9 +81,9 @@ jobs:
exit 1
fi
- name: Set version
run : ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
run : mvn versions:set -DnewVersion="$SEMVER_STR"
- name: Run maven
run: ./mvnw -B clean package -DskipTests
run: mvn -B clean package -Plinux -DskipTests
- name: Patch target dir
run: |
cp LICENSE.txt target
@@ -146,13 +123,13 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}.${REVISION_NUM}"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${SEMVER_NUM}.${REV_NUM}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\""
@@ -163,9 +140,8 @@ 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-${REVISION_NUM}\""
--java-options "-Dcryptomator.buildNumber=\"appimage-${REV_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"
--resource-dir dist/linux/resources
- name: Patch Cryptomator.AppDir
@@ -189,8 +165,7 @@ jobs:
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
- name: Download AppImageKit
run: |
curl --silent --fail-with-body --proto "=https" -L "https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${{ matrix.arch }}.AppImage" -o appimagetool.AppImage
echo "${{ matrix.appimagetool-sha }} appimagetool.AppImage" | shasum -a256 --check
curl --silent --fail-with-body --proto "=https" -L "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${{ matrix.arch }}.AppImage" -o appimagetool.AppImage
chmod +x appimagetool.AppImage
./appimagetool.AppImage --appimage-extract
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
@@ -202,7 +177,7 @@ jobs:
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Build AppImage
run: >
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.AppImage
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${SEMVER_STR}-${{ matrix.arch }}.AppImage
-u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.arch }}.AppImage.zsync"
--sign --sign-key=615D449FE6E6A235
- name: Create detached GPG signatures
@@ -210,7 +185,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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: appimage-${{ matrix.arch }}
path: |
@@ -219,10 +194,9 @@ jobs:
cryptomator-*.asc
if-no-files-found: error
- name: Publish AppImage on GitHub Releases
if: inputs.upload-to-draft
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
draft: true
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
@@ -230,24 +204,79 @@ jobs:
cryptomator-*.zsync
cryptomator-*.asc
collect-sha256sums:
name: Collect AppImage checksums
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'
runs-on: ubuntu-latest
needs: [build]
if: inputs.upload-to-draft
outputs:
x64-sha256sum: ${{ steps.sha256sum.outputs.x64-sha256sum }}
aarch64-sha256sum: ${{ steps.sha256sum.outputs.aarch64-sha256sum }}
needs: [build, get-version]
container:
image: archlinux:base-devel
env:
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
PKGDEST: ${{ github.workspace }}/pkgdest
SRCDEST: ${{ github.workspace }}/srcdest
steps:
- name: Download AppImage artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: appimage-*
path: appimage-artifacts
- name: Compute SHA256 sums
id: sha256sum
- name: Prepare pacman
run: |
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"
pacman-key --init
pacman-key --populate archlinux
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip github-cli curl pacman-contrib
- name: Checkout cryptomator/aur-bin
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: 'cryptomator/aur-bin'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: 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: 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
-115
View File
@@ -1,115 +0,0 @@
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
+7 -7
View File
@@ -37,7 +37,7 @@ on:
jobs:
download-file:
name: Downloads the file into the VM
runs-on: ubuntu-slim
runs-on: ubuntu-latest
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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
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-slim
runs-on: ubuntu-latest
needs: download-file
if: inputs.kaspersky
steps:
- name: Download artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: ${{ needs.download-file.outputs.fileName }}
path: upload
- name: Upload to Kaspersky
uses: SamKirkland/FTP-Deploy-Action@110f9186c050f71550953127052e77650219c287 # v4.6.3
uses: SamKirkland/FTP-Deploy-Action@a51268f67f6605236975928ae28b0f7e9971d50a # 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-slim
runs-on: ubuntu-latest
needs: download-file
if: inputs.avast
steps:
- name: Download artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: ${{ needs.download-file.outputs.fileName }}
path: upload
+42 -5
View File
@@ -11,7 +11,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 26
JAVA_VERSION: 25
defaults:
run:
@@ -22,14 +22,14 @@ jobs:
name: Compile and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Cache SonarCloud packages
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
@@ -37,7 +37,7 @@ jobs:
- name: Build and Test
run: >
xvfb-run
./mvnw -B verify
mvn -B verify
jacoco:report
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
-Pcoverage
@@ -47,3 +47,40 @@ 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.
<!--REPLACE with auto-generated release notes (see below)
### What's New 🎉
### Bugfixes 🐛
### Other Changes 📎
END REPLACE-->
For a comprehensive view of changes, read the [CHANGELOG](https://github.com/cryptomator/cryptomator/blob/develop/CHANGELOG.md).
---
<!-- Don't forget to include the
💾 SHA-256 checksums of release artifacts:
```
```
-->
> [!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).
<!-- Auto-Generated Release Notes: -->
+4 -4
View File
@@ -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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.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@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: ''
SLACK_ICON: false
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: ''
SLACK_FOOTER: false
MSG_MINIMAL: true
+11 -14
View File
@@ -1,8 +1,6 @@
name: Build Debian Package
on:
schedule:
- cron: '0 22 20 * *'
workflow_dispatch:
inputs:
semver:
@@ -25,12 +23,12 @@ on:
env:
JAVA_DIST: 'temurin'
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'
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'
jobs:
get-version:
@@ -45,7 +43,7 @@ jobs:
env:
INPUT_PPAVER: ${{ inputs.ppaver }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: deb-version
name: Determine deb-version
run: |
@@ -56,21 +54,20 @@ 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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
check-latest: true
cache: 'maven'
- name: Run maven
run: ./mvnw -B clean package -DskipTests
run: mvn -B clean package -Plinux -DskipTests
- name: Download OpenJFX jmods
id: download-jmods
run: |
@@ -90,7 +87,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=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
@@ -146,7 +143,7 @@ jobs:
run: |
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: linux-deb-package
path: |
+2 -2
View File
@@ -7,11 +7,11 @@ on:
jobs:
check-dependencies:
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@8356563bf7b8d1c8d693f75ca487e8f57573cec9 # v3.1.0
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # v3.0.3
with:
runner-os: 'ubuntu-latest'
java-distribution: 'temurin'
java-version: 26
java-version: 25
secrets:
nvd-api-key: ${{ secrets.NVD_API_KEY }}
ossindex-username: ${{ secrets.OSSINDEX_USERNAME }}
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Get download count of latest releases
id: get-stats
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const query = `query($owner:String!, $name:String!) {
-157
View File
@@ -1,157 +0,0 @@
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 '/<!-- HEADER -->/,/<!-- \/HEADER -->/c\
<!-- HEADER -->\
> [!NOTE]\
> Release artifacts finished building successfully.\
>\
> SHA-256 checksums have been updated below.\
<!-- /HEADER -->')
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 }}
+1 -1
View File
@@ -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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
+85
View File
@@ -0,0 +1,85 @@
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
+10 -19
View File
@@ -14,9 +14,6 @@ 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}}
@@ -26,7 +23,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 26
JAVA_VERSION: 25
jobs:
determine-version:
@@ -35,15 +32,14 @@ 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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Setup Java
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -56,32 +52,27 @@ jobs:
elif [[ "${VERSION_STRING}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
SEM_VER_STR="${VERSION_STRING}"
else
SEM_VER_STR=`./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout`
SEM_VER_STR=`mvn 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_SUFFIX="${SEM_VER_STR#"$SEM_VER_NUM"}"
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
REVCOUNT=`git rev-list --count HEAD`
TYPE="unknown"
if [[ -z $SEM_VER_SUFFIX ]]; then
if [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+$ ]]; then
TYPE="stable"
elif [[ $SEM_VER_SUFFIX =~ -alpha[1-9]+$ ]]; then
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-alpha[1-9]+$ ]]; then
TYPE="alpha"
elif [[ $SEM_VER_SUFFIX =~ -beta[1-9]+$ ]]; then
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-beta[1-9]+$ ]]; then
TYPE="beta"
elif [[ $SEM_VER_SUFFIX =~ -rc[1-9]+$ ]]; then
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-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@7c80b6b03a18b42884761daa9862ff5683ec8c8a # v4.0.0
uses: skymatic/semver-validation-action@7a6ae1c9e121540d11c9c7e4e667c83d583aa153 # v3.0.0
with:
version: ${{ steps.versions.outputs.semVerStr }}
-264
View File
@@ -1,264 +0,0 @@
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 <<EOF
- type: file
dest: .m2/repository/${dependencyName}
url: https://repo.maven.apache.org/maven2/${dependencyPath}
sha256: ${dependencySha}
EOF
done < maven-dependency-files-common.txt
) > 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 <<EOF
- type: file
dest: .m2/repository/${dependencyName}
url: https://repo.maven.apache.org/maven2/${dependencyPath}
sha256: ${dependencySha}
only-arches: [${{ matrix.variant.arch }}]
EOF
done < maven-dependency-files-javafx.txt
) > 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
+11 -12
View File
@@ -3,8 +3,6 @@ name: Build Arch package
on:
release:
types: [published]
schedule:
- cron: '0 21 20 * *'
workflow_dispatch:
inputs:
version:
@@ -44,7 +42,7 @@ jobs:
pacman-key --init
pacman-key --populate archlinux
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: cryptomator
- name: Create build user
@@ -69,13 +67,13 @@ jobs:
sudo -u builder
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
makepkg --syncdeps --cleanbuild --noconfirm --log
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: arch-package
if-no-files-found: error
path: |
${{ env.PKGDEST }}/*.pkg.tar.zst
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: pkgbuild-file
if-no-files-found: error
@@ -108,7 +106,7 @@ jobs:
env:
TAG: ${{ needs.get-version.outputs.semVerStr || github.event.release.tag_name }}
- name: Checkout cryptomator/aur repo
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: 'cryptomator/aur'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
@@ -132,6 +130,7 @@ 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)"
@@ -142,11 +141,11 @@ jobs:
fi
echo "value=${NEXT_REL}" >> "$GITHUB_OUTPUT"
echo "dist-version=${TARGET_VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT"
echo "dist-version=${VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT"
env:
TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }}
VERSION: ${{ needs.get-version.outputs.semVerStr }}
- name: Download PKGBUILD template
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: pkgbuild-file
- name: Prepare PKGBUILD
@@ -188,14 +187,14 @@ jobs:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification
if: github.event_name == 'release'
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: ''
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "AUR release PR created for ${{ github.event.repository.name }} ${{ steps.pkgrel.outputs.dist-version }} ."
SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.url }}|PR> on how to proceed."
SLACK_FOOTER: ''
SLACK_FOOTER: false
MSG_MINIMAL: true
+36 -70
View File
@@ -9,45 +9,13 @@ name: Build macOS .dmg for x64
#######################################
on:
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}}
release:
types: [published]
workflow_dispatch:
inputs:
semVerNum:
description: 'The Major.Minor.Patch part of the version'
version:
description: '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
@@ -56,18 +24,18 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '26.0.1+8'
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
JAVA_VERSION: '25.0.2+10.0.LTS'
jobs:
build:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.version }}
build-arm:
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
runs-on: ${{ matrix.os }}
outputs:
sha256sum: ${{ steps.sha256sum.outputs.value }}
needs: [get-version]
strategy:
fail-fast: false
matrix:
@@ -76,12 +44,12 @@ jobs:
architecture: x64
output-suffix: x64
fuse-lib: macFUSE
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_osx-x64_bin-jmods.zip'
openjfx-sha: '3512fabe43aee467538d329cfbbaab3c53dff2a810f0d54e381f461d5e0fac43'
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_osx-x64_bin-jmods.zip'
openjfx-sha: '0b4d8463f03901b7425d94628e4116b7078abb8dd540fbec415266fac20bda5c'
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -100,7 +68,7 @@ jobs:
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
JMOD_VERSION=${JMOD_VERSION#*@}
JMOD_VERSION=${JMOD_VERSION%%.*}
POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
@@ -109,9 +77,9 @@ jobs:
exit 1
fi
- name: Set version
run : ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: ./mvnw -B clean package -Pmac -DskipTests
run: mvn -B clean package -Pmac -DskipTests
- name: Patch target dir
run: |
cp LICENSE.txt target
@@ -149,8 +117,8 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
--java-options "-Xss5m"
@@ -159,7 +127,7 @@ jobs:
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--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\""
@@ -169,8 +137,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-${REVISION_NUM}\""
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources
- name: Patch Cryptomator.app
@@ -178,14 +145,16 @@ 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_NUM}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
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: >
./mvnw -B license:add-third-party
mvn -B license:add-third-party
-Dlicense.thirdPartyFilename=license.rtf
-Dlicense.outputDirectory=dist/mac/dmg/resources
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
@@ -270,14 +239,16 @@ jobs:
--eula "dist/mac/dmg/resources/license.rtf"
--icon ".background" 128 758
--icon ".VolumeIcon.icns" 512 758
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
env:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
- name: Codesign .dmg
run: |
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
env:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg
if: inputs.notarize || github.event_name == 'schedule'
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
with:
app-path: 'Cryptomator-*.dmg'
@@ -285,12 +256,8 @@ 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-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg"
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -303,7 +270,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: dmg-${{ matrix.output-suffix }}
path: |
@@ -311,10 +278,9 @@ jobs:
Cryptomator-*.asc
if-no-files-found: error
- name: Publish dmg on GitHub Releases
if: inputs.upload-to-draft
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
draft: true
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
+35 -69
View File
@@ -1,45 +1,13 @@
name: Build macOS .dmg for arm64
on:
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}}
release:
types: [published]
workflow_dispatch:
inputs:
semVerNum:
description: 'The Major.Minor.Patch part of the version'
version:
description: '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
@@ -54,18 +22,18 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '26.0.1+8'
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
JAVA_VERSION: '25.0.2+10.0.LTS'
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 }}
outputs:
sha256sum: ${{ steps.sha256sum.outputs.value }}
needs: [get-version]
strategy:
fail-fast: false
matrix:
@@ -74,12 +42,12 @@ jobs:
architecture: aarch64
output-suffix: arm64
fuse-lib: FUSE-T
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_osx-aarch64_bin-jmods.zip'
openjfx-sha: 'a52014d625b8b04e57fd71650f881c1397542b4018e1b04f1b4e66c8800a1f34'
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_osx-aarch64_bin-jmods.zip'
openjfx-sha: '4cd258001c75af7047005c5c891e2400ed11d24fbb09412324c0cbaf8b503c5a'
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -98,7 +66,7 @@ jobs:
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
JMOD_VERSION=${JMOD_VERSION#*@}
JMOD_VERSION=${JMOD_VERSION%%.*}
POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
@@ -107,9 +75,9 @@ jobs:
exit 1
fi
- name: Set version
run : ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: ./mvnw -B clean package -Pmac -DskipTests
run: mvn -B clean package -Pmac -DskipTests
- name: Patch target dir
run: |
cp LICENSE.txt target
@@ -147,8 +115,8 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
--java-options "-Xss5m"
@@ -157,7 +125,7 @@ jobs:
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--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\""
@@ -167,9 +135,8 @@ 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-${REVISION_NUM}\""
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources
- name: Patch Cryptomator.app
@@ -177,14 +144,16 @@ 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_NUM}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
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: >
./mvnw -B license:add-third-party
mvn -B license:add-third-party
-Dlicense.thirdPartyFilename=license.rtf
-Dlicense.outputDirectory=dist/mac/dmg/resources
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
@@ -269,14 +238,16 @@ jobs:
--eula "dist/mac/dmg/resources/license.rtf"
--icon ".background" 128 758
--icon ".VolumeIcon.icns" 512 758
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
env:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
- name: Codesign .dmg
run: |
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
env:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg
if: inputs.notarize || github.event_name == 'schedule'
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
with:
app-path: 'Cryptomator-*.dmg'
@@ -284,12 +255,8 @@ 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-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg"
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -302,7 +269,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: dmg-${{ matrix.output-suffix }}
path: |
@@ -310,10 +277,9 @@ jobs:
Cryptomator-*.asc
if-no-files-found: error
- name: Publish dmg on GitHub Releases
if: inputs.upload-to-draft
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
draft: true
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
+2 -2
View File
@@ -7,12 +7,12 @@ on:
jobs:
no-response:
runs-on: ubuntu-slim
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
days-before-stale: 14
days-before-close: 0
+24 -130
View File
@@ -5,141 +5,35 @@ on:
types: [published]
jobs:
notify:
runs-on: ubuntu-slim
get-version:
runs-on: ubuntu-latest
steps:
- name: Notify about DEB build
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
- 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
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: ''
SLACK_ICON: false
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 <https://github.com/${{ github.repository }}/actions/workflows/debian.yml|build deb Package>."
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 <https://static.cryptomator.org/desktop/latest-version.json|latest-version.json>.'
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-slim
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 <https://github.com/${{ github.repository }}/actions/workflows/winget.yml|release to winget>."
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) }} }'
SLACK_FOOTER: false
MSG_MINIMAL: true
+4 -4
View File
@@ -5,7 +5,7 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 26
JAVA_VERSION: 25
defaults:
run:
@@ -16,11 +16,11 @@ jobs:
name: Compile and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Build and Test
run: xvfb-run ./mvnw -B clean install jacoco:report -Pcoverage
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage
+6 -6
View File
@@ -12,16 +12,16 @@ defaults:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: 26
JAVA_VERSION: 25
jobs:
check-preconditions:
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
@@ -36,7 +36,7 @@ jobs:
exit 1
fi
if [[ ${SEM_VER_STR} == `./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout` ]]; then
if [[ ${SEM_VER_STR} == `mvn help:evaluate -Dexpression=project.version -q -DforceStdout` ]]; then
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
else
echo "Version not set in POM"
@@ -50,7 +50,7 @@ jobs:
exit 1
fi
- name: Cache NVD DB
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: ~/.m2/repository/org/owasp/dependency-check-data/
key: dependency-check-${{ github.run_id }}
@@ -61,6 +61,6 @@ jobs:
- name: Run org.owasp:dependency-check plugin
id: dependency-check
continue-on-error: true
run: ./mvnw -B verify -Pdependency-check -DskipTests
run: mvn -B verify -Pdependency-check -DskipTests
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
+2 -2
View File
@@ -7,12 +7,12 @@ on:
jobs:
stale:
runs-on: ubuntu-slim
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
days-before-stale: 365
days-before-close: 90
+129 -131
View File
@@ -1,48 +1,13 @@
name: Build Windows Installer
on:
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}}
release:
types: [published]
workflow_dispatch:
inputs:
semVerNum:
description: 'The Major.Minor.Patch part of the version'
version:
description: '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
@@ -57,11 +22,8 @@ on:
env:
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'
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'
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'
@@ -72,23 +34,27 @@ 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 }}
outputs:
sha256sum: ${{ steps.sha256sum.outputs.value }}
needs: [ get-version ]
strategy:
matrix:
include:
- arch: x64
os: windows-latest
java-dist: 'temurin'
java-version: '26.0.1+8'
java-dist: 'zulu' #cannot use temurin, see https://github.com/cryptomator/cryptomator/issues/3824#issuecomment-2829827427
java-version: '25.0.1+8'
java-package: 'jdk'
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ matrix.java-dist }}
java-version: ${{ matrix.java-version }}
@@ -119,7 +85,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=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
@@ -128,9 +94,9 @@ jobs:
exit 1
fi
- name: Set version
run: ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
run: mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
- name: Run maven
run: ./mvnw -B clean package -Pwin -DskipTests
run: mvn -B clean package -Pwin -DskipTests
- name: Patch target dir
run: |
cp LICENSE.txt target
@@ -168,13 +134,13 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}.${REVISION_NUM}"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--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=\"${VERSION_NUM}${VERSION_SUFFIX}\""
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
@@ -185,13 +151,12 @@ jobs:
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
--java-options "-Dcryptomator.loopbackAlias=\"cryptomator-vault\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"msi-${REVISION_NUM}\""
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
--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\""
--java-options "-Dcryptomator.disableUpdateCheck=false"
--java-options "-XX:ErrorFile=C:/cryptomator/cryptomator_crash.log"
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico
--add-launcher "Cryptomator (Debug)=dist/win/debug-launcher.properties"
@@ -220,32 +185,33 @@ jobs:
}
$jar.Dispose()
}
- name: Get msi helper dll for code signing
- name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
shell: pwsh
run: |
${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 \;
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"
- name: Sign DLLs with Azure Trusted Signing
if: inputs.sign || github.event_name == 'schedule'
if: inputs.sign || github.event_name == 'release'
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 Scripts with Azure Trusted Signing
if: inputs.sign || github.event_name == 'schedule'
uses: ./.github/actions/win-sign-action
- 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
with:
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 }}
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 }}
- name: Replace DLLs inside jars with signed ones
shell: pwsh
run: |
@@ -262,7 +228,7 @@ jobs:
}
- name: Generate license for MSI
run: >
./mvnw -B license:add-third-party
mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.outputDirectory=dist/win/resources"
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
@@ -271,16 +237,6 @@ 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
@@ -291,21 +247,21 @@ jobs:
--dest installer
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}.${REVISION_NUM}"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
--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 == 'schedule'
if: inputs.sign || github.event_name == 'release'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\installer
@@ -314,12 +270,8 @@ 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-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.msi"
run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.arch }}.msi
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -328,7 +280,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: msi-${{ matrix.arch }}
path: |
@@ -339,20 +291,18 @@ jobs:
build-exe:
name: Build .exe installer
runs-on: ${{ matrix.os }}
needs: [ build-msi ]
outputs:
sha256sum: ${{ steps.sha256sum.outputs.value }}
needs: [ get-version, build-msi ]
strategy:
matrix:
include:
- arch: x64
os: windows-latest
executable-suffix: x64
java-dist: 'temurin'
java-version: '26.0.1+8'
java-dist: 'zulu'
java-version: '24.0.1+9'
java-package: 'jdk'
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install wix and extensions
run: |
dotnet tool install --global wix --version ${WIX_VERSION}
@@ -361,14 +311,14 @@ jobs:
env:
WIX_VERSION: ${{ env.WIX_VERSION }}
- name: Download .msi
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
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@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ matrix.java-dist }}
java-version: ${{ matrix.java-version }}
@@ -377,7 +327,7 @@ jobs:
cache: 'maven'
- name: Generate license for exe
run: >
./mvnw -B license:add-third-party
mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/bundle/resources"
@@ -388,10 +338,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'
@@ -405,22 +355,21 @@ jobs:
run: >
wix build
-define BundleName="Cryptomator"
-define BundleVersion="${VERSION_NUM}.${REVISION_NUM}"
-define BundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
-define BundleVendor="Skymatic GmbH"
-define BundleCopyright="(C) 2016 - 2026 Skymatic GmbH"
-define BundleCopyright="(C) 2016 - 2025 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/Cryptomator-Installer.exe"
-out "../../installer/unsigned/Cryptomator-Installer.exe"
- name: Detach burn engine in preparation to sign
if: inputs.sign || github.event_name == 'schedule'
run: >
wix burn detach installer/Cryptomator-Installer.exe -engine tmp/engine.exe
wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe
- name: Sign WiX burn engine with Azure Trusted Signing
if: inputs.sign || github.event_name == 'schedule'
if: inputs.sign || github.event_name == 'release'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\tmp
@@ -430,14 +379,21 @@ 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
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
run: >
wix burn reattach installer/unsigned/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 == 'schedule'
if: inputs.sign || github.event_name == 'release'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\installer
@@ -447,12 +403,18 @@ 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-*.exe)
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
- 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 }}
- name: Add possible alpha/beta tags to installer name
run: mv installer/Cryptomator-Installer.exe "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.executable-suffix }}.exe"
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.executable-suffix }}.exe
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -461,7 +423,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: exe-${{ matrix.executable-suffix }}
path: |
@@ -471,22 +433,58 @@ jobs:
publish:
name: Publish installers to the github release
if: inputs.upload-to-draft
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
merge-multiple: true
- name: Publish installers on GitHub Releases
id: publish
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
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 <https://github.com/${{ github.repository }}/actions/workflows/winget.yml| release them to winget>."
SLACK_FOOTER: false
MSG_MINIMAL: true
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Submit package
uses: vedantmgoyal2009/winget-releaser@7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 # no_specific_version
uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # no_specific_version
with:
identifier: Cryptomator.Cryptomator
version: ${{ inputs.tag }}
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{userhome}/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/.config/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator-Dev/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator-Dev/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;@{localappdata}/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{localappdata}/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{appdata}/Cryptomator-Dev/Plugins&quot; -Dcryptomator.integrationsWin.keychainPaths=&quot;@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.integrationsWin.windowsHelloKeychainPaths=&quot;@{appdata}/Cryptomator-Dev/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/windowsHelloKeychain.json&quot; -Dcryptomator.p12Path=&quot;@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.p12Path=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12&quot; -Dcryptomator.ipcSocketPath=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;@{userhome}/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.mountPointsDir=&quot;@{userhome}/Library/Application Support/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>
-4
View File
@@ -1,4 +0,0 @@
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
+2 -63
View File
@@ -7,68 +7,7 @@ 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/compare/1.19.2...HEAD)
### 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))
* 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
* Cryptomamtor Hub Vaults: Additional patch for (#4179, [GHSA-34rf-rwr3-7g43](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-34rf-rwr3-7g43))
## [1.19.1](https://github.com/cryptomator/cryptomator/releases/1.19.1) - 2026-03-12
### Security
* Cryptomamtor Hub Vaults: Fixed possible man-in-the-middle attack with tampered vault config (#4179, [GHSA-34rf-rwr3-7g43](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-34rf-rwr3-7g43))
* Disallow unencrypted http connections to hub by default ([CVE-2026-32309](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-vv33-h7qx-c264))
* Disallow loading of masterkey file from arbitrary paths (#4180, [CVE-2026-32310](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-5phc-5pfx-hr52))
* Fixed not-configured plugin directory does not disable plugin search ([#4176](https://github.com/cryptomator/cryptomator/pull/4176))
### Added
* Trust on first use, adding new config properties `cryptomator.hub.allowedHosts` and `cryptomator.hub.enableTrustOnFirstUse` (#4179)
### Fixed
* Fixed Finder window opens twice when revealing vault on macOS ([#4177](https://github.com/cryptomator/cryptomator/pull/4177))
* Fixed app does not start due to secret service detection failure on Linux ([#4175](https://github.com/cryptomator/cryptomator/pull/4175))
### Changed
* Pin version of appimagetool([#4181](https://github.com/cryptomator/cryptomator/pull/4181))
* Updated translations
* Updated dependencies:
* `org.cryptomator:integrations-api` from 1.8.0-beta1 to 1.8.0
* `org.cryptomator:integrations-linux` from 1.7.0-beta4 to 1.7.0
* `org.cryptomator:integrations-mac` from 1.5.0-beta3 to 1.5.0
## [1.19.0](https://github.com/cryptomator/cryptomator/releases/tag/1.19.0) - 2026-03-09
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.18.0...HEAD)
### Added
* Self-Update Mechanism ([#3948](https://github.com/cryptomator/cryptomator/pull/3948))
@@ -92,7 +31,7 @@ Changes to prior versions can be found on the [Github release page](https://gith
* Disable user defined app start config on Windows ([#4132](https://github.com/cryptomator/cryptomator/issues/4132))
* Disable plugin loading by default ([#4136](https://github.com/cryptomator/cryptomator/4136))
* Use JDK 25 ([#4031](https://github.com/cryptomator/cryptomator/pull/4031))
* Update JavaFX to 25.0.2 ([#4145](https://github.com/cryptomator/cryptomator/pull/4145))
* Update JavaFX to 25.0.2 ([#4145](https://github.com/cryptomator/cryptomator/pull/4145)))
* Updated translations
* Updated dependencies
* `ch.qos.logback:*` from 1.5.19 to 1.5.32
+5 -1
View File
@@ -79,11 +79,15 @@ For more information on the security details visit [cryptomator.org](https://doc
### Dependencies
* JDK 25 (e.g. temurin, zulu)
* Maven 3
### Run Maven
```
./mvnw clean install
mvn clean install
# or mvn clean install -Pwin
# or mvn clean install -Pmac
# or mvn clean install -Plinux
```
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.
+10 -11
View File
@@ -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
[ -x ../../../mvnw ] || { echo >&2 "mvnw not found at ../../../mvnw."; exit 1; }
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found."; 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=$(../../../mvnw -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
VERSION=$(mvn -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
../../../mvnw -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
# compile
../../../mvnw -B -f ../../../pom.xml clean package -DskipTests
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
cp ../../../LICENSE.txt ../../../target
cp ../../../target/cryptomator-*.jar ../../../target/mods
JAVAFX_VERSION=25.0.3
JAVAFX_VERSION=25.0.2
JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256='47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50'
JAVAFX_JMODS_SHA256='e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
if [ "${CPU_ARCH}" = "aarch64" ]; then
JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256='e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f'
JAVAFX_JMODS_SHA256='c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
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=$(../../../mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout -B -f ../../../pom.xml)
POM_JFX_VERSION=$(mvn 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 - 2026 Skymatic GmbH" \
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--app-version "${VERSION}.${REVISION_NO}" \
@@ -99,7 +99,6 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
--resource-dir ../resources
# transform AppDir
@@ -124,7 +123,7 @@ ln -s org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/meta
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
# load AppImageTool
curl -L https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
chmod +x /tmp/appimagetool.AppImage
# create AppImage
@@ -73,7 +73,6 @@
<url type="faq">https://community.cryptomator.org/c/kb/faq</url>
<url type="help">https://docs.cryptomator.org/</url>
<url type="translate">https://translate.cryptomator.org</url>
<url type="vcs-browser">https://github.com/cryptomator/cryptomator</url>
<developer id="de.skymatic">
<name>Skymatic GmbH</name>
@@ -84,15 +83,6 @@
</content_rating>
<releases>
<release date="2026-03-20" version="1.19.2">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.2</url>
</release>
<release date="2026-03-12" version="1.19.1">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.1</url>
</release>
<release date="2026-03-09" version="1.19.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.0</url>
</release>
<release date="2025-11-12" version="1.18.0">
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.18.0</url>
</release>
+1 -1
View File
@@ -2,7 +2,7 @@ Source: cryptomator
Maintainer: Cryptobot <releases@cryptomator.org>
Section: utils
Priority: optional
Build-Depends: debhelper (>=10), coffeelibs-jdk-26 (>= 26.0.1+8-0ppa1), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1
Build-Depends: debhelper (>=10), openjdk-25-jdk (>= 25+36), 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
+3 -3
View File
@@ -4,11 +4,12 @@
# 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
@@ -45,7 +46,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 - 2026 Skymatic GmbH" \
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--java-options "-Dfile.encoding=\"utf-8\"" \
@@ -63,7 +64,6 @@ override_dh_auto_build:
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
--resource-dir resources \
--verbose
-15
View File
@@ -1,15 +0,0 @@
#!/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 "$@"
@@ -1,182 +0,0 @@
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'}
+7 -8
View File
@@ -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=26.0.1+8
_jfxver=25.0.3
_jdkver=25.0.2+10
_jfxver=25.0.2
_src_app_dir=cryptomator-${pkgver//_/-}
source=($SOURCES);
source_x86_64=("jdk-${_jdkver}.tar.gz::https://github.com/adoptium/temurin${_jdkver:0:2}-binaries/releases/download/jdk-${_jdkver//\+/%2B}/OpenJDK${_jdkver:0:2}U-jdk_x64_linux_hotspot_${_jdkver//\+/_}.tar.gz"
@@ -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=('8e512f13e575a43655fc92319436c94890c137b9035cc6bd6f9cf24239704d3a'
'47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50')
sha256sums_aarch64=('613f9b2861dea937b24d5eca745ef8567733b377d0bb612195acaad0e3f61360'
'e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f')
sha256sums_x86_64=('987387933b64b9833846dee373b640440d3e1fd48a04804ec01a6dbf718e8ab8'
'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b')
sha256sums_aarch64=('a9d73e711d967dc44896d4f430f73a68fd33590dabc29a7f2fb9f593425b854c'
'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646')
options=('!strip')
validpgpkeys=('58117AFA1F85B3EEC154677D615D449FE6E6A235')
@@ -47,7 +47,7 @@ build() {
cd "${srcdir}/${_src_app_dir}"
mvn -B clean package -DskipTests
mvn -B clean package -DskipTests -Plinux
cp LICENSE.txt target
cp target/cryptomator-*.jar target/mods
@@ -94,7 +94,6 @@ build() {
--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.hub.enableTrustOnFirstUse=true" \
--app-version "${pkgver//_*/}" \
--verbose
}
+9 -10
View File
@@ -24,29 +24,29 @@ rm -rf runtime dmg *.app *.dmg
# set variables
APP_NAME="Cryptomator"
VENDOR="Skymatic GmbH"
COPYRIGHT_YEARS="2016 - 2026"
COPYRIGHT_YEARS="2016 - 2025"
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=`../../../mvnw -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
VERSION_NO=`mvn -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.3
JAVAFX_VERSION=25.0.2
JAVAFX_ARCH="undefined"
JAVAFX_JMODS_SHA256="undefined"
if [ "$(machine)" = "arm64e" ]; then
JAVAFX_ARCH="aarch64"
JAVAFX_JMODS_SHA256="a52014d625b8b04e57fd71650f881c1397542b4018e1b04f1b4e66c8800a1f34"
JAVAFX_JMODS_SHA256="4cd258001c75af7047005c5c891e2400ed11d24fbb09412324c0cbaf8b503c5a"
else
JAVAFX_ARCH="x64"
JAVAFX_JMODS_SHA256="3512fabe43aee467538d329cfbbaab3c53dff2a810f0d54e381f461d5e0fac43"
JAVAFX_JMODS_SHA256="0b4d8463f03901b7425d94628e4116b7078abb8dd540fbec415266fac20bda5c"
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
[ -x ../../../mvnw ] || { echo >&2 "mvnw not found at ../../../mvnw."; exit 1; }
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found. Fix by 'brew install maven'."; 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=$(../../../mvnw -f../../../pom.xml help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
POM_JFX_VERSION=$(mvn -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
../../../mvnw -B -f../../../pom.xml clean package -DskipTests -Pmac
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../LICENSE.txt ../../../target
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
@@ -125,7 +125,6 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
--resource-dir ../resources
@@ -137,7 +136,7 @@ sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Inf
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
# generate license
../../../mvnw -B -f../../../pom.xml license:add-third-party \
mvn -B -f../../../pom.xml license:add-third-party \
-Dlicense.thirdPartyFilename=license.rtf \
-Dlicense.outputDirectory=dist/mac/dmg/resources \
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \
+66 -91
View File
@@ -16,19 +16,6 @@ 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
@@ -78,8 +65,7 @@ Write-Host "`$Env:JAVA_HOME=$Env:JAVA_HOME"
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
# compile
Invoke-CommandWithExitCheck -Command `
"mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "clean", "package", "-DskipTests", "-Pwin")
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
# add runtime
@@ -107,9 +93,9 @@ switch ($archName) {
$jmodPaths = "$Env:JAVA_HOME/jmods"
}
'x64' {
$javaFxVersion='25.0.3'
$javaFxVersion='25.0.2'
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
$javaFxJmodsSHA256 = '0bf9b83260b85607a9ba200124debabd9cdb013cbc0d659e62a20192a7137907'
$javaFxJmodsSHA256 = '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3'
$javaFxJmods = '.\resources\jfxJmods.zip'
if( !(Test-Path -Path $javaFxJmods) ) {
@@ -143,18 +129,16 @@ if ((& "$Env:JAVA_HOME\bin\jlink" --help | Select-String -Pattern "Linking from
}
### create runtime
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
)
& "$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
$appPath = ".\$AppName"
if ($clean -and (Test-Path -Path $appPath)) {
@@ -183,7 +167,6 @@ $javaOptions = @(
"--java-options", "-Dcryptomator.showTrayIcon=true"
"--java-options", "-Dcryptomator.buildNumber=`"msi-$revisionNo`""
"--java-options", "-Dcryptomator.disableUpdateCheck=false"
"--java-options", "-Dcryptomator.hub.enableTrustOnFirstUse=true"
)
@@ -211,15 +194,14 @@ if ($LASTEXITCODE -ne 0) {
}
#Create RTF license for msi
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")
&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"
# patch app dir
Copy-Item "contrib\*" -Destination "$AppName"
@@ -227,46 +209,42 @@ attrib -r "$AppName\$AppName.exe"
attrib -r "$AppName\${AppName} (Debug).exe"
# create .msi
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources\"
$Env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT = "${Env:JP_WIXWIZARD_RESOURCES}".Replace('\', '\\');
$Env:JP_WIXHELPER_DIR = ""
$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
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"
)
if ($LASTEXITCODE -ne 0) {
Write-Error "jpackage MSI failed with exit code $LASTEXITCODE"
return 1;
}
#Create RTF license for bundle
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")
&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"
# download Winfsp
$winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
@@ -292,21 +270,18 @@ Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninsta
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi" -Force
# create bundle including winfsp
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"
)
& 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"
Write-Host "Created EXE installer .\installer\$AppName-Installer.exe"
return 0;
+1 -4
View File
@@ -4,18 +4,15 @@
:: 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% -Action %ACTION%
-LoopbackAlias %LOOPBACK_ALIAS%
:: Return the exit code from PowerShell
exit /b %ERRORLEVEL%
+11 -36
View File
@@ -1,17 +1,15 @@
#Requires -RunAsAdministrator
Param(
[Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias,
[string] $Action = "install"
[Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias
)
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) {
@@ -20,26 +18,9 @@ function Add-AliasToHost {
}
}
$content = Get-Content $hostsFile
$content += "`r`n$aliasLine"
$content | Set-Content "$hostsFile.tmp" -Encoding ascii
Move-Item "$hostsFile.tmp" $hostsFile -Force
Add-Content -Path $hostsFile -Encoding ascii -Value "`r`n$aliasLine"
}
# 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 {
@@ -73,20 +54,14 @@ 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'
Set-WebDAVFileSizeLimit
Write-Output 'Set WebDAV file size limit'
Add-AliasToHost $LoopbackAlias
Write-Output 'Ensured alias exists in hosts file'
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."
}
Set-WebDAVFileSizeLimit
Write-Output 'Set WebDAV file size limit'
Edit-ProviderOrder
Write-Output 'Ensured correct provider order'
exit 0
+1 -1
View File
@@ -1,4 +1,4 @@
mime-type=application/vnd.cryptomator.vault
extension=cryptomator
description=Cryptomator Vault File
icon=C:\\Users\\Arbeit\\Skymatic\\cryptomator-jdk26-jpackage\\dist\\win\\resources\\Cryptomator-Vault.ico
icon=resources/Cryptomator-Vault.ico
-4
View File
@@ -1,4 +0,0 @@
mime-type=application/vnd.cryptomator.vault
extension=cryptomator
description=Cryptomator Vault File
icon=${env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT}Cryptomator-Vault.ico
+8 -11
View File
@@ -68,7 +68,7 @@
<?endif?>
<!-- TODO: how does this work again? -->
<ns0:Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)msica.dll"></ns0:Binary>
<ns0:Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)\wixhelper.dll" />
<ns0:CustomAction Id="JpFindRelatedProducts" BinaryRef="JpCaDll" DllEntry="FindRelatedProductsEx" />
<?ifndef SkipCryptomatorLegacyCheck ?>
@@ -109,7 +109,7 @@
</ns0:CreateFolder>
</ns0:Component>
<ns0:Component Id="AdminConfigFile" NeverOverwrite="yes" Permanent="yes">
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\config.properties" Name="config.properties" KeyPath="yes">
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\cryptomator.config" Name="cryptomator.config" KeyPath="yes">
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
<util:PermissionEx User="Administrators" GenericAll="yes"/>
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
@@ -158,13 +158,9 @@
<ns0:CustomAction Id="DisableUserConfig" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- WebDAV patches -->
<ns0:SetProperty Id="PatchWebDAV" Value="&quot;[INSTALLDIR]patchWebDAV.bat&quot; &quot;$(var.LoopbackAlias)&quot; install" Sequence="execute" Before="PatchWebDAV" />
<ns0:SetProperty Id="PatchWebDAV" Value="&quot;[INSTALLDIR]patchWebDAV.bat&quot; &quot;$(var.LoopbackAlias)&quot;" Sequence="execute" Before="PatchWebDAV" />
<ns0:CustomAction Id="PatchWebDAV" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- WebDAV patches (Uninstall) -->
<ns0:SetProperty Id="PatchWebDAVUninstall" Value="&quot;[INSTALLDIR]patchWebDAV.bat&quot; &quot;$(var.LoopbackAlias)&quot; uninstall" Sequence="execute" Before="PatchWebDAVUninstall" />
<ns0:CustomAction Id="PatchWebDAVUninstall" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
<!-- Update check configuration -->
<ns0:SetProperty Id="PatchUpdateCheck" Value="&quot;[INSTALLDIR]patchUpdateCheck.bat&quot; &quot;[DISABLEUPDATECHECK]&quot;" Sequence="execute" Before="PatchUpdateCheck" />
<ns0:CustomAction Id="PatchUpdateCheck" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
@@ -218,8 +214,9 @@
<ns0:RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to Wix4CloseApplications_* -->
<ns0:Custom Action="DisableUserConfig" After="InstallFiles" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
<!-- Skip action on uninstall -->
<!-- TODO: don't skip action, but remove cryptomator alias from hosts file -->
<ns0:Custom Action="PatchWebDAV" After="DisableUserConfig" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
<ns0:Custom Action="PatchWebDAVUninstall" Before="RemoveFiles" Condition="Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE" />
<!-- Configure update check setting if property is provided -->
<ns0:Custom Action="PatchUpdateCheck" After="PatchWebDAV" Condition="DISABLEUPDATECHECK AND NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
</ns0:InstallExecuteSequence>
@@ -228,7 +225,7 @@
<ns0:Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
</ns0:InstallUISequence>
<ns0:WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)banner.bmp" />
<ns0:WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)background.bmp" />
<ns0:WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" />
<ns0:WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
</ns0:Package>
</ns0:Wix>
</ns0:Wix>
Vendored
-295
View File
@@ -1,295 +0,0 @@
#!/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-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -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 "$@"
Vendored
-189
View File
@@ -1,189 +0,0 @@
<# : 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-<version>,maven-mvnd-<version>-<platform>}/<hash>
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"
+41 -64
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.19.3</version>
<version>1.19.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -26,7 +26,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.jdk.version>26</project.jdk.version>
<project.jdk.version>25</project.jdk.version>
<!-- Group IDs of jars that need to stay on the class path for now -->
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
@@ -35,42 +35,43 @@
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.10.0</cryptomator.cryptofs.version>
<cryptomator.cryptolib.version>2.2.2</cryptomator.cryptolib.version>
<cryptomator.integrations.version>1.9.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.6.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.5.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.7.0</cryptomator.integrations.linux.version>
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.6.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.5.0-beta3</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.7.0-beta4</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>6.0.1</cryptomator.fuse.version>
<cryptomator.webdav.version>3.0.2</cryptomator.webdav.version>
<cryptomator.webdav.version>3.0.1</cryptomator.webdav.version>
<cryptomator.webdav-servlet.version>1.2.12</cryptomator.webdav-servlet.version>
<!-- 3rd party dependencies -->
<caffeine.version>3.2.4</caffeine.version>
<caffeine.version>3.2.3</caffeine.version>
<commons-lang3.version>3.20.0</commons-lang3.version>
<dagger.version>2.59.2</dagger.version>
<easybind.version>2.2</easybind.version>
<jackson.version>2.21.4</jackson.version>
<javafx.version>25.0.3</javafx.version>
<jwt.version>4.5.2</jwt.version>
<jackson.version>2.21.1</jackson.version>
<javafx.version>25.0.2</javafx.version>
<jwt.version>4.5.1</jwt.version>
<nimbus-jose.version>10.5</nimbus-jose.version>
<logback.version>1.5.35</logback.version>
<slf4j.version>2.0.18</slf4j.version>
<logback.version>1.5.32</logback.version>
<slf4j.version>2.0.17</slf4j.version>
<tinyoauth2.version>0.8.1</tinyoauth2.version>
<zxcvbn.version>1.9.0</zxcvbn.version>
<!-- test dependencies -->
<junit.jupiter.version>6.1.0</junit.jupiter.version>
<mockito.version>5.23.0</mockito.version>
<junit.jupiter.version>6.0.3</junit.jupiter.version>
<mockito.version>5.22.0</mockito.version>
<hamcrest.version>3.0</hamcrest.version>
<!-- build-time dependencies -->
<jetbrains.annotations.version>26.1.0</jetbrains.annotations.version>
<dependency-check.version>12.2.2</dependency-check.version>
<jacoco.version>0.8.15</jacoco.version>
<dependency-check.version>12.2.0</dependency-check.version>
<jacoco.version>0.8.14</jacoco.version>
<license-generator.version>2.7.1</license-generator.version>
<junit-tree-reporter.version>1.5.1</junit-tree-reporter.version>
<mvn-compiler.version>3.15.0</mvn-compiler.version>
<mvn-resources.version>3.5.0</mvn-resources.version>
<mvn-dependency.version>3.11.0</mvn-dependency.version>
<mvn-surefire.version>3.5.6</mvn-surefire.version>
<mvn-dependency.version>3.10.0</mvn-dependency.version>
<mvn-surefire.version>3.5.3</mvn-surefire.version>
<mvn-jar.version>3.5.0</mvn-jar.version>
<!-- Property used by surefire to determine jacoco engine -->
@@ -355,12 +356,22 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<dependencies>
<dependency>
<groupId>me.fabriciorby</groupId>
<artifactId>maven-surefire-junit5-tree-reporter</artifactId>
<version>${junit-tree-reporter.version}</version>
</dependency>
</dependencies>
<configuration>
<reportFormat>plain</reportFormat>
<consoleOutputReporter>
<disable>true</disable>
</consoleOutputReporter>
<argLine>@{surefire.jacoco.args} -javaagent:${org.mockito:mockito-core:jar} --enable-native-access=javafx.graphics</argLine>
<statelessTestsetInfoReporter
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
</statelessTestsetInfoReporter>
</configuration>
</plugin>
<plugin>
@@ -502,6 +513,9 @@
<os>
<family>mac</family>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
@@ -513,55 +527,15 @@
</profile>
<profile>
<id>linux-aarch64</id>
<id>linux</id>
<activation>
<os>
<family>unix</family>
<name>linux</name>
<arch>aarch64</arch>
</os>
</activation>
<dependencies>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-linux</artifactId>
<version>${cryptomator.integrations.linux.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<version>${javafx.version}</version>
<classifier>linux-aarch64</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
<classifier>linux-aarch64</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
<classifier>linux-aarch64</classifier>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
<classifier>linux-aarch64</classifier>
</dependency>
</dependencies>
</profile>
<profile>
<id>linux-x86_64</id>
<activation>
<os>
<family>unix</family>
<name>linux</name>
<arch>amd64</arch>
<name>Linux</name>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
@@ -578,6 +552,9 @@
<os>
<family>windows</family>
</os>
<property>
<name>idea.version</name>
</property>
</activation>
<dependencies>
<dependency>
@@ -9,13 +9,10 @@ import org.slf4j.LoggerFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@@ -23,22 +20,22 @@ public class Environment {
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
private static final int DEFAULT_MIN_PW_LENGTH = 8;
public static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
public static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
public static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
public static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
public static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
public static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
public static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
public static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
public static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
public static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
public static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
public static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
public static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
public static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
public static final String HUB_ALLOWED_HOSTS_PROP_NAME = "cryptomator.hub.allowedHosts";
public static final String HUB_TOFU_PROP_NAME = "cryptomator.hub.enableTrustOnFirstUse";
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
private static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
private static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
private static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
private static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
private static final String LICENSE_CHAIN_REQUIRED_CN_PROP_NAME = "cryptomator.licenseChainRequiredCn";
private static final String DEFAULT_LICENSE_CHAIN_REQUIRED_CN = "License Intermediate CA (Prod)";
private Environment() {}
@@ -62,8 +59,7 @@ public class Environment {
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
logCryptomatorSystemProperty(HUB_ALLOWED_HOSTS_PROP_NAME);
logCryptomatorSystemProperty(HUB_TOFU_PROP_NAME);
logCryptomatorSystemProperty(LICENSE_CHAIN_REQUIRED_CN_PROP_NAME);
}
public static Environment getInstance() {
@@ -152,16 +148,8 @@ public class Environment {
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
}
public Set<String> hubAllowedHosts() {
var allowedHubHostsString = System.getProperty(HUB_ALLOWED_HOSTS_PROP_NAME, "");
return Arrays.stream(allowedHubHostsString.split(","))
.map(String::trim)
.filter(Predicate.not(String::isEmpty))
.collect(Collectors.toUnmodifiableSet());
}
public boolean hubTrustOnFirstUse() {
return Boolean.getBoolean(HUB_TOFU_PROP_NAME);
public String getLicenseChainRequiredCn() {
return System.getProperty(LICENSE_CHAIN_REQUIRED_CN_PROP_NAME, DEFAULT_LICENSE_CHAIN_REQUIRED_CN);
}
private Optional<Path> getPath(String propertyName) {
@@ -2,31 +2,60 @@ package org.cryptomator.common;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.google.common.io.BaseEncoding;
import org.jetbrains.annotations.VisibleForTesting;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertPathValidatorException;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.List;
import java.util.Optional;
@Singleton
class LicenseChecker {
private final JWTVerifier verifier;
private static final String LICENSE_ROOT_CERTIFICATE = """
-----BEGIN CERTIFICATE-----
MIIBqDCCAVqgAwIBAgIUKNImuR2JD+NyAWaYzb8V8w8SdFwwBQYDK2VwMD8xCzAJ
BgNVBAYTAkRFMRYwFAYDVQQKDA1Ta3ltYXRpYyBHbWJIMRgwFgYDVQQDDA9MaWNl
bnNlIFJvb3QgQ0EwIBcNMjYwMjI2MTMzMTQxWhgPMjA3NjAyMTQxMzMxNDFaMD8x
CzAJBgNVBAYTAkRFMRYwFAYDVQQKDA1Ta3ltYXRpYyBHbWJIMRgwFgYDVQQDDA9M
aWNlbnNlIFJvb3QgQ0EwKjAFBgMrZXADIQCOyUIv+3Ust66VWJ8yH5ruJGxyZC1u
LK2Yxb+ZtPGAQKNmMGQwEgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMC
AQYwHQYDVR0OBBYEFGhKUh0jCta2wXzxrUldIBqB4Bz3MB8GA1UdIwQYMBaAFGhK
Uh0jCta2wXzxrUldIBqB4Bz3MAUGAytlcANBAOERjFKpGnjxH1nh2u5lsCjX65zz
XisC7XFaZQikVLKzHK+YTIusi3x7dGCFBjO/m3ieQpt7BsaPo0lLL719pQ8=
-----END CERTIFICATE-----
""";
private final ECPublicKey legacyPublicKey;
private final X509Certificate rootCertificate;
private final String requiredChainCn;
@Inject
public LicenseChecker(@Named("licensePublicKey") String pemEncodedPublicKey) {
Algorithm algorithm = Algorithm.ECDSA512(decodePublicKey(pemEncodedPublicKey), null);
this.verifier = JWT.require(algorithm).build();
public LicenseChecker(@Named("licensePublicKey") String legacyLicensePublicKey, Environment environment) {
this(legacyLicensePublicKey, LICENSE_ROOT_CERTIFICATE, environment.getLicenseChainRequiredCn());
}
@VisibleForTesting
LicenseChecker(String legacyLicensePublicKey, String trustedRootCert, String requiredChainCn) {
this.legacyPublicKey = decodePublicKey(legacyLicensePublicKey);
this.rootCertificate = X509Helper.parsePemCertificate(trustedRootCert);
this.requiredChainCn = requiredChainCn;
}
private static ECPublicKey decodePublicKey(String pemEncodedPublicKey) {
@@ -47,10 +76,44 @@ class LicenseChecker {
public Optional<DecodedJWT> check(String licenseKey) {
try {
DecodedJWT decodedJwt = JWT.decode(licenseKey);
Claim x5cClaim = decodedJwt.getHeaderClaim("x5c");
ECPublicKey signingKey;
if (x5cClaim == null || x5cClaim.isMissing()) {
signingKey = this.legacyPublicKey;
} else {
var certChain = verifyChain(x5cClaim);
signingKey = asEcPublicKey(certChain.getFirst().getPublicKey());
}
JWTVerifier verifier = JWT.require(Algorithm.ECDSA512(signingKey, null)).build();
return Optional.of(verifier.verify(licenseKey));
} catch (JWTVerificationException exception) {
} catch (JWTVerificationException | GeneralSecurityException e) {
return Optional.empty();
}
}
private List<X509Certificate> verifyChain(Claim x5cClaim) throws GeneralSecurityException {
List<String> x5cEntries = x5cClaim.asList(String.class);
if (x5cEntries == null || x5cEntries.isEmpty()) {
throw new CertPathValidatorException("x5c claim is empty.");
}
List<X509Certificate> certChain = X509Helper.parseX5cCertificateChain(x5cEntries);
boolean containsRequiredCn = certChain.stream() //
.flatMap(cert -> X509Helper.extractCommonName(cert).stream()) //
.anyMatch(requiredChainCn::equals);
if (!containsRequiredCn) {
throw new CertPathValidatorException("x5c certificate chain does not contain required CN " + requiredChainCn);
}
X509Helper.validateChain(certChain, rootCertificate);
return certChain;
}
private static ECPublicKey asEcPublicKey(PublicKey publicKey) {
if (publicKey instanceof ECPublicKey ecPublicKey) {
return ecPublicKey;
} else {
throw new IllegalArgumentException("Leaf certificate key is not an EC public key.");
}
}
}
@@ -0,0 +1,121 @@
package org.cryptomator.common;
import com.google.common.io.BaseEncoding;
import javax.security.auth.x500.X500Principal;
import java.io.ByteArrayInputStream;
import java.security.GeneralSecurityException;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertificateFactory;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.security.cert.PKIXParameters;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
final class X509Helper {
private static final String CERT_BEGIN = "-----BEGIN CERTIFICATE-----";
private static final String CERT_END = "-----END CERTIFICATE-----";
private X509Helper() {
}
static X509Certificate parsePemCertificate(String pemCertificate) {
String base64 = pemCertificate.replace(CERT_BEGIN, "").replace(CERT_END, "").replaceAll("\\s", "");
byte[] der = BaseEncoding.base64().decode(base64);
return parseDerCertificate(der);
}
static List<X509Certificate> parseX5cCertificateChain(List<String> x5cEntries) {
List<X509Certificate> certificates = new ArrayList<>(x5cEntries.size());
for (String x5cEntry : x5cEntries) {
byte[] der = BaseEncoding.base64().decode(x5cEntry);
certificates.add(parseDerCertificate(der));
}
return certificates;
}
static void validateChain(List<X509Certificate> certificateChain, X509Certificate rootCertificate) throws GeneralSecurityException {
if (certificateChain.isEmpty()) {
throw new IllegalArgumentException("Certificate chain must not be empty.");
}
List<X509Certificate> certPathCertificates = new ArrayList<>(certificateChain);
if (rootCertificate.equals(certPathCertificates.get(certPathCertificates.size() - 1))) {
certPathCertificates.remove(certPathCertificates.size() - 1);
}
if (certPathCertificates.isEmpty()) {
throw new IllegalArgumentException("Certificate path must contain at least one non-root certificate.");
}
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
CertPath certPath = certificateFactory.generateCertPath(certPathCertificates);
PKIXParameters params = new PKIXParameters(Set.of(new TrustAnchor(rootCertificate, null)));
params.setRevocationEnabled(false);
CertPathValidator.getInstance("PKIX").validate(certPath, params);
}
static Optional<String> extractCommonName(X509Certificate cert) {
String rfc2253Name = cert.getSubjectX500Principal().getName(X500Principal.RFC2253);
for (String rdn : splitRdns(rfc2253Name)) {
int equalsPos = rdn.indexOf('=');
if (equalsPos > 0 && "CN".equalsIgnoreCase(rdn.substring(0, equalsPos).trim())) {
return Optional.of(unescapeRdnValue(rdn.substring(equalsPos + 1).trim()));
}
}
return Optional.empty();
}
private static List<String> splitRdns(String distinguishedName) {
List<String> rdns = new ArrayList<>();
StringBuilder current = new StringBuilder();
boolean escaped = false;
for (int i = 0; i < distinguishedName.length(); i++) {
char c = distinguishedName.charAt(i);
if (escaped) {
current.append(c);
escaped = false;
} else if (c == '\\') {
current.append(c);
escaped = true;
} else if (c == ',') {
rdns.add(current.toString());
current.setLength(0);
} else {
current.append(c);
}
}
rdns.add(current.toString());
return rdns;
}
private static String unescapeRdnValue(String value) {
StringBuilder unescaped = new StringBuilder(value.length());
boolean escaped = false;
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
if (escaped) {
unescaped.append(c);
escaped = false;
} else if (c == '\\') {
escaped = true;
} else {
unescaped.append(c);
}
}
return unescaped.toString();
}
private static X509Certificate parseDerCertificate(byte[] derEncodedCertificate) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(derEncodedCertificate));
} catch (GeneralSecurityException e) {
throw new IllegalArgumentException("Invalid certificate.", e);
}
}
}
@@ -24,12 +24,9 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.geometry.NodeOrientation;
import java.nio.file.Path;
import java.time.Instant;
import java.util.HashSet;
import java.util.Set;
public class Settings {
@@ -81,7 +78,6 @@ public class Settings {
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
public final StringProperty lastUpdateAttemptedByVersion;
public final ObservableSet<String> trustedHosts;
public static Settings create(SettingsProvider provider, Environment env) {
var defaults = new SettingsJson();
@@ -122,7 +118,6 @@ public class Settings {
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
this.previouslyUsedVaultDirectory = new SimpleObjectProperty<>(this, "previouslyUsedVaultDirectory", json.previouslyUsedVaultDirectory);
this.lastUpdateAttemptedByVersion = new SimpleStringProperty(this, "lastUpdateAttemptedByVersion", json.lastUpdateAttemptedByVersion);
this.trustedHosts = FXCollections.observableSet(json.trustedHosts);
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
@@ -154,7 +149,6 @@ public class Settings {
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
trustedHosts.addListener(this::somethingChanged);
}
@SuppressWarnings("deprecation")
@@ -213,7 +207,6 @@ public class Settings {
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
json.trustedHosts = Set.copyOf(trustedHosts);
return json;
}
@@ -4,23 +4,17 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
class SettingsJson {
@JsonProperty("directories")
@JsonSetter(nulls = Nulls.AS_EMPTY)
List<VaultSettingsJson> directories = new ArrayList<>();
List<VaultSettingsJson> directories = List.of();
@JsonProperty("writtenByVersion")
String writtenByVersion;
@@ -105,8 +99,4 @@ class SettingsJson {
@JsonProperty("lastUpdateAttemptedByVersion")
String lastUpdateAttemptedByVersion;
@JsonProperty("trustedHosts")
@JsonSetter(nulls = Nulls.AS_EMPTY)
Set<String> trustedHosts = new HashSet<>();
}
@@ -1,32 +0,0 @@
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;
}
}
@@ -22,31 +22,20 @@ 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.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;
import static org.cryptomator.common.vaults.VaultState.Value.*;
@Singleton
public class VaultListManager {
@@ -83,57 +72,18 @@ 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();
assertIsVaultDirectory(normalizedPathToVault);
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);
if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) {
throw new NoSuchFileException(normalizedPathToVault.toString(), null, "Not a vault directory");
}
}
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);
}
return get(normalizedPathToVault) //
.orElseGet(() -> {
Vault newVault = create(newVaultSettings(normalizedPathToVault));
vaultList.add(newVault);
return newVault;
});
}
private VaultSettings newVaultSettings(Path path) {
@@ -203,7 +153,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 -> {
@@ -27,8 +27,6 @@ import java.util.Set;
* <li>cryptomator.p12Path</li>
* <li>cryptomator.mountPointsDir</li>
* <li>cryptomator.disableUpdateCheck</li>
* <li>cryptomator.hub.allowedHosts</li>
* <li>cryptomator.hub.enableTrustOnFirstUse</li>
* </ul>
*
* @see Properties
@@ -44,9 +42,7 @@ class AdminPropertiesFactory {
"cryptomator.pluginDir", //
"cryptomator.p12Path", //
"cryptomator.mountPointsDir", //
"cryptomator.disableUpdateCheck", //
"cryptomator.hub.allowedHosts", //
"cryptomator.hub.enableTrustOnFirstUse");
"cryptomator.disableUpdateCheck");
/**
@@ -2,14 +2,12 @@ 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;
@@ -43,7 +41,6 @@ public class ChooseExistingVaultController implements FxController {
private final ObjectProperty<Vault> vault;
private final VaultListManager vaultListManager;
private final ResourceBundle resourceBundle;
private final Dialogs dialogs;
private final ObservableValue<Image> screenshot;
@Inject
@@ -54,7 +51,6 @@ public class ChooseExistingVaultController implements FxController {
@AddVaultWizardWindow ObjectProperty<Vault> vault, //
VaultListManager vaultListManager, //
ResourceBundle resourceBundle, //
Dialogs dialogs, //
FxApplicationStyle applicationStyle) {
this.window = window;
this.successScene = successScene;
@@ -63,7 +59,6 @@ public class ChooseExistingVaultController implements FxController {
this.vault = vault;
this.vaultListManager = vaultListManager;
this.resourceBundle = resourceBundle;
this.dialogs = dialogs;
this.screenshot = applicationStyle.appliedAppThemeProperty().map(this::selectScreenshot);
}
@@ -92,9 +87,6 @@ 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());
@@ -19,7 +19,6 @@ public enum FxmlFile {
HEALTH_START("/fxml/health_start.fxml"), //
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
HUB_NO_KEYCHAIN("/fxml/hub_no_keychain.fxml"), //
HUB_CHECK_HOST_TRUST("/fxml/hub_check_host_trust.fxml"), //
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
@@ -30,7 +29,6 @@ public enum FxmlFile {
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"), //
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
HUB_UNTRUSTED_HOST("/fxml/hub_untrusted_host.fxml"), //
HUB_REQUIRE_ACCOUNT_INIT("/fxml/hub_require_account_init.fxml"), //
LOCK_FORCED("/fxml/lock_forced.fxml"), //
LOCK_FAILED("/fxml/lock_failed.fxml"), //
@@ -58,6 +58,8 @@ public class DecryptFileNamesViewController implements FxController {
private final Stage window;
private final Vault vault;
private final ResourceBundle resourceBundle;
private final List<Path> initialList;
@FXML
public TableColumn<CipherAndCleartext, String> ciphertextColumn;
@FXML
@@ -66,11 +68,12 @@ public class DecryptFileNamesViewController implements FxController {
public TableView<CipherAndCleartext> cipherToCleartextTable;
@Inject
public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, ResourceBundle resourceBundle) {
public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, @DecryptNameWindow List<Path> pathsToDecrypt, ResourceBundle resourceBundle) {
this.window = window;
this.vault = vault;
this.resourceBundle = resourceBundle;
this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList());
this.initialList = pathsToDecrypt;
}
@FXML
@@ -94,7 +97,8 @@ public class DecryptFileNamesViewController implements FxController {
});
cipherToCleartextTable.setOnDragDropped(event -> {
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
decrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
checkAndDecrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
cipherToCleartextTable.setItems(mapping);
}
});
cipherToCleartextTable.setOnDragExited(_ -> cipherToCleartextTable.setItems(mapping));
@@ -120,7 +124,9 @@ public class DecryptFileNamesViewController implements FxController {
});
}
});
window.setOnHidden(_ -> mapping.clear());
if (!initialList.isEmpty()) {
checkAndDecrypt(initialList);
}
}
private void copySingleCelltoClipboard() {
@@ -143,18 +149,10 @@ public class DecryptFileNamesViewController implements FxController {
fileChooser.setInitialDirectory(vault.getPath().toFile());
var ciphertextNodes = fileChooser.showOpenMultipleDialog(window);
if (ciphertextNodes != null) {
decrypt(ciphertextNodes.stream().map(File::toPath).toList());
checkAndDecrypt(ciphertextNodes.stream().map(File::toPath).toList());
}
}
public void decrypt(List<Path> pathsToDecrypt) {
if (pathsToDecrypt.isEmpty()) {
return;
}
checkAndDecrypt(pathsToDecrypt);
cipherToCleartextTable.setItems(mapping);
}
private void checkAndDecrypt(List<Path> pathsToDecrypt) {
mapping.clear();
//Assumption: All files are in the same directory
@@ -28,28 +28,23 @@ public interface DecryptNameComponent {
@FxmlScene(FxmlFile.DECRYPTNAMES)
Lazy<Scene> decryptNamesView();
DecryptFileNamesViewController controller();
@DecryptNameWindow
Vault vault();
default void showDecryptFileNameWindow(List<Path> pathsToDecrypt) {
default void showDecryptFileNameWindow() {
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);
DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner, @BindsInstance @DecryptNameWindow List<Path> pathsToDecrypt);
}
}
@@ -1,7 +1,6 @@
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;
@@ -140,24 +139,6 @@ 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) //
@@ -1,18 +1,15 @@
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;
@@ -32,18 +29,14 @@ 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<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService, @PrimaryStage Stage primaryStage, Dialogs dialogs) {
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService) {
this.launchEventQueue = launchEventQueue;
this.executorService = executorService;
this.appWindows = appWindows;
this.vaultListManager = vaultListManager;
this.vaultService = vaultService;
this.primaryStage = primaryStage;
this.dialogs = dialogs;
}
public void startHandlingLaunchEvents() {
@@ -65,33 +58,31 @@ class AppLaunchEventHandler {
private void handleLaunchEvent(AppLaunchEvent event) {
switch (event.type()) {
case REVEAL_APP -> appWindows.showMainWindow();
case OPEN_FILE -> event.pathsToOpen().forEach(this::openPotentialVault);
case OPEN_FILE -> Platform.runLater(() -> {
event.pathsToOpen().forEach(this::openPotentialVault);
});
default -> LOG.warn("Unsupported event type: {}", event.type());
}
}
// TODO deduplicate MainWindowController...
private void openPotentialVault(Path path) {
Path potentialVaultPath = path.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT) ? path.getParent() : path;
Optional<Vault> 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);
}
});
return;
}
assert Platform.isFxApplicationThread();
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());
Path potentialVaultPath = path.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT) ? path.getParent() : path;
final Optional<Vault> 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);
}
} else {
vaultListManager.add(potentialVaultPath);
LOG.debug("Added vault {}", potentialVaultPath);
}
} catch (IOException e) {
LOG.error("Failed to add vault {}", potentialVaultPath, e);
LOG.error("Failed to add vault " + path, e);
}
}
@@ -64,6 +64,17 @@ 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
@@ -77,4 +88,10 @@ abstract class FxApplicationModule {
return factory.create();
}
@Provides
@FxApplicationScoped
static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) {
return factory.create();
}
}
@@ -39,7 +39,6 @@ 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 {
@@ -48,15 +47,15 @@ public class FxApplicationWindows {
private final Stage primaryStage;
private final Optional<TrayIntegrationProvider> trayIntegration;
private final CachedLazy<MainWindowComponent> mainWindow;
private final CachedLazy<PreferencesComponent> preferencesWindow;
private final Lazy<MainWindowComponent> mainWindow;
private final Lazy<PreferencesComponent> 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 CachedLazy<EventViewComponent> eventViewWindow;
private final CachedLazy<NotificationComponent> notificationWindow;
private final Lazy<EventViewComponent> eventViewWindow;
private final Lazy<NotificationComponent> notificationWindow;
private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final ShareVaultComponent.Factory shareVaultWindow;
@@ -66,8 +65,8 @@ public class FxApplicationWindows {
@Inject
public FxApplicationWindows(@PrimaryStage Stage primaryStage, //
Optional<TrayIntegrationProvider> trayIntegration, //
MainWindowComponent.Builder mainWindowBuilder, //
PreferencesComponent.Builder preferencesWindowBuilder, //
Lazy<MainWindowComponent> mainWindow, //
Lazy<PreferencesComponent> preferencesWindow, //
QuitComponent.Builder quitWindowBuilder, //
UnlockComponent.Factory unlockWorkflowFactory, //
UpdateReminderComponent.Factory updateReminderWindowFactory, //
@@ -75,21 +74,21 @@ public class FxApplicationWindows {
ErrorComponent.Factory errorWindowFactory, //
VaultOptionsComponent.Factory vaultOptionsWindow, //
ShareVaultComponent.Factory shareVaultWindow, //
EventViewComponent.Factory eventViewWindowFactory, //
NotificationComponent.Factory notificationWindowFactory, //
Lazy<EventViewComponent> eventViewWindow, //
Lazy<NotificationComponent> notificationWindow,
ExecutorService executor, //
Dialogs dialogs) {
this.primaryStage = primaryStage;
this.trayIntegration = trayIntegration;
this.mainWindow = new CachedLazy<>(mainWindowBuilder::build);
this.preferencesWindow = new CachedLazy<>(preferencesWindowBuilder::build);
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
this.quitWindowBuilder = quitWindowBuilder;
this.unlockWorkflowFactory = unlockWorkflowFactory;
this.updateReminderWindowFactory = updateReminderWindowFactory;
this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory;
this.eventViewWindow = new CachedLazy<>(eventViewWindowFactory::create);
this.notificationWindow = new CachedLazy<>(notificationWindowFactory::create);
this.eventViewWindow = eventViewWindow;
this.notificationWindow = notificationWindow;
this.executor = executor;
this.vaultOptionsWindow = vaultOptionsWindow;
this.shareVaultWindow = shareVaultWindow;
@@ -219,29 +218,4 @@ public class FxApplicationWindows {
LOG.error("Failed to display stage", error);
}
}
private static class CachedLazy<T> implements Lazy<T> {
private final Supplier<T> supplier;
private volatile T instance = null;
public CachedLazy(Supplier<T> 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;
}
}
}
@@ -1,5 +1,6 @@
package org.cryptomator.ui.keyloading.hub;
import com.nimbusds.jose.JWEObject;
import dagger.Lazy;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
@@ -11,6 +12,8 @@ import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.WorkerStateEvent;
@@ -1,179 +0,0 @@
package org.cryptomator.ui.keyloading.hub;
import dagger.Lazy;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
import java.net.URI;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
@KeyLoadingScoped
public class CheckHostTrustController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(CheckHostTrustController.class);
private static final String CHECK_KEY = "hub.checkHostTrust.message.check";
private static final String ASK_SINGULAR_KEY = "hub.checkHostTrust.message.ask";
private static final String ASK_PLURAL_KEY = "hub.checkHostTrust.message.ask.plural";
private static final String TRUSTED_CRYPTOMATOR_CLOUD_DOMAIN = ".cryptomator.cloud";
private final Stage window;
private final HubConfig hubConfig;
private final URI canonicalHubUri;
private final URI canonicalAuthUri;
private final Lazy<Scene> authFlowScene;
private final Lazy<Scene> untrustedHostScene;
private final CompletableFuture<ReceivedKey> result;
private final Settings settings;
private final Environment env;
private final ResourceBundle resourceBundle;
private final SortedSet<String> hostnames;
private final StringProperty messageLabel;
@FXML
private TextFlow hostnamesFlow;
@Inject
public CheckHostTrustController(@KeyLoading Stage window, //
HubConfig hubConfig, //
@FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, //
@FxmlScene(FxmlFile.HUB_UNTRUSTED_HOST) Lazy<Scene> untrustedHostScene, //
CompletableFuture<ReceivedKey> result, //
Settings settings, //
Environment env, //
ResourceBundle resourceBundle) {
this.window = window;
this.hubConfig = hubConfig;
this.canonicalHubUri = hubConfig.getApiBaseUrl();
this.canonicalAuthUri = URI.create(hubConfig.authEndpoint);
this.authFlowScene = authFlowScene;
this.untrustedHostScene = untrustedHostScene;
this.result = result;
this.settings = settings;
this.env = env;
this.resourceBundle = resourceBundle;
this.hostnames = new TreeSet<>();
this.messageLabel = new SimpleStringProperty(resourceBundle.getString(CHECK_KEY));
}
@FXML
public void initialize() {
if (!isConsistentHubConfig()) {
LOG.warn("Inconsistent hub config detected. Denying access to protect the user.");
deny();
} else if (isAllCryptomatorCloud() && !isAnyHttpHost()) {
trust(); // trust *.cryptomator.cloud by default, domain is owned by Cryptomator maintainers
} else if (containsAllowedHosts(env.hubAllowedHosts())) {
trust(); // trust hosts explicitly allowlisted via system property
} else if (isAnyHttpHost() && !isAllLocalhost()) {
LOG.warn("Denying attempt to connect to hub instance via unencrypted HTTP.");
deny(); // never trust http hosts except for local testing
} else if (env.hubTrustOnFirstUse() && containsAllowedHosts(settings.trustedHosts)) {
trust(); // trust hosts previously allowlisted by the user
} else if (env.hubTrustOnFirstUse()) {
hostnames.add(getAuthority(canonicalHubUri));
hostnames.add(getAuthority(canonicalAuthUri));
renderHostnames(); // ask user whether to trust these hosts
} else {
LOG.warn("Cryptomator is not allowed to connect to {}. Check your {} config.", getAuthority(canonicalHubUri), Environment.HUB_ALLOWED_HOSTS_PROP_NAME);
deny();
}
}
@FXML
public void trust() {
settings.trustedHosts.addAll(hostnames);
Platform.runLater(() -> {
window.setScene(authFlowScene.get());
});
}
@FXML
public void deny() {
result.cancel(true);
Platform.runLater(() -> {
window.setScene(untrustedHostScene.get());
});
}
private void renderHostnames() {
hostnamesFlow.getChildren().clear();
for (var hostname : hostnames) {
hostnamesFlow.getChildren().add(new Text(hostname + System.lineSeparator()));
}
var messageKey = hostnames.size() > 1 ? ASK_PLURAL_KEY : ASK_SINGULAR_KEY;
messageLabel.set(resourceBundle.getString(messageKey));
}
private boolean isConsistentHubConfig() {
var canonicalHubAuthority = getAuthority(canonicalHubUri);
var canonicalAuthAuthority = getAuthority(canonicalAuthUri);
// apiBaseURL.host == deviceUrl.host == authSuccessUrl.host == authErrorUrl.host
return (hubConfig.apiBaseUrl == null || getAuthority(hubConfig.apiBaseUrl).equals(canonicalHubAuthority)) //
&& (hubConfig.devicesResourceUrl == null || getAuthority(hubConfig.devicesResourceUrl).equals(canonicalHubAuthority)) //
&& getAuthority(hubConfig.authSuccessUrl).equals(canonicalHubAuthority) //
&& getAuthority(hubConfig.authErrorUrl).equals(canonicalHubAuthority) //
// authUrl.host == tokenUrl.host:
&& getAuthority(hubConfig.tokenEndpoint).equals(canonicalAuthAuthority);
}
private boolean isAllCryptomatorCloud() {
return canonicalHubUri.getHost().endsWith(TRUSTED_CRYPTOMATOR_CLOUD_DOMAIN) && canonicalAuthUri.getHost().endsWith(TRUSTED_CRYPTOMATOR_CLOUD_DOMAIN);
}
private boolean isAnyHttpHost() {
return "http".equalsIgnoreCase(canonicalHubUri.getScheme()) || "http".equalsIgnoreCase(canonicalAuthUri.getScheme());
}
private boolean isAllLocalhost() {
return "localhost".equalsIgnoreCase(canonicalHubUri.getHost()) && "localhost".equalsIgnoreCase(canonicalAuthUri.getHost());
}
@VisibleForTesting
boolean containsAllowedHosts(Set<String> allowedHubHosts) {
return allowedHubHosts.contains(getAuthority(canonicalHubUri)) && allowedHubHosts.contains(getAuthority(canonicalAuthUri));
}
public static String getAuthority(String string) {
return getAuthority(URI.create(string));
}
public static String getAuthority(URI uri) {
if (uri.getPort() == -1) {
return "%s://%s".formatted(uri.getScheme(), uri.getHost());
} else {
return "%s://%s:%s".formatted(uri.getScheme(), uri.getHost(), uri.getPort());
}
}
//--- JavaFX property getter & setter
public StringProperty messageLabelProperty() {
return messageLabel;
}
public String getMessageLabel() {
return messageLabel.get();
}
}
@@ -98,13 +98,6 @@ public abstract class HubKeyLoadingModule {
return fxmlLoaders.createScene(FxmlFile.HUB_NO_KEYCHAIN);
}
@Provides
@FxmlScene(FxmlFile.HUB_CHECK_HOST_TRUST)
@KeyLoadingScoped
static Scene provideHubCheckHostTrustScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_CHECK_HOST_TRUST);
}
@Provides
@FxmlScene(FxmlFile.HUB_AUTH_FLOW)
@KeyLoadingScoped
@@ -175,13 +168,6 @@ public abstract class HubKeyLoadingModule {
return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
}
@Provides
@FxmlScene(FxmlFile.HUB_UNTRUSTED_HOST)
@KeyLoadingScoped
static Scene provideHubUntrustedHostScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene(FxmlFile.HUB_UNTRUSTED_HOST);
}
@Provides
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT)
@KeyLoadingScoped
@@ -194,11 +180,6 @@ public abstract class HubKeyLoadingModule {
@FxControllerKey(NoKeychainController.class)
abstract FxController bindNoKeychainController(NoKeychainController controller);
@Binds
@IntoMap
@FxControllerKey(CheckHostTrustController.class)
abstract FxController bindCheckHostAuthenticityController(CheckHostTrustController controller);
@Binds
@IntoMap
@FxControllerKey(AuthFlowController.class)
@@ -244,11 +225,6 @@ public abstract class HubKeyLoadingModule {
@FxControllerKey(UnauthorizedDeviceController.class)
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
@Binds
@IntoMap
@FxControllerKey(UntrustedHostController.class)
abstract FxController bindUnauthorizedHostController(UntrustedHostController controller);
@Binds
@IntoMap
@FxControllerKey(RequireAccountInitController.class)
@@ -36,19 +36,19 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
private final Stage window;
private final KeychainManager keychainManager;
private final AtomicReference<String> fsOwnerId;
private final Lazy<Scene> checkHostTrustScene;
private final Lazy<Scene> authFlowScene;
private final Lazy<Scene> noKeychainScene;
private final CompletableFuture<ReceivedKey> result;
private final DeviceKey deviceKey;
@Inject
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_CHECK_HOST_TRUST) Lazy<Scene> checkHostTrustScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference<String> fsOwnerId) {
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference<String> fsOwnerId) {
this.window = window;
this.keychainManager = keychainManager;
this.fsOwnerId = fsOwnerId;
window.setTitle(windowTitle);
window.setOnCloseRequest(_ -> result.cancel(true));
this.checkHostTrustScene = checkHostTrustScene;
this.authFlowScene = authFlowScene;
this.noKeychainScene = noKeychainScene;
this.result = result;
this.deviceKey = deviceKey;
@@ -62,7 +62,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
throw new NoKeychainAccessProviderException();
}
var keypair = deviceKey.get();
showWindow(checkHostTrustScene);
showWindow(authFlowScene);
var jwe = result.get();
return jwe.decryptMasterkey(keypair.getPrivate());
} catch (NoKeychainAccessProviderException e) {
@@ -1,34 +0,0 @@
package org.cryptomator.ui.keyloading.hub;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.keyloading.KeyLoading;
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.util.concurrent.CompletableFuture;
@KeyLoadingScoped
public class UntrustedHostController implements FxController {
private final Stage window;
private final CompletableFuture<ReceivedKey> result;
@Inject
public UntrustedHostController(@KeyLoading Stage window, CompletableFuture<ReceivedKey> result) {
this.window = window;
this.result = result;
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
}
@FXML
public void close() {
window.close();
}
private void windowClosed(WindowEvent windowEvent) {
result.cancel(true);
}
}
@@ -1,7 +1,6 @@
package org.cryptomator.ui.keyloading.masterkeyfile;
import com.google.common.base.Preconditions;
import org.cryptomator.common.Constants;
import org.cryptomator.common.Passphrase;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault;
@@ -64,21 +63,16 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException {
window.setTitle(resourceBundle.getString("unlock.title").formatted(vault.getDisplayName()));
Preconditions.checkArgument(SCHEME.equalsIgnoreCase(keyId.getScheme()), "Only supports keys with scheme " + SCHEME);
if (!Constants.MASTERKEY_FILENAME.equals(keyId.getSchemeSpecificPart())) {
LOG.warn("unsupported masterkey path found in vault.cryptomator: {}", keyId.getSchemeSpecificPart());
}
try {
// determine masterkey file path:
Path filePath = vault.getPath().resolve(Constants.MASTERKEY_FILENAME);
Path filePath = vault.getPath().resolve(keyId.getSchemeSpecificPart());
if (!Files.exists(filePath)) {
filePath = askUserForMasterkeyFilePath();
}
// unlock:
if (passphrase == null) {
askForPassphrase();
}
var masterkey = masterkeyFileAccess.load(filePath, passphrase);
// backup on successful unlock:
//backup
if (filePath.startsWith(vault.getPath())) {
try {
BackupHelper.attemptBackup(filePath);
@@ -1,8 +1,9 @@
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;
@@ -57,7 +58,6 @@ public class VaultDetailUnlockedController implements FxController {
private final DecryptNameComponent.Factory decryptNameWindowFactory;
private final ResourceBundle resourceBundle;
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
private final LoadingCache<Vault, DecryptNameComponent> decryptNameWindows;
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
private final ObservableValue<Boolean> accessibleViaPath;
private final ObservableValue<Boolean> accessibleViaUri;
@@ -89,8 +89,7 @@ public class VaultDetailUnlockedController implements FxController {
this.revealPathService = revealPathService;
this.decryptNameWindowFactory = decryptNameWindowFactory;
this.resourceBundle = resourceBundle;
this.vaultStats = Caffeine.newBuilder().weakValues().build(this::buildVaultStats);
this.decryptNameWindows = Caffeine.newBuilder().weakValues().build(this::buildDecryptNameWindow);
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
this.vaultStatsBuilder = vaultStatsBuilder;
var mp = vault.flatMap(Vault::mountPointProperty);
this.accessibleViaPath = mp.map(m -> m instanceof Mountpoint.WithPath).orElse(false);
@@ -162,7 +161,7 @@ public class VaultDetailUnlockedController implements FxController {
}
private void showDecryptNameWindow(List<Path> pathsToDecrypt) {
decryptNameWindows.get(vault.get()).showDecryptFileNameWindow(pathsToDecrypt);
decryptNameWindowFactory.create(vault.get(), mainWindow, pathsToDecrypt).showDecryptFileNameWindow();
}
private boolean startsWithVaultAccessPoint(Path path) {
@@ -199,10 +198,6 @@ 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());
@@ -222,7 +217,7 @@ public class VaultDetailUnlockedController implements FxController {
@FXML
public void showVaultStatistics() {
vaultStats.get(vault.get()).showVaultStatisticsWindow();
vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
}
/* Getter/Setter */
@@ -4,7 +4,6 @@ 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;
@@ -24,7 +23,6 @@ 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;
@@ -57,7 +55,6 @@ 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;
@@ -93,7 +90,6 @@ public class VaultListController implements FxController {
private final VaultComponent.Factory vaultComponentFactory;
private final RecoveryKeyComponent.Factory recoveryKeyWindow;
private final List<MountService> mountServices;
private final ExecutorService executor;
public ListView<Vault> vaultList;
public StackPane root;
@@ -117,8 +113,7 @@ public class VaultListController implements FxController {
RecoveryKeyComponent.Factory recoveryKeyWindow, //
VaultComponent.Factory vaultComponentFactory, //
List<MountService> mountServices, //
FxFSEventList fxFSEventList, //
ExecutorService executor) {
FxFSEventList fxFSEventList) {
this.mainWindow = mainWindow;
this.vaults = vaults;
this.selectedVault = selectedVault;
@@ -132,7 +127,6 @@ 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();
@@ -330,18 +324,15 @@ public class VaultListController implements FxController {
}
private void addVault(Path 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));
try {
if (pathToVault.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT)) {
vaultListManager.add(pathToVault.getParent());
} else {
vaultListManager.add(pathToVault);
}
});
} catch (IOException e) {
LOG.debug("Not a vault: {}", pathToVault);
}
}
@FXML
@@ -21,7 +21,6 @@ import javax.inject.Inject;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ToggleGroup;
@@ -57,7 +56,6 @@ public class GeneralPreferencesController implements FxController {
public CheckBox autoCloseVaultsCheckbox;
public CheckBox debugModeCheckbox;
public CheckBox autoStartCheckbox;
public Button resetTrustedHostsButton;
public ToggleGroup nodeOrientation;
private CompletionStage<Void> keychainMigrations = CompletableFuture.completedFuture(null);
@@ -107,9 +105,6 @@ public class GeneralPreferencesController implements FxController {
quickAccessServiceChoiceBox.setConverter(new NamedServiceConverter<>());
Bindings.bindBidirectional(settings.quickAccessService, quickAccessServiceChoiceBox.valueProperty(), quickAccessSettingsConverter);
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
if (resetTrustedHostsButton != null) {
resetTrustedHostsButton.disableProperty().bind(Bindings.isEmpty(settings.trustedHosts));
}
}
private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) {
@@ -136,10 +131,6 @@ public class GeneralPreferencesController implements FxController {
return autoStartProvider.isPresent();
}
public boolean isHubTrustOnFirstUseEnabled() {
return environment.hubTrustOnFirstUse();
}
@FXML
public void toggleAutoStart() {
autoStartProvider.ifPresent(autoStart -> {
@@ -162,11 +153,6 @@ public class GeneralPreferencesController implements FxController {
return !quickAccessServices.isEmpty();
}
@FXML
public void resetTrustedHosts() {
settings.trustedHosts.clear();
}
@FXML
public void showLogfileDirectory() {
try {
@@ -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() && updateChecker.isUpdateAvailable() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate());
return !unlockedVaults.isEmpty() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate());
}
public BooleanBinding prohibitUpdateWhileUnlockedProperty() {
@@ -18,10 +18,7 @@ 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/" //
+ "?utm_source=cryptomator-desktop" //
+ "&utm_medium=app" //
+ "&utm_campaign=share-vault";
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/";
private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/security/best-practices/#sharing-of-vaults";
private final Stage window;
@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.text.TextFlow?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.keyloading.hub.CheckHostTrustController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_LEFT"
accessibleRole="DIALOG">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<Group>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="QUESTION" glyphSize="24"/>
</StackPane>
</Group>
<VBox HBox.hgrow="ALWAYS">
<Label styleClass="label-large" text="${controller.messageLabel}" wrapText="true" textAlignment="LEFT">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<TextFlow fx:id="hostnamesFlow" styleClass="text-flow" minHeight="60"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
<buttons>
<Button text="%hub.checkHostTrust.denyBtn" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#deny"/>
<Button text="%hub.checkHostTrust.trustBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#trust"/>
</buttons>
</ButtonBar>
</VBox>
</children>
</HBox>
@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.Group?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<HBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.keyloading.hub.UntrustedHostController"
minWidth="400"
maxWidth="400"
minHeight="145"
spacing="12"
alignment="TOP_LEFT"
accessibleRole="DIALOG">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<Group>
<StackPane>
<padding>
<Insets topRightBottomLeft="6"/>
</padding>
<Circle styleClass="glyph-icon-primary" radius="24"/>
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
</StackPane>
</Group>
<VBox HBox.hgrow="ALWAYS">
<Label styleClass="label-large" text="%hub.untrustedHost.message" wrapText="true" textAlignment="LEFT">
<padding>
<Insets bottom="6" top="6"/>
</padding>
</Label>
<Label text="%hub.untrustedHost.description" wrapText="true"/>
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
<buttons>
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" onAction="#close"/>
</buttons>
</ButtonBar>
</VBox>
</children>
</HBox>
@@ -22,7 +22,7 @@
</ImageView>
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
<Label text="© 2016 2026 Skymatic GmbH"/>
<Label text="© 2016 2025 Skymatic GmbH"/>
</VBox>
</HBox>
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Hyperlink?>
@@ -35,8 +34,6 @@
<CheckBox fx:id="useQuickAccessCheckbox" text="%preferences.general.quickAccessService"/>
<ChoiceBox fx:id="quickAccessServiceChoiceBox" accessibleText="%preferences.general.quickAccessService"/>
</HBox>
<Button fx:id="resetTrustedHostsButton" text="%preferences.general.resetTrustedHosts" visible="${controller.hubTrustOnFirstUseEnabled}" managed="${controller.hubTrustOnFirstUseEnabled}" onAction="#resetTrustedHosts"/>
<Region VBox.vgrow="ALWAYS"/>
<HBox spacing="12" alignment="CENTER_LEFT">
@@ -9,94 +9,90 @@
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<ScrollPane xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
fitToWidth="true"
hbarPolicy="NEVER">
<VBox spacing="6">
<fx:define>
<ToggleGroup fx:id="mountPointToggleGroup"/>
</fx:define>
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<HBox spacing="12" alignment="CENTER_LEFT">
<Label text="%vaultOptions.mount.volume.type" labelFor="$vaultVolumeTypeChoiceBox"/>
<ChoiceBox fx:id="vaultVolumeTypeChoiceBox"/>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}" accessibleText="%vaultOptions.mount.info">
<graphic>
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
</graphic>
<tooltip>
<Tooltip text="%vaultOptions.mount.info" showDelay="100ms"/>
</tooltip>
</Hyperlink>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" visible="${!controller.defaultMountServiceSelected}" managed="${!controller.defaultMountServiceSelected}" accessibleText="%preferences.volume.docsTooltip">
<graphic>
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
</graphic>
<tooltip>
<Tooltip text="%preferences.volume.docsTooltip" showDelay="100ms"/>
</tooltip>
</Hyperlink>
</HBox>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
spacing="6">
<fx:define>
<ToggleGroup fx:id="mountPointToggleGroup"/>
</fx:define>
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<children>
<HBox spacing="12" alignment="CENTER_LEFT">
<Label text="%vaultOptions.mount.volume.type" labelFor="$vaultVolumeTypeChoiceBox"/>
<ChoiceBox fx:id="vaultVolumeTypeChoiceBox"/>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}" accessibleText="%vaultOptions.mount.info">
<graphic>
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
</graphic>
<tooltip>
<Tooltip text="%vaultOptions.mount.info" showDelay="100ms"/>
</tooltip>
</Hyperlink>
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" visible="${!controller.defaultMountServiceSelected}" managed="${!controller.defaultMountServiceSelected}" accessibleText="%preferences.volume.docsTooltip">
<graphic>
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
</graphic>
<tooltip>
<Tooltip text="%preferences.volume.docsTooltip" showDelay="100ms"/>
</tooltip>
</Hyperlink>
</HBox>
<Label styleClass="label-red" text="%vaultOptions.mount.volumeType.restartRequired" visible="${controller.selectedMountServiceRequiresRestart}" managed="${controller.selectedMountServiceRequiresRestart}"/>
<Label styleClass="label-red" text="%vaultOptions.mount.volumeType.restartRequired" visible="${controller.selectedMountServiceRequiresRestart}" managed="${controller.selectedMountServiceRequiresRestart}"/>
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortChangeable}" managed="${controller.loopbackPortChangeable}">
<Label text="%vaultOptions.mount.volume.tcp.port" labelFor="$vaultLoopbackPortField"/>
<NumericTextField fx:id="vaultLoopbackPortField"/>
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
</HBox>
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortChangeable}" managed="${controller.loopbackPortChangeable}">
<Label text="%vaultOptions.mount.volume.tcp.port" labelFor="$vaultLoopbackPortField"/>
<NumericTextField fx:id="vaultLoopbackPortField"/>
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
</HBox>
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlyOptionAllowed}" managed="${controller.readOnlyOptionAllowed}"/>
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlyOptionAllowed}" managed="${controller.readOnlyOptionAllowed}"/>
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/>
<TextField fx:id="mountFlagsField" HBox.hgrow="ALWAYS" maxWidth="Infinity" accessibleText="%vaultOptions.mount.customMountFlags">
<VBox.margin>
<Insets left="24"/>
</VBox.margin>
</TextField>
</VBox>
<Label text="%vaultOptions.mount.mountPoint">
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/>
<TextField fx:id="mountFlagsField" HBox.hgrow="ALWAYS" maxWidth="Infinity" accessibleText="%vaultOptions.mount.customMountFlags">
<VBox.margin>
<Insets top="9"/>
<Insets left="24"/>
</VBox.margin>
</Label>
</TextField>
</VBox>
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointAutoBtn" text="%vaultOptions.mount.mountPoint.auto"/>
<Label text="%vaultOptions.mount.mountPoint">
<VBox.margin>
<Insets top="9"/>
</VBox.margin>
</Label>
<HBox spacing="6" visible="${controller.mountpointDriveLetterSupported}" managed="${controller.mountpointDriveLetterSupported}">
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointDriveLetterBtn" text="%vaultOptions.mount.mountPoint.driveLetter"/>
<ChoiceBox fx:id="driveLetterSelection" disable="${!mountPointDriveLetterBtn.selected}" accessibleText="%vaultOptions.mount.mountPoint.driveLetter"/>
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointAutoBtn" text="%vaultOptions.mount.mountPoint.auto"/>
<HBox spacing="6" visible="${controller.mountpointDriveLetterSupported}" managed="${controller.mountpointDriveLetterSupported}">
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointDriveLetterBtn" text="%vaultOptions.mount.mountPoint.driveLetter"/>
<ChoiceBox fx:id="driveLetterSelection" disable="${!mountPointDriveLetterBtn.selected}" accessibleText="%vaultOptions.mount.mountPoint.driveLetter"/>
</HBox>
<VBox spacing="6" visible="${controller.mountpointDirSupported}" managed="${controller.mountpointDirSupported}">
<HBox spacing="6" alignment="CENTER_LEFT">
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointDirBtn" text="%vaultOptions.mount.mountPoint.custom"/>
<Button text="%vaultOptions.mount.mountPoint.directoryPickerButton" onAction="#chooseCustomMountPoint" contentDisplay="LEFT" disable="${!mountPointDirBtn.selected}">
<graphic>
<FontAwesome5IconView glyph="FOLDER_OPEN" glyphSize="15"/>
</graphic>
</Button>
</HBox>
<TextField fx:id="directoryPathField" text="${controller.directoryPath}" visible="${mountPointDirBtn.selected}" managed="${mountPointDirBtn.managed}" maxWidth="Infinity" editable="false" accessibleText="%vaultOptions.mount.mountPoint.custom">
<VBox.margin>
<Insets left="24"/>
</VBox.margin>
</TextField>
</VBox>
</children>
<VBox spacing="6" visible="${controller.mountpointDirSupported}" managed="${controller.mountpointDirSupported}">
<HBox spacing="6" alignment="CENTER_LEFT">
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointDirBtn" text="%vaultOptions.mount.mountPoint.custom"/>
<Button text="%vaultOptions.mount.mountPoint.directoryPickerButton" onAction="#chooseCustomMountPoint" contentDisplay="LEFT" disable="${!mountPointDirBtn.selected}">
<graphic>
<FontAwesome5IconView glyph="FOLDER_OPEN" glyphSize="15"/>
</graphic>
</Button>
</HBox>
<TextField fx:id="directoryPathField" text="${controller.directoryPath}" visible="${mountPointDirBtn.selected}" managed="${mountPointDirBtn.managed}" maxWidth="Infinity" editable="false" accessibleText="%vaultOptions.mount.mountPoint.custom">
<VBox.margin>
<Insets left="24"/>
</VBox.margin>
</TextField>
</VBox>
</children>
</VBox>
</ScrollPane>
</VBox>
+1 -18
View File
@@ -110,13 +110,6 @@ addvaultwizard.existing.restore=Restore…
addvaultwizard.existing.chooseBtn=Choose…
addvaultwizard.existing.filePickerTitle=Select Vault File
addvaultwizard.existing.filePickerMimeDesc=Cryptomator Vault
addvaultwizard.existing.notAVault.title=Not a Vault
addvaultwizard.existing.notAVault.message=The selected folder is not a Cryptomator vault
addvaultwizard.existing.notAVault.description.missingDataDir=The required "d" subdirectory is missing inside "%s".
addvaultwizard.existing.notAVault.description.dataNotADirectory=The "d" entry inside "%s" is not a directory.
addvaultwizard.existing.notAVault.description.missingVaultConfig=The required "vault.cryptomator" file is missing inside "%s".
addvaultwizard.existing.notAVault.description.vaultConfigAccessDenied=File "vault.cryptomator" inside "%s" cannot be read due to insufficient access rights.
addvaultwizard.existing.notAVault.description.unsupportedStructure=The directory structure of "%s" is not supported.
## Success
addvaultwizard.success.nextStepsInstructions=Added vault "%s".\nYou need to unlock this vault to access or add contents. Alternatively you can unlock it at any later point in time.
addvaultwizard.success.unlockNow=Unlock Now
@@ -169,12 +162,6 @@ unlock.error.title=Unlock "%s" failed
hub.noKeychain.message=Unable to access device key
hub.noKeychain.description=In order to unlock Hub vaults, a device key is required, which is secured using a keychain. To proceed, enable “%s” and select a keychain in the preferences.
hub.noKeychain.openBtn=Open Preferences
### Check Host Authenticity
hub.checkHostTrust.message.check=Checking Configuration…
hub.checkHostTrust.message.ask=Trust this host?
hub.checkHostTrust.message.ask.plural=Trust these hosts?
hub.checkHostTrust.trustBtn=Trust
hub.checkHostTrust.denyBtn=Deny
### Waiting
hub.auth.message=Waiting for authentication…
hub.auth.description=You should automatically be redirected to the login page.
@@ -206,9 +193,6 @@ hub.archived.description=This vault has been archived and is no longer accessibl
### Unauthorized
hub.unauthorized.message=Access denied
hub.unauthorized.description=You are not authorized to open this vault. Contact the vault's owner to request access.
### Untrusted Host
hub.untrustedHost.message=Host not trusted
hub.untrustedHost.description=Connection to Hub was blocked for your security. If you believe the Hub host is safe, contact your Hub administrator or try again.
### Requires Account Initialization
hub.requireAccountInit.message=Action required
hub.requireAccountInit.description.0=To proceed, please complete the steps required in your
@@ -322,7 +306,6 @@ preferences.general.debugDirectory=Reveal log files
preferences.general.autoStart=Launch Cryptomator on system start
preferences.general.keychainBackend=Store passwords with
preferences.general.quickAccessService=Add unlocked vaults to the quick access area
preferences.general.resetTrustedHosts=Reset trusted hosts
## Interface
preferences.interface=Interface
preferences.interface.theme=Look & Feel
@@ -734,4 +717,4 @@ eventView.entry.inUse.ignoreLock=Ignore use status
## FileIsInUse Notification
notification.inUse.message=File is in use on another device
notification.inUse.description=The file is open by %s on %s. Ask them to close the file and let synchronization finish. You can ignore the status to open it now, but this may cause conflicts or overwrite newer changes.
notification.inUse.action=Ignore Use Status
notification.inUse.action=Ignore Use Status
@@ -32,7 +32,6 @@
## Success
## Failure
## Hub
### Check Host Authenticity
### Waiting
### Receive Key
### Register Device
@@ -41,7 +40,6 @@
### Registration Failed
### Archived
### Unauthorized
### Untrusted Host
### Requires Account Initialization
### License Exceeded
@@ -156,4 +154,4 @@
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
@@ -154,8 +154,6 @@ unlock.error.title=فشل فتح "%s"
hub.noKeychain.message=غير قادر على الوصول إلى مفتاح الجهاز
hub.noKeychain.description=من أجل فتح مركز مخازن، مطلوب مفتاح جهاز، يتم تأمينه باستخدام سلسلة مفاتيح. للمضي قدما، قم بتمكين "%s" واختر سلسلة مفاتيح في التفضيلات.
hub.noKeychain.openBtn=فتح التفضيلات
### Check Host Authenticity
hub.checkHostTrust.trustBtn=ثق
### Waiting
hub.auth.message=جاري الانتظار للمصادقة…
hub.auth.description=يجب أن يتم إعادة توجيهك تلقائياً إلى صفحة تسجيل الدخول.
@@ -185,7 +183,6 @@ hub.registerFailed.description.deviceAlreadyExists=هذا الجهاز مسجل
### Unauthorized
hub.unauthorized.message=تم رفض الوصول
hub.unauthorized.description=غير مسموح لك بفتح هذا المستودع. اتصل بمالك المستودع لطلب الوصول.
### Untrusted Host
### Requires Account Initialization
hub.requireAccountInit.message=مطلوب اتخاذ إجراء
hub.requireAccountInit.description.0=للمتابعة، يرجى إكمال الخطوات المطلوبة في
@@ -648,4 +645,4 @@ eventView.entry.inUse.showEncrypted=عرض ملَف المشفر
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
@@ -151,8 +151,6 @@ unlock.error.title="%s" биге асылманы
hub.noKeychain.message=Йыһаз асҡысына инеү рөхсәте юҡ
hub.noKeychain.description=Хаб һаҡлағыстарын асыу өсөн йыһаз асҡысы кәрәк, ул иһә асҡыс сылбырында һаҡлана. Артабан “%s” мөмкинлеге бирегеҙ һәм көйләүҙәрҙә асҡыс сылбырын һайлағыҙ.
hub.noKeychain.openBtn=Көйләүҙәрҙе ас
### Check Host Authenticity
hub.checkHostTrust.trustBtn=Ышан
### Waiting
hub.auth.message=Аутентиклау көтөлә…
hub.auth.description=Автоматик рәүештә инеү битенә йүнәлтелергә тейешһегеҙ.
@@ -172,7 +170,6 @@ hub.registerSuccess.unlockBtn=Биген ас
### Archived
### Unauthorized
hub.unauthorized.message=Инеү кире ҡағылды
### Untrusted Host
### Requires Account Initialization
hub.requireAccountInit.message=Эш-хәрәкәт кәрәкле
hub.requireAccountInit.description.0=Дауам итер өсөн, кәрәкле аҙымдар тамамланырға тейеш урын:
@@ -558,4 +555,4 @@ dokanySupportEnd.preferencesBtn=Көйләүҙәрҙе ас
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
@@ -145,7 +145,6 @@ unlock.error.customPath.description.inUse=Дыскавая літара або
hub.noKeychain.message=Няма доступу да ключа прылады
hub.noKeychain.description=Каб разамкнуць скарбніцы Hub, патрэбны ключ прылады, які захаваны ў звязку ключоў. Каб працягнуць, уключы "%s" ды абяры звязак ключоў у наладах.
hub.noKeychain.openBtn=Адчыніць налады
### Check Host Authenticity
### Waiting
hub.auth.message=Чаканне спраўджання…
hub.auth.description=Ты мусіш аўтаматычна перанакіравацца на старонку ўваходу.
@@ -163,7 +162,6 @@ hub.registerSuccess.unlockBtn=Адамкнуць
### Archived
### Unauthorized
hub.unauthorized.message=Адмова ў доступе
### Untrusted Host
### Requires Account Initialization
### License Exceeded
hub.invalidLicense.message=Несапраўдная ліцэнзія Hub
@@ -534,4 +532,4 @@ dokanySupportEnd.preferencesBtn=Адчыніць налады
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
@@ -158,7 +158,6 @@ unlock.error.title=Неуспешно отключване на „%s“
hub.noKeychain.message=Няма достъп до ключа на устройството
hub.noKeychain.description=За да отключите хранилищата в Hub е необходим ключ за устройството, който се защитава с помощта на ключодържател. За да продължите, разрешете „%s“ и изберете ключодържателя в настройките.
hub.noKeychain.openBtn=Към настройките
### Check Host Authenticity
### Waiting
hub.auth.message=Изчакване на удостоверяване…
hub.auth.description=Автоматично ще бъдете пренасочени към страницата за вход.
@@ -178,7 +177,6 @@ hub.registerSuccess.unlockBtn=Отключване
### Archived
### Unauthorized
hub.unauthorized.message=Отказан достъп
### Untrusted Host
### Requires Account Initialization
hub.requireAccountInit.message=Необходимо е действие
hub.requireAccountInit.description.0=За да продължите завършете необходимите стъпки в
@@ -565,4 +563,4 @@ dokanySupportEnd.preferencesBtn=Към настройките
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
@@ -92,7 +92,6 @@ unlock.unlockBtn=আনলক করুন
## Success
## Failure
## Hub
### Check Host Authenticity
### Waiting
### Receive Key
### Register Device
@@ -102,7 +101,6 @@ hub.registerSuccess.unlockBtn=আনলক করুন
### Registration Failed
### Archived
### Unauthorized
### Untrusted Host
### Requires Account Initialization
### License Exceeded
@@ -223,4 +221,4 @@ vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন ক
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
@@ -104,8 +104,6 @@ unlock.success.rememberChoice=Zapamtite izbor, ne pokazujte ovo ponovo
unlock.success.revealBtn=Otkrij pogon
## Failure
## Hub
### Check Host Authenticity
hub.checkHostTrust.trustBtn=Povjerenje
### Waiting
### Receive Key
### Register Device
@@ -115,7 +113,6 @@ hub.registerSuccess.unlockBtn=Otključaj
### Registration Failed
### Archived
### Unauthorized
### Untrusted Host
### Requires Account Initialization
### License Exceeded
@@ -359,4 +356,4 @@ quit.lockAndQuitBtn=Zaključaj i zatvori
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
@@ -155,8 +155,6 @@ unlock.error.title=El desblocatge de "%s" ha fallat
hub.noKeychain.message=No es pot accedir a la clau del dispositiu
hub.noKeychain.description=Per poder desblocar caixes fortes del Hub es requereix la clau d'un dispositiu, que s'emmagatzema de forma segura en un clauer. Per continuar, habiliti "%s" i seleccioni un clauer en les Preferències.
hub.noKeychain.openBtn=Obrir les Preferències
### Check Host Authenticity
hub.checkHostTrust.trustBtn=Confia-hi
### Waiting
hub.auth.message=Sestà esperant lautenticació…
hub.auth.description=Hauríeu de ser redirigits a la pàgina d'accés.
@@ -186,7 +184,6 @@ hub.registerFailed.description.deviceAlreadyExists=El dispositiu ja ha estat reg
### Unauthorized
hub.unauthorized.message=Accés denegat
hub.unauthorized.description=No estàs autoritzat a obrir aquesta caixa forta. Contacta amb el seu propietari per obtenir accés.
### Untrusted Host
### Requires Account Initialization
hub.requireAccountInit.message=Acció necessària
hub.requireAccountInit.description.0=Per a continuar, si us plau, seguiu els passos necessaris en el vostre
@@ -614,4 +611,4 @@ shareVault.hub.openHub=Obre Cryptomator Hub
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
@@ -150,8 +150,6 @@ unlock.error.title=Odemknutí "%s" selhalo
hub.noKeychain.message=Nelze získat přístup ke klíči zařízení
hub.noKeychain.description=Chcete-li odemknout Hub trezory, je potřeba použít klíč zařízení, který je zabezpečen pomocí klíčenky. Chcete-li pokračovat, povolte „%s“ a vyberte klíčenku v nastavení.
hub.noKeychain.openBtn=Otevřít předvolby
### Check Host Authenticity
hub.checkHostTrust.trustBtn=Důvěřovat
### Waiting
hub.auth.message=Čekání na ověření…
hub.auth.description=Měli byste být automaticky přesměrováni na přihlašovací stránku.
@@ -180,7 +178,6 @@ hub.registerFailed.description.deviceAlreadyExists=Toto zařízení je již regi
### Archived
### Unauthorized
hub.unauthorized.message=Přístup odepřen
### Untrusted Host
### Requires Account Initialization
hub.requireAccountInit.message=Požadována akce
hub.requireAccountInit.description.0=Chcete-li pokračovat, vyplňte prosím požadované kroky ve vašem
@@ -575,4 +572,4 @@ shareVault.hub.openHub=Otevřít Cryptomator Hub
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
+1 -33
View File
@@ -109,13 +109,6 @@ addvaultwizard.existing.restore=Gendan…
addvaultwizard.existing.chooseBtn=Vælg…
addvaultwizard.existing.filePickerTitle=Vælg boks-fil
addvaultwizard.existing.filePickerMimeDesc=Cryptomator boks
addvaultwizard.existing.notAVault.title=Ikke en boks
addvaultwizard.existing.notAVault.message=Den valgte mappe skal være en gyldig Cryptomator boks
addvaultwizard.existing.notAVault.description.missingDataDir=Den krævede "d" undermappe mangler inde i "%s".
addvaultwizard.existing.notAVault.description.dataNotADirectory=Indgangen "d" inde i "%s" er ikke en mappe.
addvaultwizard.existing.notAVault.description.missingVaultConfig=Den påkrævede "vault.cryptomator"-fil mangler inde i "%s".
addvaultwizard.existing.notAVault.description.vaultConfigAccessDenied=Filen "vault.cryptomator" inde i "%s" kan ikke læses på grund af utilstrækkelige adgangsrettigheder.
addvaultwizard.existing.notAVault.description.unsupportedStructure=Mappestrukturen for "%s" understøttes ikke.
## Success
addvaultwizard.success.nextStepsInstructions=Tilføjede boks "%s".\nDu skal låse op for denne boks for at tilgå indholdet. Alternativt kan du låse den op på et senere tidspunkt.
addvaultwizard.success.unlockNow=Lås op nu
@@ -168,12 +161,6 @@ unlock.error.title=Oplåsning af "%s" mislykkedes
hub.noKeychain.message=Kan ikke tilgå enhedsnøgle
hub.noKeychain.description=En enhedsnøgle er påkrævet for at låse Hub bokse op. Enhedsnøglen er sikret i en nøglering. For at fortsætte, aktivér “%s” og vælg en nøglering i indstillingerne.
hub.noKeychain.openBtn=Åbn Indstillinger
### Check Host Authenticity
hub.checkHostTrust.message.check=Kontrollerer konfiguration…
hub.checkHostTrust.message.ask=Stol på denne vært?
hub.checkHostTrust.message.ask.plural=Stol på disse værter?
hub.checkHostTrust.trustBtn=Hav tillid
hub.checkHostTrust.denyBtn=Afvis
### Waiting
hub.auth.message=Afventer godkendelse…
hub.auth.description=Du burde automatisk blive omdirigeret til login-siden.
@@ -205,9 +192,6 @@ hub.archived.description=Denne boks er blevet arkiveret og er ikke længere tilg
### Unauthorized
hub.unauthorized.message=Adgang nægtet
hub.unauthorized.description=Du har ikke tilladelse til at åbne denne boks. Kontant ejeren af boksen for at anmode om adgang.
### Untrusted Host
hub.untrustedHost.message=Vært Ikke pålidelig
hub.untrustedHost.description=Forbindelsen til Hub blev blokeret for din sikkerhed. Hvis du mener, at Hub-værten er sikker, så kontakt din Hub-administrator eller prøv igen.
### Requires Account Initialization
hub.requireAccountInit.message=Handling påkrævet
hub.requireAccountInit.description.0=For at fortsætte, skal du fuldføre de nødvendige trin i din
@@ -285,8 +269,6 @@ health.check.detail.checkFinishedAndFound=Kontrolproceduren er kørt færdig. Ge
health.check.detail.checkFailed=Kontrolproceduren blev afbrudt af en fejl.
health.check.detail.checkCancelled=Kontrolproceduren blev annulleret.
health.check.detail.listFilters.label=Filter
health.check.detail.filterSeverity=Filtrér efter sværhedsgrad
health.check.detail.filterFixState=Filtrér efter fix status
health.check.detail.fixAllSpecificBtn=Løs alle af type
health.check.exportBtn=Eksportér rapport
## Result view
@@ -321,7 +303,6 @@ preferences.general.debugDirectory=Vis logfiler
preferences.general.autoStart=Start Cryptomator automatisk ved opstart
preferences.general.keychainBackend=Gem adgangskoder med
preferences.general.quickAccessService=Tilføj oplåste bokse til området hurtig adgang
preferences.general.resetTrustedHosts=Nulstil betroede værter
## Interface
preferences.interface=Brugerflade
preferences.interface.theme=Udseende
@@ -377,7 +358,6 @@ preferences.contribute.promptText=Indsæt koden for supporter-certifikatet her
preferences.contribute.thankYou=Tak fordi du støtter Cryptomators open source-udvikling!
preferences.contribute.donate=Donér
preferences.contribute.sponsor=Sponsor
preferences.contribute.removeCert.tooltip=Fjern certifikat
### Remove License Key Dialog
removeCert.title=Fjern certifikat
@@ -387,7 +367,6 @@ removeCert.description=Cryptomators kernefunktioner påvirkes ikke af dette. Hve
## About
preferences.about=Om
preferences.about.thirdPartyLicenses=Tredjepartslicenser
# Vault Statistics
stats.title=Statistik for %s
@@ -427,7 +406,6 @@ stats.access.total=Samlede adgang: %d
# Main Window
## Vault List
main.vaultlist=Bokse
main.vaultlist.listEntry=Boks %s (%s)
main.vaultlist.emptyList.onboardingInstruction=Klik her for at tilføje en boks
main.vaultlist.contextMenu.remove=Fjern…
main.vaultlist.contextMenu.lock=Lås
@@ -441,15 +419,12 @@ main.vaultlist.addVaultBtn.menuItemExisting=Åbn eksisterende boks…
main.vaultlist.addVaultBtn.menuItemRecover=Genopret eksisterende boks…
main.vaultlist.addVaultButton.tooltip=Tilføj boks
main.vaultlist.showEventsButton.tooltip=Åbn begivenhedsvisning
main.vaultlist.showPreferencesButton.tooltip=Vis Indstillinger
##Notification
main.notification.updateAvailable=Opdatering er tilgængelig.
main.notification.support=Støt Cryptomator.
main.notification.closeButton.tooltip=Luk infobjælke
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Tak fordi du valgte Cryptomator til at beskytte dine filer. Hvis du har brug for hjælp, så tjek vores guider for at komme i gang:
main.vaultDetail.storageLocation=Placering af boks
### Locked
main.vaultDetail.lockedStatus=LÅST
main.vaultDetail.unlockBtn=Lås op…
@@ -507,7 +482,6 @@ vaultOptions.general=Generelt
vaultOptions.general.vaultName=Boks-navn
vaultOptions.general.autoLock.lockAfterTimePart1=Lås efter inaktivitet i
vaultOptions.general.autoLock.lockAfterTimePart2=minutter
vaultOptions.general.autoLock.accessibleText=Lås timeout i minutter
vaultOptions.general.unlockAfterStartup=Lås boksen op når Cryptomator starter
vaultOptions.general.actionAfterUnlock=Efter oplåsning af boks
vaultOptions.general.actionAfterUnlock.ignore=Gør intet
@@ -538,7 +512,6 @@ vaultOptions.masterkey.forgetSavedPasswordBtn=Glem gemt adgangskode
vaultOptions.masterkey.recoveryKeyExplanation=En gendannelsesnøgle er den eneste måde du kan få adgang til din boks på, hvis du har glemt dit password.
vaultOptions.masterkey.showRecoveryKeyBtn=Vis gendannelsesnøgle
vaultOptions.masterkey.recoverPasswordBtn=Nulstil adgangskode
vaultOptions.masterkey.missingMasterkeyFile=Disse tilvalg er kun tilgængelige hvis masterkeyfilen er til stede i boksmappen.
## Hub
vaultOptions.hub=Gendannelse
vaultOptions.hub.convertInfo=Du kan bruge gendannelsesnøglen til at konvertere denne Hub-boks til en adgangskode-baseret boks i en nødsituation.
@@ -692,8 +665,6 @@ decryptNames.filePicker.title=Vælg krypteret fil
decryptNames.filePicker.extensionDescription=Cryptomator krypteret fil
decryptNames.copyTable.tooltip=Kopiér tabel
decryptNames.clearTable.tooltip=Ryd tabel
decryptNames.column.encrypted=Krypteret
decryptNames.column.decrypted=Dekrypteret
decryptNames.copyHint=Kopiér celleindhold med %s
decryptNames.dropZone.message=Slip filer eller klik for at vælge
decryptNames.dropZone.error.vaultInternalFiles=Boks interne filer med intet dekryptérbart navn valgt
@@ -706,8 +677,6 @@ decryptNames.dropZone.error.generic=Kunne ikke dekryptere filnavne
eventView.title=Begivenheder
eventView.filter.allVaults=Alle
eventView.clearListButton.tooltip=Ryd liste
eventView.filterVaults=Filtrér efter boks
eventView.cell.actionsButton.tooltip=Begivenhedshandlinger
## event list entries
eventView.entry.vaultLocked.description=Lås "%s" op for detaljer
eventView.entry.conflictResolved.message=Løst konflikt
@@ -725,7 +694,6 @@ eventView.entry.brokenFileNode.copyDecrypted=Kopiér dekrypteret sti
eventView.entry.inUse.message=Fil i brug
eventView.entry.inUse.showDecrypted=Vis dekrypteret fil
eventView.entry.inUse.showEncrypted=Vis krypteret fil
eventView.entry.inUse.copyUserAndDevice=Kopiér låsebruger og enhedsnavn
eventView.entry.inUse.ignoreLock=Ignorér anvendelsesstatus
@@ -733,4 +701,4 @@ eventView.entry.inUse.ignoreLock=Ignorér anvendelsesstatus
## FileIsInUse Notification
notification.inUse.message=Filen er i brug på en anden enhed
notification.inUse.description=Filen er åbnet af %s på %s. Bed dem om at lukke filen og lade synkroniseringen afslutte. Du kan ignorere status for at åbne den nu, men det kan forårsage konflikter eller overskrive nyere ændringer.
notification.inUse.action=Ignorér anvendelsesstatus
notification.inUse.action=Ignorér anvendelsesstatus
@@ -104,7 +104,6 @@ addvaultwizard.existing.restore=Wiederherstellen…
addvaultwizard.existing.chooseBtn=Durchsuchen …
addvaultwizard.existing.filePickerTitle=Tresordatei auswählen
addvaultwizard.existing.filePickerMimeDesc=Cryptomator-Tresor
addvaultwizard.existing.notAVault.description.unsupportedStructure=Die Verzeichnisstruktur von "%s" wird nicht unterstützt.
## Success
addvaultwizard.success.nextStepsInstructions=Tresor „%s“ wurde hinzugefügt.\nUm auf Inhalte zuzugreifen oder welche hinzuzufügen, musst du den Tresor entsperren. Du kannst ihn aber auch zu jedem späteren Zeitpunkt entsperren.
addvaultwizard.success.unlockNow=Jetzt entsperren
@@ -157,8 +156,6 @@ unlock.error.title=„%s“ konnte nicht entsperrt werden
hub.noKeychain.message=Zugriff auf Geräteschlüssel nicht möglich
hub.noKeychain.description=Zum Entsperren von Hub-Tresoren wird ein Geräteschlüssel benötigt, der in einem Schlüsselbund gesichert ist. Um fortzufahren, aktiviere „%s“ und wähle in den Einstellungen einen Schlüsselbund.
hub.noKeychain.openBtn=Einstellungen öffnen
### Check Host Authenticity
hub.checkHostTrust.trustBtn=Vertrauen
### Waiting
hub.auth.message=Warten auf Authentifizierung …
hub.auth.description=Du solltest automatisch zur Anmeldeseite weitergeleitet werden.
@@ -190,7 +187,6 @@ hub.archived.description=Dieser Tresor wurde archiviert, daher ist ein Zugriff d
### Unauthorized
hub.unauthorized.message=Zugriff verweigert
hub.unauthorized.description=Du bist nicht berechtigt, diesen Tresor zu öffnen. Wende dich an den Tresoreigentümer, um Zugriff zu erhalten.
### Untrusted Host
### Requires Account Initialization
hub.requireAccountInit.message=Aktion erforderlich
hub.requireAccountInit.description.0=Um fortzufahren, führe bitte die erforderlichen Schritte in deinem
@@ -302,7 +298,6 @@ preferences.general.debugDirectory=Protokolldateien anzeigen
preferences.general.autoStart=Cryptomator beim Systemstart starten
preferences.general.keychainBackend=Passwörter speichern mit
preferences.general.quickAccessService=Entsperrte Tresore zum Schnellzugriff hinzufügen
preferences.general.resetTrustedHosts=Vertrauenswürdige Hosts zurücksetzen
## Interface
preferences.interface=Benutzeroberfläche
preferences.interface.theme=Erscheinungsbild
@@ -706,4 +701,4 @@ eventView.entry.inUse.copyUserAndDevice=Benutzer- und Gerätenamen der Sperre ko
# Notifications
## FileIsInUse Notification
notification.inUse.action=Verwendungsstatus ignorieren
notification.inUse.action=Verwendungsstatus ignorieren
@@ -154,8 +154,6 @@ unlock.error.title=Ξεκλείδωμα "%s" απέτυχε
hub.noKeychain.message=Δεν είναι δυνατή η πρόσβαση στο κλειδί της συσκευής
hub.noKeychain.description=Για να ξεκλειδώσετε τις κρύπτες Hub, απαιτείται ένα κλειδί συσκευής, το οποίο ασφαλίζεται με χρήση μπρελόκ. Για να συνεχίσετε, ενεργοποιήστε το "%s" και επιλέξτε ένα keychain στις προτιμήσεις.
hub.noKeychain.openBtn=Άνοιγμα Προτιμήσεων
### Check Host Authenticity
hub.checkHostTrust.trustBtn=Εμπιστευτείτε
### Waiting
hub.auth.message=Αναμονή για επαλήθευση…
hub.auth.description=Θα πρέπει να ανακατευθυνθείτε αυτόματα στη σελίδα σύνδεσης.
@@ -185,7 +183,6 @@ hub.registerFailed.description.deviceAlreadyExists=Αυτή η συσκευή ε
### Unauthorized
hub.unauthorized.message=Δεν επιτρέπεται η πρόσβαση
hub.unauthorized.description=Δεν είστε εξουσιοδοτημένοι να ανοίξετε αυτή την κρύπτη. Επικοινωνήστε με τον ιδιοκτήτη τς κρύπτης για να ζητήσετε πρόσβαση.
### Untrusted Host
### Requires Account Initialization
hub.requireAccountInit.message=Απαιτείται ενέργεια
hub.requireAccountInit.description.0=Για να συνεχίσετε, παρακαλούμε ολοκληρώστε τα βήματα που απαιτούνται στο δικό σας
@@ -648,4 +645,4 @@ eventView.entry.inUse.showEncrypted=Εμφάνιση κρυπτογραφημέ
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
+1 -30
View File
@@ -18,11 +18,6 @@ generic.button.print=Imprimir
generic.button.remove=Eliminar
## Vault state
vault.state.locked=Bloqueada
vault.state.unlocked=Desbloqueada
vault.state.missing=Faltante
vault.state.migrationNeeded=Migración obligatoria
vault.state.processing=Procesando
vault.state.error=Error
# Error
@@ -109,13 +104,6 @@ addvaultwizard.existing.restore=…
addvaultwizard.existing.chooseBtn=Elegir…
addvaultwizard.existing.filePickerTitle=Seleccionar archivo de bóveda
addvaultwizard.existing.filePickerMimeDesc=Bóveda de Cryptomator
addvaultwizard.existing.notAVault.title=No es una bóveda
addvaultwizard.existing.notAVault.message=La carpeta seleccionada no es una bóveda de Cryptomator
addvaultwizard.existing.notAVault.description.missingDataDir=Falta el subdirectorio "d" necesario dentro de "%s".
addvaultwizard.existing.notAVault.description.dataNotADirectory=La entrada "d" dentro de "%s" no es un directorio.
addvaultwizard.existing.notAVault.description.missingVaultConfig=Falta el archivo "vault.cryptomator" necesario dentro de "%s".
addvaultwizard.existing.notAVault.description.vaultConfigAccessDenied=El archivo "vault.cryptomator" dentro de "%s" no se puede leer debido a la falta de permisos de acceso.
addvaultwizard.existing.notAVault.description.unsupportedStructure=La estructura de directorios de "%s" no está soportada.
## Success
addvaultwizard.success.nextStepsInstructions=Bóveda "%s" añadida.\nSe necesita desbloquear esta bóveda para acceder o añadir contenido. Alternativamente se \n puede desbloquear en otro momento.
addvaultwizard.success.unlockNow=Desbloquear ahora
@@ -168,12 +156,6 @@ unlock.error.title=Error al desbloquear "%s"
hub.noKeychain.message=No se puede acceder a la clave del dispositivo
hub.noKeychain.description=Para desbloquear las bóvedas de Hub, se requiere una clave de dispositivo que se asegura con un llavero. Para continuar, habilite "%s" y seleccione un llavero en las preferencias.
hub.noKeychain.openBtn=Abrir preferencias
### Check Host Authenticity
hub.checkHostTrust.message.check=Comprobando configuración…
hub.checkHostTrust.message.ask=¿Confiar en este host?
hub.checkHostTrust.message.ask.plural=¿Confiar en estos hosts?
hub.checkHostTrust.trustBtn=Confiar
hub.checkHostTrust.denyBtn=Denegar
### Waiting
hub.auth.message=Esperando la autenticación…
hub.auth.description=Debería ser redirigido automáticamente a la página de inicio de sesión.
@@ -200,14 +182,9 @@ hub.registerFailed.message=El registro del dispositivo ha fallado
hub.registerFailed.description.generic=Ha ocurrido un error al intentar registrarse. Para más detalles, consulte el registro de la aplicación.
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo ya se ha registrado para otro usuario. Intente cambiar la cuenta de usuario o utilice un dispositivo diferente.
### Archived
hub.archived.message=Bóveda archivada
hub.archived.description=Esta bóveda ha sido archivada y ya no es accesible. Póngase en contacto con el propietario de la bóveda.
### Unauthorized
hub.unauthorized.message=Acceso denegado
hub.unauthorized.description=No tiene autorización para abrir esta bóveda. Contacta al propietario de la bóveda para solicitar acceso.
### Untrusted Host
hub.untrustedHost.message=Host no confiable
hub.untrustedHost.description=La conexión al Hub fue bloqueada por su seguridad. Si cree que el Hub es seguro, póngase en contacto con la administración de Hub o vuelva a intentarlo.
### Requires Account Initialization
hub.requireAccountInit.message=Acción requerida
hub.requireAccountInit.description.0=Para continuar, por favor complete los pasos necesarios en su
@@ -321,7 +298,6 @@ preferences.general.debugDirectory=Revelar archivos de registro
preferences.general.autoStart=Cargar Cryptomator al iniciar el sistema
preferences.general.keychainBackend=Guardar contraseñas con
preferences.general.quickAccessService=Añadir bóvedas desbloqueadas al área de acceso rápido
preferences.general.resetTrustedHosts=Restablecer hosts de confianza
## Interface
preferences.interface=Interfaz
preferences.interface.theme=Apariencia
@@ -427,7 +403,6 @@ stats.access.total=Accesos totales: %d
# Main Window
## Vault List
main.vaultlist=Bóvedas
main.vaultlist.listEntry=Bóveda %s (%s)
main.vaultlist.emptyList.onboardingInstruction=Haga clic aquí para añadir una bóveda
main.vaultlist.contextMenu.remove=Eliminar…
main.vaultlist.contextMenu.lock=Bloquear
@@ -445,11 +420,9 @@ main.vaultlist.showPreferencesButton.tooltip=Mostrar preferencias
##Notification
main.notification.updateAvailable=Existen actualizaciones disponibles.
main.notification.support=Soporte de Cryptomator.
main.notification.closeButton.tooltip=Cerrar barra de información
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=Gracias por elegir Cryptomator para proteger sus archivos. En caso de necesitar ayuda, revise nuestras guías:
main.vaultDetail.storageLocation=Ubicación de almacenamiento en bóveda
### Locked
main.vaultDetail.lockedStatus=BLOQUEADA
main.vaultDetail.unlockBtn=Desbloquear…
@@ -538,7 +511,6 @@ vaultOptions.masterkey.forgetSavedPasswordBtn=Olvidar contraseña guardada
vaultOptions.masterkey.recoveryKeyExplanation=Una clave de recuperación es el único medio para restaurar el acceso a una bóveda si pierde su contraseña.
vaultOptions.masterkey.showRecoveryKeyBtn=Mostrar clave de recuperación
vaultOptions.masterkey.recoverPasswordBtn=Restablecer contraseña
vaultOptions.masterkey.missingMasterkeyFile=Estas opciones sólo están disponibles si el archivo masterkey está presente en el directorio de la bóveda.
## Hub
vaultOptions.hub=Recuperación
vaultOptions.hub.convertInfo=Puede utilizar la clave de recuperación para convertir esta bóveda de Hub en una bóveda con contraseña en una emergencia.
@@ -725,7 +697,6 @@ eventView.entry.brokenFileNode.copyDecrypted=Copiar ruta descifrada
eventView.entry.inUse.message=Fichero en uso
eventView.entry.inUse.showDecrypted=Mostrar archivo descifrado
eventView.entry.inUse.showEncrypted=Mostrar archivo cifrado
eventView.entry.inUse.copyUserAndDevice=Copiar nombre de usuario y dispositivo de bloqueo
eventView.entry.inUse.ignoreLock=Ignorar el estado de uso
@@ -733,4 +704,4 @@ eventView.entry.inUse.ignoreLock=Ignorar el estado de uso
## FileIsInUse Notification
notification.inUse.message=El archivo está en uso en otro dispositivo
notification.inUse.description=El archivo está abierto por %s en %s. Solicítale que cierre el archivo y deje que finalice la sincronización. Puede ignorar el estado para abrirlo ahora, pero esto puede causar conflictos o sobrescribir cambios más recientes.
notification.inUse.action=Ignorar el Estado de Uso
notification.inUse.action=Ignorar el Estado de Uso

Some files were not shown because too many files have changed in this diff Show More