Compare commits

...

200 Commits

Author SHA1 Message Date
jose-cardoso-a22506616
472b10a8a4 Merge pull request #4228 from jose-cardoso-a22506616/issue-3993
Closes #3993
2026-05-04 08:50:10 +02:00
Armin Schrenk
2fe7fe886c Merge pull request #4227 from yugui923/fix/vault-options-mount-scroll
fix(ui): make vault options mounting tab scrollable
2026-04-27 14:50:46 +02:00
Armin Schrenk
459112f5b8 Fix release workflow
"workflow_call" is not a event name
2026-04-27 13:01:13 +02:00
Armin Schrenk
ea86124c7d Merge pull request #4205 from cryptomator/feature/immutable-releases
Feature: Refactor Release pipeline to allow immutable releases
2026-04-27 09:22:01 +02:00
Yuri Gui
cd9e315447 fix(ui): make Vault Options "Mounting" tab scrollable
Wrap the mounting-options VBox in a ScrollPane so that conditionally
shown rows (drive-letter selector, custom mount-point picker, custom
mount flags) remain reachable when the Vault Options window is at or
near its 300px minimum height. Previously the rows were clipped past
the bottom of the tab with no scrollbar, forcing the user to enlarge
the window manually before the controls could be seen or interacted
with.

Fixes #3150

Co-Authored-By: Yuri Gui <yugui923@users.noreply.github.com>
2026-04-25 14:41:28 +00:00
Armin Schrenk
1266926ebb fix docs
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2026-04-24 15:39:05 +02:00
Armin Schrenk
1e6bc1f043 Update RELEASE doc
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2026-04-24 15:10:57 +02:00
Armin Schrenk
1230e787a9 Update RELEASE.md
Signed-off-by: Armin Schrenk <armin.schrenk@skymatic.de>
2026-04-21 15:36:54 +02:00
Armin Schrenk
da8493f25f add release documentation 2026-04-16 17:57:39 +02:00
Armin Schrenk
4a0a4309c2 Replace get-version workflow call with slim job 2026-04-16 17:53:08 +02:00
Armin Schrenk
c52ae0120d update changelog 2026-04-16 17:51:15 +02:00
Armin Schrenk
e6cef947c1 rename job 2026-04-16 17:51:06 +02:00
Armin Schrenk
08009ca1a0 Merge branch 'develop' into feature/immutable-releases
# Conflicts:
#	.github/workflows/appimage.yml
#	.github/workflows/mac-dmg-x64.yml
#	.github/workflows/mac-dmg.yml
#	.github/workflows/win-exe.yml
2026-04-14 12:14:23 +02:00
Armin Schrenk
d27a52752b improve readability of version splitting 2026-04-14 11:40:02 +02:00
Armin Schrenk
c745fca2ea update draft-release.yml
adhere to new workflow_call api
2026-04-13 17:58:18 +02:00
Armin Schrenk
d1fcf528b0 use pwsh to move reattach burn engine 2026-04-13 17:40:43 +02:00
Armin Schrenk
b6133e481c Refactor appimage, dmg and msi/exe workflows
* unify inputs handling for worklfow dispatch and call
* simplify conditions for signing/release steps
2026-04-13 16:27:12 +02:00
LamTrinh.Dev
d67aa9c10a + Update Copyright year from "2016 - 2025" to "2016-2026". (#4218)
* + Update Copyright year from "2016 - 2025" to "2016-2026".
2026-04-13 15:30:33 +02:00
Armin Schrenk
19a9595f2e rename worfklow to clearly indicate what it does 2026-04-13 11:46:03 +02:00
Armin Schrenk
f21ae0e11c fix notarization of dmg files on workflow calls 2026-04-13 11:37:51 +02:00
Armin Schrenk
54f805a0c9 fix exe installer not found after 2026-04-09 17:18:07 +02:00
Armin Schrenk
aa239418cf use more local environment instead of copying data into script 2026-04-09 16:57:30 +02:00
Armin Schrenk
8f0c6eb994 fix bugs
* win-exe still checked for release event
* only perform appimage checksum step on release
* remove unused input in appimage flow
2026-04-09 16:55:08 +02:00
Armin Schrenk
4751c81d06 adjust hub-url in share vault dialog 2026-04-08 16:49:11 +02:00
Armin Schrenk
0e1132133c replace guava cache with caffeine 2026-04-08 16:23:25 +02:00
Armin Schrenk
c7d77091f5 Merge branch 'develop' into feature/immutable-releases
# Conflicts:
#	.github/workflows/appimage.yml
#	.github/workflows/build.yml
#	.github/workflows/mac-dmg-x64.yml
#	.github/workflows/mac-dmg.yml
#	.github/workflows/post-publish.yml
2026-04-08 16:08:23 +02:00
Armin Schrenk
3a4039d663 use ubuntu-slim runner for small tasks 2026-04-08 13:00:23 +02:00
Armin Schrenk
f94d204604 cleanup 2026-04-08 12:57:32 +02:00
Armin Schrenk
d76dbe0ddb Replace automatic version file update
by simple notification
2026-04-08 12:54:27 +02:00
Armin Schrenk
235a23ce77 fix check for tag belongs to main or release branch 2026-04-08 12:38:23 +02:00
Armin Schrenk
357b30684b point to correct release workflow file in template 2026-04-08 12:37:50 +02:00
Armin Schrenk
517e12a586 pin jdk version for release 2026-04-08 12:35:54 +02:00
Armin Schrenk
96aa8fe180 add pkgrel update step for aur-bin 2026-04-07 17:20:08 +02:00
Armin Schrenk
7bffc317ff rename workflow from release to create-release
to prevent confusion with github release.yml standard
2026-04-07 17:05:30 +02:00
Armin Schrenk
7dde389671 move aur-bin PR creation into own workflow 2026-04-07 17:04:56 +02:00
dependabot[bot]
a40f17e8f1 Bump the github-actions group with 5 updates (#4207)
Bumps the github-actions group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [softprops/action-gh-release](https://github.com/softprops/action-gh-release) | `2.5.0` | `2.6.1` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `8.0.0` | `8.0.1` |
| [actions/cache](https://github.com/actions/cache) | `5.0.3` | `5.0.4` |
| [skymatic/semver-validation-action](https://github.com/skymatic/semver-validation-action) | `3.0.0` | `4.0.0` |
| [vedantmgoyal2009/winget-releaser](https://github.com/vedantmgoyal2009/winget-releaser) | `19e706d4c9121098010096f9c495a70a7518b30f` | `7bd472be23763def6e16bd06cc8b1cdfab0e2fd5` |


Updates `softprops/action-gh-release` from 2.5.0 to 2.6.1
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](a06a81a03e...153bb8e044)

Updates `actions/download-artifact` from 8.0.0 to 8.0.1
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](70fc10c6e5...3e5f45b2cf)

Updates `actions/cache` from 5.0.3 to 5.0.4
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](cdf6c1fa76...668228422a)

Updates `skymatic/semver-validation-action` from 3.0.0 to 4.0.0
- [Release notes](https://github.com/skymatic/semver-validation-action/releases)
- [Commits](7a6ae1c9e1...7c80b6b03a)

Updates `vedantmgoyal2009/winget-releaser` from 19e706d4c9121098010096f9c495a70a7518b30f to 7bd472be23763def6e16bd06cc8b1cdfab0e2fd5
- [Release notes](https://github.com/vedantmgoyal2009/winget-releaser/releases)
- [Commits](19e706d4c9...7bd472be23)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.6.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/cache
  dependency-version: 5.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: skymatic/semver-validation-action
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: vedantmgoyal2009/winget-releaser
  dependency-version: 7bd472be23763def6e16bd06cc8b1cdfab0e2fd5
  dependency-type: direct:production
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 11:39:43 +02:00
Copilot
699e22d31a chore: Consolidate dependabot maven groups and switch to monthly schedule (#4208)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: infeo <9036915+infeo@users.noreply.github.com>
2026-04-02 16:58:12 +02:00
Armin Schrenk
5d5b9137cc fix workflows 2026-04-01 16:38:07 +02:00
Armin Schrenk
02767b0caf pin aws-configure action 2026-04-01 16:29:59 +02:00
Armin Schrenk
ddd8c572e7 prevent accidental releases of unsigend tags 2026-04-01 16:27:20 +02:00
Armin Schrenk
aa8ccf53df remove manual trigger for workflow 2026-04-01 16:16:46 +02:00
Armin Schrenk
c199561a9e Link to the changelog of the git tag
in the release notes
2026-04-01 16:10:26 +02:00
Armin Schrenk
e341983ffe add update-latest-version job to post-publish 2026-04-01 14:14:33 +02:00
Armin Schrenk
f10b28ecb1 fix typos 2026-03-31 18:11:41 +02:00
Armin Schrenk
95eed96a37 gate windows specifc post-publishes task with check
for windows artifacts in release
2026-03-31 16:02:32 +02:00
mindmonk
9de0981594 Merge pull request #4164 from cryptomator/feature/decrypt-name-single-dialog-per-vault
Fix Decrypt File Name dialog to reuse a single window
2026-03-31 11:02:13 +02:00
Armin Schrenk
4d6ce70afe Merge branch 'develop' into feature/immutable-releases
# Conflicts:
#	.github/workflows/appimage.yml
#	.github/workflows/mac-dmg-x64.yml
#	.github/workflows/mac-dmg.yml
#	.github/workflows/win-exe.yml
2026-03-30 15:37:06 +02:00
Armin Schrenk
e75deb282c use correct cmd
for computing sha sum
2026-03-30 15:33:24 +02:00
Jan-Peter Klein
87fe209b54 Merge branch 'develop' into feature/decrypt-name-single-dialog-per-vault 2026-03-30 12:47:55 +02:00
Jan-Peter Klein
21cffb13c7 remove unused initialList field from DecryptFileNamesViewController 2026-03-30 12:46:11 +02:00
Armin Schrenk
ee92cc4e9f Add docs update workflow 2026-03-30 12:22:03 +02:00
Jan-Peter Klein
9eca05aeb7 address infeos review: drop paths injection, close on locked vault, clear mapping on close 2026-03-30 12:15:09 +02:00
Armin Schrenk
57c84627f9 Update release draft with sed and envsubst
instead of awk
2026-03-30 12:07:08 +02:00
Armin Schrenk
a653fc3627 Merge pull request #4199 from cryptomator/feature/ci-build-flatpak
Feature: Build flatpak on CI
2026-03-27 15:30:48 +01:00
Armin Schrenk
db31fcad98 fix flatpak/cryptomator module git error 2026-03-27 13:39:48 +01:00
Armin Schrenk
b82c73f789 decouple build script from app source
additionally, check tag if a PR should be created
2026-03-26 18:18:04 +01:00
Armin Schrenk
70d3507e5d also tie this workflow to get-version workflow 2026-03-26 17:39:05 +01:00
Armin Schrenk
518b45d149 checkout the correct repository 2026-03-26 17:38:31 +01:00
mindmonk
65f2eea6c9 Merge pull request #4194 from cryptomator/feature/hide-update-warning-when-no-update-available
Hide "lock vaults" warning when no update is available
2026-03-26 17:33:25 +01:00
Jan-Peter Klein
12a4a8218a change warning visibility check to use isUpdateAvailable 2026-03-26 12:41:15 +01:00
Armin Schrenk
76dd4f079c use correct cache key 2026-03-26 11:02:08 +01:00
Armin Schrenk
309a8fc705 adjust repository dispatch for website update
* change event type to desktop-release
* add client payload
2026-03-26 10:48:47 +01:00
Armin Schrenk
0b77e6ce04 use release object in event payload 2026-03-25 23:15:13 +01:00
Armin Schrenk
d22e912ca4 fix wrong webhook 2026-03-25 15:14:35 +01:00
Armin Schrenk
690726efc3 adjust post-publish
* run av-whitelist after publish
* notify winget
* remove gpg signature creation
2026-03-25 11:43:58 +01:00
Armin Schrenk
d9c0c4980b add release flow to create releases 2026-03-25 11:42:26 +01:00
Armin Schrenk
4e5eef5a16 remove release drafting from build flow 2026-03-25 11:41:54 +01:00
Armin Schrenk
81b2081033 fix outputs typo in appimage flow 2026-03-25 11:41:38 +01:00
Armin Schrenk
b35fb011e0 fix wrong env variable 2026-03-25 11:03:56 +01:00
Armin Schrenk
fa1e9c7ca7 [skip ci] add comment for envsubst usage 2026-03-25 11:02:32 +01:00
Armin Schrenk
75bb64b333 add permissions 2026-03-24 17:01:22 +01:00
Armin Schrenk
1f40045b41 ensure correct source is used for checkout 2026-03-24 16:55:27 +01:00
Armin Schrenk
1951e5e82c ensure correct revision 2026-03-24 16:20:33 +01:00
Armin Schrenk
0be39c5b7d [skip ci] fix typo 2026-03-24 13:21:25 +01:00
Armin Schrenk
6886188ff4 remove manual update script 2026-03-24 13:05:42 +01:00
Armin Schrenk
3a2de81369 do not use placeholder files 2026-03-24 12:37:10 +01:00
Armin Schrenk
4de3ea2892 add verification step 2026-03-24 12:22:46 +01:00
Armin Schrenk
0beb2e3334 adjust flathub pr job
* add boolean dispatch input to create pr manually
* remove tarball job
* adjust template build script to final build file
* change PR description
* fix slack webhook
2026-03-24 12:15:41 +01:00
Armin Schrenk
537d79f3c2 activate all runners again 2026-03-24 10:57:32 +01:00
Armin Schrenk
10d5f4f530 rename job 2026-03-24 10:57:08 +01:00
Armin Schrenk
ab0e7e1ea9 fix pattern matching and make flow easier to read 2026-03-23 17:53:33 +01:00
Armin Schrenk
967a3aa10b ensure to download correct javafx jars 2026-03-23 16:47:18 +01:00
Armin Schrenk
821649c821 [debug] replace natsort with sort 2026-03-23 15:36:36 +01:00
Armin Schrenk
7cf75e9dba use SNAPSHOT version of flatpak action
and keep build dirs
2026-03-23 11:18:38 +01:00
Armin Schrenk
dfe126f827 update maven dependency source file 2026-03-23 11:10:03 +01:00
Armin Schrenk
80ce98991c use correct runner for every arch 2026-03-23 10:48:53 +01:00
Armin Schrenk
579d42ec74 try x86 runner 2026-03-20 18:42:40 +01:00
Armin Schrenk
047108683f use as base repo the flathub repo 2026-03-20 18:20:15 +01:00
Armin Schrenk
830c9196e7 activate build flatpak step again 2026-03-20 18:10:38 +01:00
Armin Schrenk
7208f23da1 fix type 2026-03-20 18:04:09 +01:00
Armin Schrenk
b0a4a0bcfa add debugging code 2026-03-20 17:59:45 +01:00
Armin Schrenk
f7b13dd121 Use flathub repo
remove yq edits and simply override build yaml
2026-03-20 17:51:10 +01:00
Armin Schrenk
a5ccfa01d2 use yq action
not available in flathub container
2026-03-20 17:26:04 +01:00
Armin Schrenk
62bf4c5b45 fix prepare step 2026-03-20 17:06:42 +01:00
Armin Schrenk
7168d7e31d deactivate x86 runner during development 2026-03-20 16:54:50 +01:00
Armin Schrenk
3c633f8a34 move all build scripts into flatpak-build dir
* additionally, remove maven-dependencies
2026-03-20 16:54:05 +01:00
Armin Schrenk
b606a4ee8e use FreeDesktop runtime 2026-03-20 16:44:29 +01:00
Armin Schrenk
1af094692f run also on certain push events 2026-03-20 16:25:51 +01:00
Armin Schrenk
c86d8ac6b8 build flatpak on CI 2026-03-20 16:22:35 +01:00
Armin Schrenk
b06d65ab43 Merge branch 'main' into develop
# Conflicts:
#	pom.xml
2026-03-20 08:15:31 +01:00
Armin Schrenk
390bdc33e0 Merge branch 'hotfix/1.19.2' 2026-03-20 08:14:04 +01:00
Armin Schrenk
2284d1fcee Update Changelog 2026-03-20 08:13:37 +01:00
Sebastian Stenzel
62a439e10e CHANGELOG.md: moved security fixes to subsection
[ci skip]
2026-03-20 08:02:58 +01:00
Sebastian Stenzel
ddad663489 add release to metainfo file 2026-03-19 16:41:17 +01:00
Sebastian Stenzel
f08e7d9b92 bump version to 1.19.2 2026-03-19 16:34:42 +01:00
Sebastian Stenzel
6c2865a09d simplify port/scheme check 2026-03-19 16:27:09 +01:00
Armin Schrenk
5a0d15c937 Merge pull request #4187 from cryptomator/feature/monthly-dist-builds
Feature: Monthly distribution builds
2026-03-17 13:18:37 +01:00
Jan-Peter Klein
53757dab93 fix unlocked vault warning shown without an available update 2026-03-16 14:22:23 +01:00
Armin Schrenk
e1d4d3e85b fix webhook in workflow 2026-03-13 18:03:19 +01:00
Armin Schrenk
859fe238b8 also test signing step in monthly builds 2026-03-13 17:51:22 +01:00
Armin Schrenk
23df40796b build release artifacts monthly 2026-03-13 17:50:53 +01:00
Armin Schrenk
b98b1326b2 Merge pull request #4147 from cryptomator/feature/dagger-fixes
fix dagger binding graph
2026-03-13 17:39:29 +01:00
Armin Schrenk
2f8831b561 faster initialization 2026-03-13 17:29:59 +01:00
Armin Schrenk
4948b9b586 prevent data races 2026-03-13 17:06:50 +01:00
Armin Schrenk
838018e72e Remove Actalis code signing (#4169) 2026-03-13 16:49:37 +01:00
Sebastian Stenzel
d5433e7a3f CHANGELOG.md: moved security fixes to subsection
[ci skip]
2026-03-13 10:50:20 +01:00
Armin Schrenk
194e19bf5e [skip ci] reset to snapshot version 2026-03-12 14:37:17 +01:00
Armin Schrenk
1358e7098d Merge branch 'main' into develop 2026-03-12 14:35:39 +01:00
Armin Schrenk
4be327608a Merge branch 'release/1.19.1' 2026-03-12 14:35:20 +01:00
Armin Schrenk
929ef37ded finalize release 2026-03-12 14:12:41 +01:00
Armin Schrenk
6fce1807f0 prepare 1.19.1 2026-03-12 14:08:50 +01:00
Armin Schrenk
c1d72650cc [skip ci] update changelog 2026-03-12 14:06:30 +01:00
Armin Schrenk
dab368e44b Update org.cryptomator.integrations-api to 1.8.0
closes #4176
2026-03-12 14:00:06 +01:00
Armin Schrenk
7548174e4f Update org.cryptomator.integrations-linux to 1.7.0
closes #4175
2026-03-12 13:46:04 +01:00
Armin Schrenk
b513060744 Update dependency org.cryptomator.integrations-mac from 1.5.0-beta3 to 1.5.0
closes #4177
2026-03-12 13:40:18 +01:00
Armin Schrenk
311a9ef70b [skip ci] update changelog 2026-03-12 13:36:56 +01:00
Armin Schrenk
7331144f9d Set cryptomator.hub.enableTrustOnFirstUse (#4182)
in installers/package containers
2026-03-12 13:13:49 +01:00
Cryptobot
d576b36f7b New Crowdin updates (#4172)
New translations strings.properties:
Danish; Korean; Portuguese, Brazilian; Chinese Traditional
2026-03-12 12:43:14 +01:00
Armin Schrenk
7a6340aac6 Merge pull request #4181 from cryptomator/feature/pin-appimagetool
pin appimagetool
2026-03-12 12:31:37 +01:00
Sebastian Stenzel
6b82abcd80 Merge pull request #4179 from cryptomator/feature/admin-config-allowlist
Hub: Trust on first use
2026-03-12 12:26:59 +01:00
Armin Schrenk
97bde05422 adjust wording
use singular because in most cases hub and auth are on either pre-trusted or on the same domain
2026-03-12 12:23:30 +01:00
Armin Schrenk
3bc50eee47 add checksum of appimage tool 2026-03-12 12:16:46 +01:00
Armin Schrenk
b95a220de7 Adjust wording 2026-03-12 12:08:27 +01:00
Armin Schrenk
2896e18429 Adjust dialog message to show current status
checking vs user interaction
2026-03-12 11:42:44 +01:00
Armin Schrenk
29db91f976 fix javafx display issue
do not change scene in initialize() method
2026-03-12 11:28:38 +01:00
Sebastian Stenzel
1e3dfe3de1 Merge pull request #4180 from cryptomator/feature/patched-masterkey-loading
don't load masterkey from arbitrary paths
2026-03-12 10:23:43 +01:00
Sebastian Stenzel
dcb1f5e80f pin appimagetool 2026-03-12 10:21:12 +01:00
Sebastian Stenzel
5c75eeab27 reuse existing constant 2026-03-12 10:18:19 +01:00
Sebastian Stenzel
8e4bff8c19 don't load masterkey from arbitrary paths 2026-03-12 09:58:52 +01:00
Sebastian Stenzel
e066f155b2 filter out "" 2026-03-12 09:21:55 +01:00
Sebastian Stenzel
c3c7a23ccd added test 2026-03-12 09:14:27 +01:00
Sebastian Stenzel
c816411644 deserialize null as empty 2026-03-12 09:13:40 +01:00
Sebastian Stenzel
f752490624 default to mutable collections 2026-03-12 09:12:09 +01:00
Sebastian Stenzel
d196b66b4e refined rules 2026-03-12 09:02:19 +01:00
Sebastian Stenzel
d803a1d71e apply suggestions from code review 2026-03-11 23:17:16 +01:00
Sebastian Stenzel
c75430cde2 only trust subdomains of .cryptomator.cloud
not `notcryptomator.cloud`
2026-03-11 23:13:50 +01:00
Sebastian Stenzel
f44c3531c7 devicesResourceUrl: optional consistency check 2026-03-11 23:12:13 +01:00
Sebastian Stenzel
596829bc77 dedup 2026-03-11 23:09:04 +01:00
Sebastian Stenzel
096aeb0c54 renamed var
[ci skip]
2026-03-11 22:49:28 +01:00
Sebastian Stenzel
1a12684557 fix test 2026-03-11 22:42:25 +01:00
Sebastian Stenzel
998664acc3 distrust http (except for localhost) 2026-03-11 22:01:21 +01:00
Sebastian Stenzel
c52b3fc4ad change scene without lag 2026-03-11 22:01:14 +01:00
Sebastian Stenzel
1fde3650ca renamed classes 2026-03-11 21:47:33 +01:00
Sebastian Stenzel
57614f59e4 trust *.cryptomator.cloud 2026-03-11 21:38:51 +01:00
Sebastian Stenzel
a95883f98b renamed system properties 2026-03-11 21:32:51 +01:00
Sebastian Stenzel
6cf18c33a1 simplify sorting 2026-03-11 21:12:47 +01:00
Sebastian Stenzel
232e6fbf71 add tests 2026-03-11 21:05:14 +01:00
Sebastian Stenzel
d15731d5a4 add plural label 2026-03-11 20:30:00 +01:00
Sebastian Stenzel
45d7421e4f add "reset trusted hosts" button to general preferences 2026-03-11 20:24:56 +01:00
Sebastian Stenzel
4d0aabfe6e cleanup 2026-03-11 20:12:16 +01:00
Sebastian Stenzel
db0e266fde clean up dialog 2026-03-11 19:48:37 +01:00
Sebastian Stenzel
6281561ab4 add -Dcryptomator.allowUnknownHubHosts=true 2026-03-11 18:53:14 +01:00
Sebastian Stenzel
b981f0dc19 moved logic to separate controller 2026-03-11 18:52:36 +01:00
Armin Schrenk
bc41429982 Use environment for cryptomator system properties 2026-03-11 17:59:58 +01:00
Armin Schrenk
a31621cfde validate hub config and check allowed hosts file 2026-03-11 17:35:22 +01:00
Armin Schrenk
aa68224d63 Add new properties to admin config
* allowUnknownHubHosts
* allowedHubHosts
2026-03-11 16:49:48 +01:00
Armin Schrenk
b8413a21a3 update appstream info 2026-03-09 17:01:46 +01:00
Armin Schrenk
af59196f19 [skip ci] Merge branch 'main' into develop 2026-03-09 09:32:54 +01:00
Armin Schrenk
3a58f560fc Merge branch 'release/1.19.0' 2026-03-09 09:27:31 +01:00
Armin Schrenk
a2cca0b624 finalize release 2026-03-08 23:53:07 +01:00
Armin Schrenk
0c4ba94f10 Merge branch 'develop' into release/1.19.0 2026-03-08 23:47:36 +01:00
Cryptobot
0f8b6a8f51 New Crowdin updates (#4170)
* New translations strings.properties (Korean)
[ci skip]
2026-03-08 23:45:59 +01:00
Armin Schrenk
8ca6946fe6 Suppress CVE-2025-11143
Jetty is used only locally
2026-03-08 18:08:23 +01:00
Armin Schrenk
e583b70931 use correct filename for admin config on Windows 2026-03-07 12:06:13 +01:00
Armin Schrenk
d9134b49ad add workflow_call to release artifact workflows 2026-03-06 17:11:11 +01:00
Armin Schrenk
6f6d0d8771 Update wording in release draft 2026-03-06 12:58:17 +01:00
Armin Schrenk
9d3d67b605 Update changelog 2026-03-05 23:12:58 +01:00
Armin Schrenk
8da27e36d6 Merge branch 'develop' into release/1.19.0 2026-03-05 23:10:11 +01:00
dependabot[bot]
1123fa28a3 Bump org.apache.maven.plugins:maven-resources-plugin (#4167)
Bumps the maven-build-plugins group with 1 update: [org.apache.maven.plugins:maven-resources-plugin](https://github.com/apache/maven-resources-plugin).


Updates `org.apache.maven.plugins:maven-resources-plugin` from 3.4.0 to 3.5.0
- [Release notes](https://github.com/apache/maven-resources-plugin/releases)
- [Commits](https://github.com/apache/maven-resources-plugin/compare/v3.4.0...maven-resources-plugin-3.5.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-resources-plugin
  dependency-version: 3.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: maven-build-plugins
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-05 23:09:12 +01:00
Armin Schrenk
be3ecc536a Update to cryptofs 2.10.0 2026-03-05 17:47:52 +01:00
Armin Schrenk
94c8dd911b [skip ci] update changelog 2026-03-05 17:45:14 +01:00
Armin Schrenk
7e888b0e3b ensure logging for Substituting properties 2026-03-05 17:44:47 +01:00
dependabot[bot]
a066afe37d Bump the java-production-dependencies group with 8 updates (#4152)
* Bump the java-production-dependencies group with 8 updates

Bumps the java-production-dependencies group with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [ch.qos.logback:logback-core](https://github.com/qos-ch/logback) | `1.5.31` | `1.5.32` |
| [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) | `1.5.31` | `1.5.32` |
| [com.auth0:java-jwt](https://github.com/auth0/java-jwt) | `4.5.0` | `4.5.1` |
| [com.fasterxml.jackson.core:jackson-databind](https://github.com/FasterXML/jackson) | `2.21.0` | `2.21.1` |
| com.fasterxml.jackson.datatype:jackson-datatype-jsr310 | `2.21.0` | `2.21.1` |
| [com.google.dagger:dagger](https://github.com/google/dagger) | `2.59.1` | `2.59.2` |
| [com.google.dagger:dagger-compiler](https://github.com/google/dagger) | `2.59.1` | `2.59.2` |
| [org.jetbrains:annotations](https://github.com/JetBrains/java-annotations) | `26.0.2-1` | `26.1.0` |


Updates `ch.qos.logback:logback-core` from 1.5.31 to 1.5.32
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.31...v_1.5.32)

Updates `ch.qos.logback:logback-classic` from 1.5.31 to 1.5.32
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.31...v_1.5.32)

Updates `ch.qos.logback:logback-classic` from 1.5.31 to 1.5.32
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.31...v_1.5.32)

Updates `com.auth0:java-jwt` from 4.5.0 to 4.5.1
- [Release notes](https://github.com/auth0/java-jwt/releases)
- [Changelog](https://github.com/auth0/java-jwt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/auth0/java-jwt/compare/4.5.0...4.5.1)

Updates `com.fasterxml.jackson.core:jackson-databind` from 2.21.0 to 2.21.1
- [Commits](https://github.com/FasterXML/jackson/commits)

Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.21.0 to 2.21.1

Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.21.0 to 2.21.1

Updates `com.google.dagger:dagger` from 2.59.1 to 2.59.2
- [Release notes](https://github.com/google/dagger/releases)
- [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/dagger/compare/dagger-2.59.1...dagger-2.59.2)

Updates `com.google.dagger:dagger-compiler` from 2.59.1 to 2.59.2
- [Release notes](https://github.com/google/dagger/releases)
- [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/dagger/compare/dagger-2.59.1...dagger-2.59.2)

Updates `org.jetbrains:annotations` from 26.0.2-1 to 26.1.0
- [Release notes](https://github.com/JetBrains/java-annotations/releases)
- [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md)
- [Commits](https://github.com/JetBrains/java-annotations/compare/26.0.2-1...26.1.0)

Updates `com.google.dagger:dagger-compiler` from 2.59.1 to 2.59.2
- [Release notes](https://github.com/google/dagger/releases)
- [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/dagger/compare/dagger-2.59.1...dagger-2.59.2)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-core
  dependency-version: 1.5.32
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.32
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.32
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: com.auth0:java-jwt
  dependency-version: 4.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-version: 2.21.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310
  dependency-version: 2.21.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310
  dependency-version: 2.21.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: com.google.dagger:dagger
  dependency-version: 2.59.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: com.google.dagger:dagger-compiler
  dependency-version: 2.59.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
- dependency-name: org.jetbrains:annotations
  dependency-version: 26.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: java-production-dependencies
- dependency-name: com.google.dagger:dagger-compiler
  dependency-version: 2.59.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: java-production-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* [skip ci] update changelog

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Armin Schrenk <armin.schrenk@skymatic.de>
2026-03-05 17:29:09 +01:00
Cryptobot
21c8cbf709 New Crowdin updates (#4148)
New translations strings.properties

Arabic; Bulgarian; Chinese Simplified; Chinese Traditional; Danish; Dutch; Finnish; French; German; Greek; Italian; Japanese; Korean; Latvian; Polish; Portuguese; Portuguese, Brazilian; Punjabi; Russian; Slovak; Spanish; Swedish; Turkish; Ukrainian; Vietnamese; 

[ci skip]
2026-03-05 17:28:41 +01:00
dependabot[bot]
ea236d45a3 Bump the java-test-dependencies group across 1 directory with 2 updates (#4079)
Bumps the java-test-dependencies group with 2 updates in the / directory: [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit-framework) and [org.mockito:mockito-core](https://github.com/mockito/mockito).


Updates `org.junit.jupiter:junit-jupiter` from 5.13.4 to 6.0.1
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.4...r6.0.1)

Updates `org.mockito:mockito-core` from 5.20.0 to 5.21.0
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v5.20.0...v5.21.0)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-version: 6.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: java-test-dependencies
- dependency-name: org.mockito:mockito-core
  dependency-version: 5.21.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: java-test-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-05 17:27:42 +01:00
dependabot[bot]
fab5ac3726 Bump the maven-build-plugins group across 1 directory with 8 updates (#4151)
* Bump the maven-build-plugins group across 1 directory with 8 updates

Bumps the maven-build-plugins group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [me.fabriciorby:maven-surefire-junit5-tree-reporter](https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter) | `1.4.0` | `1.5.1` |
| [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) | `3.14.1` | `3.15.0` |
| [org.apache.maven.plugins:maven-resources-plugin](https://github.com/apache/maven-resources-plugin) | `3.3.1` | `3.4.0` |
| [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) | `3.8.1` | `3.10.0` |
| [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) | `3.5.4` | `3.5.5` |
| [org.codehaus.mojo:license-maven-plugin](https://github.com/mojohaus/license-maven-plugin) | `2.7.0` | `2.7.1` |
| [org.apache.maven.plugins:maven-jar-plugin](https://github.com/apache/maven-jar-plugin) | `3.4.2` | `3.5.0` |
| [org.owasp:dependency-check-maven](https://github.com/dependency-check/DependencyCheck) | `12.1.5` | `12.2.0` |



Updates `me.fabriciorby:maven-surefire-junit5-tree-reporter` from 1.4.0 to 1.5.1
- [Release notes](https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter/releases)
- [Commits](https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter/compare/v1.4.0...v1.5.1)

Updates `org.apache.maven.plugins:maven-compiler-plugin` from 3.14.1 to 3.15.0
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.14.1...maven-compiler-plugin-3.15.0)

Updates `org.apache.maven.plugins:maven-resources-plugin` from 3.3.1 to 3.4.0
- [Release notes](https://github.com/apache/maven-resources-plugin/releases)
- [Commits](https://github.com/apache/maven-resources-plugin/compare/maven-resources-plugin-3.3.1...v3.4.0)

Updates `org.apache.maven.plugins:maven-dependency-plugin` from 3.8.1 to 3.10.0
- [Release notes](https://github.com/apache/maven-dependency-plugin/releases)
- [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.8.1...maven-dependency-plugin-3.10.0)

Updates `org.apache.maven.plugins:maven-surefire-plugin` from 3.5.4 to 3.5.5
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.4...surefire-3.5.5)

Updates `org.codehaus.mojo:license-maven-plugin` from 2.7.0 to 2.7.1
- [Release notes](https://github.com/mojohaus/license-maven-plugin/releases)
- [Commits](https://github.com/mojohaus/license-maven-plugin/compare/2.7.0...2.7.1)

Updates `org.apache.maven.plugins:maven-jar-plugin` from 3.4.2 to 3.5.0
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.4.2...maven-jar-plugin-3.5.0)

Updates `org.owasp:dependency-check-maven` from 12.1.5 to 12.2.0
- [Release notes](https://github.com/dependency-check/DependencyCheck/releases)
- [Changelog](https://github.com/dependency-check/DependencyCheck/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dependency-check/DependencyCheck/compare/v12.1.5...v12.2.0)

---
updated-dependencies:
- dependency-name: me.fabriciorby:maven-surefire-junit5-tree-reporter
  dependency-version: 1.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: maven-build-plugins
- dependency-name: org.apache.maven.plugins:maven-compiler-plugin
  dependency-version: 3.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: maven-build-plugins
- dependency-name: org.apache.maven.plugins:maven-resources-plugin
  dependency-version: 3.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: maven-build-plugins
- dependency-name: org.apache.maven.plugins:maven-dependency-plugin
  dependency-version: 3.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: maven-build-plugins
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-version: 3.5.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: maven-build-plugins
- dependency-name: org.codehaus.mojo:license-maven-plugin
  dependency-version: 2.7.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: maven-build-plugins
- dependency-name: org.apache.maven.plugins:maven-jar-plugin
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: maven-build-plugins
- dependency-name: org.owasp:dependency-check-maven
  dependency-version: 12.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: maven-build-plugins
...

Signed-off-by: dependabot[bot] <support@github.com>

* downgrade maven surefire

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Armin Schrenk <armin.schrenk@skymatic.de>
2026-03-05 17:23:36 +01:00
dependabot[bot]
57666a620c Bump the github-actions group with 3 updates (#4162)
Bumps the github-actions group with 3 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact), [actions/download-artifact](https://github.com/actions/download-artifact) and [actions/stale](https://github.com/actions/stale).


Updates `actions/upload-artifact` from 6.0.0 to 7.0.0
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](b7c566a772...bbbca2ddaa)

Updates `actions/download-artifact` from 7.0.0 to 8.0.0
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](37930b1c2a...70fc10c6e5)

Updates `actions/stale` from 10.1.1 to 10.2.0
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](997185467f...b5d41d4e1d)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/stale
  dependency-version: 10.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-05 17:12:30 +01:00
Armin Schrenk
e49fe23b35 Explicitly define webdav-nio-adapter-servlet dependency
and update changelog
2026-03-03 18:22:57 +01:00
Armin Schrenk
254a7696c8 [skip ci] update changelog 2026-03-03 18:13:54 +01:00
Armin Schrenk
62bd43ef9b Update fuse-nio-adapter from 6.0.0 to 6.0.1
closes #4121
2026-03-03 18:12:49 +01:00
Tobias Hagemann
b73510ec0f Merge pull request #4166 from cryptomator/feature/liquid-glass-icon
Add Liquid Glass icon for macOS 26 and remove DockTilePlugin workaround
2026-03-03 14:31:23 +01:00
Armin Schrenk
7daca70a15 Feature: Replace JavaFX host services with RevealPathService impls (#4165)
when a OS path need to be shown in the OS file browser.
2026-03-03 11:25:29 +01:00
Jan-Peter Klein
92d11d8bd5 Merge branch 'develop' into feature/decrypt-name-single-dialog-per-vault 2026-03-02 14:18:15 +01:00
Jan-Peter Klein
300a811510 fix decrypt file name dialog to reuse one window per vault 2026-03-02 13:39:38 +01:00
Armin Schrenk
1dab85d265 Merge branch 'develop' into release/1.19.0 2026-02-18 16:25:04 +01:00
Armin Schrenk
84a966993f fix dagger binding graph 2026-02-18 16:24:22 +01:00
Armin Schrenk
ad240eb18b Merge branch 'develop' into release/1.19.0
# Conflicts:
#	pom.xml
2025-12-30 12:40:06 +01:00
Sebastian Stenzel
04bdb2d8c4 fix mocked update check 2025-11-24 20:58:57 +01:00
Sebastian Stenzel
f32d24c1c0 Merge branch 'develop' into release/1.19.0 2025-11-24 20:58:25 +01:00
Sebastian Stenzel
3f06460667 bump version 2025-11-24 14:50:19 +01:00
106 changed files with 2747 additions and 1250 deletions

View File

@@ -3,10 +3,7 @@ updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "Etc/UTC"
interval: "monthly"
ignore:
- dependency-name: "org.cryptomator:integrations-api"
versions: ["2.0.0-alpha1"]
@@ -14,37 +11,13 @@ updates:
versions: ["2.0.1.MR"]
- dependency-name: "org.openjfx:*"
update-types: ["version-update:semver-major"]
# due to https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter/issues/68
- dependency-name: "org.apache.maven.plugins:maven-surefire-plugin"
versions: [ "3.5.4", "3.5.5" ]
groups:
java-test-dependencies:
patterns:
- "org.junit.jupiter:*"
- "org.mockito:*"
- "org.hamcrest:*"
- "com.google.jimfs:jimfs"
maven-build-plugins:
patterns:
- "org.apache.maven.plugins:*"
- "org.jacoco:jacoco-maven-plugin"
- "org.owasp:dependency-check-maven"
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
- "org.codehaus.mojo:license-maven-plugin"
javafx:
patterns:
- "org.openjfx:*"
java-production-dependencies:
maven-dependencies:
patterns:
- "*"
exclude-patterns:
- "org.openjfx:*"
- "org.apache.maven.plugins:*"
- "org.jacoco:jacoco-maven-plugin"
- "org.owasp:dependency-check-maven"
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
- "org.codehaus.mojo:license-maven-plugin"
- "org.junit.jupiter:*"
- "org.mockito:*"
- "org.hamcrest:*"
- "com.google.jimfs:jimfs"
- package-ecosystem: "github-actions"
directory: "/" # even for `.github/workflows`

34
.github/release-body.md.template vendored Normal file
View File

@@ -0,0 +1,34 @@
<!-- 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
.github/workflows/RELEASE.md vendored Normal file
View File

@@ -0,0 +1,189 @@
# Cryptomator Release Workflow
This document describes the automated release pipeline defined in [`draft-release.yml`](draft-release.yml) and [`post-publish.yml`](post-publish.yml).
## Overview
The release process has two phases:
1. **Draft phase** (`draft-release.yml`) -- triggered by pushing a signed git tag. Compiles, tests, builds platform installers, and creates a **draft** GitHub Release.
2. **Post-publish phase** (`post-publish.yml`) -- triggered when the draft release is manually **published**. Submits Windows installers for AV whitelisting, notifies the team for DEB build and latest-version update, and triggers downstream updates (website, docs, winget).
```mermaid
---
config:
htmlLabels: false
---
flowchart TD
%% ── Trigger ──────────────────────────────────────────────
push_tag([🏷 Signed tag pushed])
%% ── Draft phase ──────────────────────────────────────────
push_tag --> get-version
subgraph draft["draft-release.yml"]
get-version["get-version
*parse semver from tag*"]
get-version --> create-release-draft
create-release-draft["create-release-draft
*compile & test (Linux)
create draft release
sign source tarball*"]
create-release-draft --> build-exe-and-msi
create-release-draft --> build-dmg-arm64
create-release-draft --> build-dmg-x64
create-release-draft --> build-appimages
build-exe-and-msi["build-exe-and-msi
*calls win-exe.yml
MSI + EXE (x64)
code-signed & GPG-signed*"]
build-dmg-arm64["build-dmg-arm64
*calls mac-dmg.yml
DMG (arm64)
notarized & GPG-signed*"]
build-dmg-x64["build-dmg-x64
*calls mac-dmg-x64.yml
DMG (x64)
notarized & GPG-signed*"]
build-appimages["build-appimages
*calls appimage.yml
AppImage (x86_64 + aarch64)
GPG-signed*"]
build-exe-and-msi --> update-sha256sums
build-dmg-arm64 --> update-sha256sums
build-dmg-x64 --> update-sha256sums
build-appimages --> update-sha256sums
update-sha256sums["update-sha256sums
*compute checksums
update release body*"]
end
update-sha256sums --> manual_review
%% ── Manual gate ──────────────────────────────────────────
manual_review{{Manual review
& publish}}
%% ── Post-publish phase ───────────────────────────────────
manual_review --> published([📢 Release published])
published --> post-publish
subgraph post-publish["post-publish.yml"]
direction TB
check-release["check-release
*classify release tag
stable, alpha, beta, rc, unknown*"]
notify["notify
*Slack notifications
deb build & version check*"]
get-asset-urls["get-asset-urls
*extract MSI & EXE
download URLs*"]
check-release --> notify-winget
check-release --> trigger-website
check-release --> trigger-docs
get-asset-urls --> allowlist-msi
allowlist-msi --> allowlist-exe
allowlist-msi["allowlist-msi-x64
*av-whitelist.yml
Kaspersky & Avast*"]
allowlist-exe["allowlist-exe-x64
*av-whitelist.yml
Kaspersky & Avast*"]
notify-winget["notify-winget
*Slack: ready for winget
stable only*"]
trigger-website["trigger-website-update
*dispatch to
cryptomator.github.io
stable only*"]
trigger-docs["trigger-docs-update
*dispatch to
cryptomator/docs
stable only, Windows*"]
end
```
## Phase 1: Draft Release (`draft-release.yml`)
**Trigger:** push of any tag (`*`)
### Jobs
| Job | Runs on | Description |
|-----|---------|-------------|
| **get-version** | ubuntu | Parses the tag into semver components (`semVerNum`, `semVerSuffix`, `revNum`, `versionType`). The release is aborted if not an alpha, beta, rc or 'stable' release. |
| **create-release-draft** | ubuntu | Checks out the repo, verifies the tag is **signed** and lives on a `main` or `release/*` branch. Runs `mvn 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.

View File

@@ -1,17 +1,44 @@
name: Build AppImage
on:
release:
types: [published]
schedule:
- cron: '0 23 20 * *'
workflow_call:
inputs:
semVerNum:
type: string
description: 'The Major.Minor.Patch part of the version'
required: true
revisionNum:
type: string
description: 'The revision number'
required: true
semVerSuffix:
type: string
description: 'The suffix of the version, including dash'
required: true
upload-to-draft:
type: boolean
default: true
outputs:
sha256-appimage-x64:
description: "SHA256 sum of the x64 appimage"
value: ${{ jobs.collect-sha256sums.outputs.x64-sha256sum}}
sha256-appimage-aarch64:
description: "SHA256 sum of the aarch64 appimage"
value: ${{ jobs.collect-sha256sums.outputs.aarch64-sha256sum}}
workflow_dispatch:
inputs:
version:
description: 'Version'
semVerNum:
description: 'The Major.Minor.Patch part of the version'
required: false
create-pr:
description: 'Create a PR for aur-bin repo'
type: boolean
default: false
revisionNum:
description: 'The revision number'
required: false
semVerSuffix:
description: 'The suffix of the version, including dash'
required: false
default: '-SNAPSHOT'
push:
branches-ignore:
- 'dependabot/**'
@@ -24,21 +51,15 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '25.0.2+10.0.LTS'
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.version }} #okay if not defined
build:
name: Build AppImage
runs-on: ${{ matrix.os }}
needs: [get-version]
env:
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
SEMVER_NUM: ${{ needs.get-version.outputs.semVerNum }}
REV_NUM: ${{ needs.get-version.outputs.revNum }}
strategy:
fail-fast: false
matrix:
@@ -47,10 +68,12 @@ jobs:
arch: x86_64
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
openjfx-sha: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
appimagetool-sha: 'ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0'
- os: ubuntu-24.04-arm
arch: aarch64
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
appimagetool-sha: 'f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Java
@@ -81,7 +104,7 @@ jobs:
exit 1
fi
- name: Set version
run : mvn versions:set -DnewVersion="$SEMVER_STR"
run : mvn versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
- name: Run maven
run: mvn -B clean package -Plinux -DskipTests
- name: Patch target dir
@@ -123,13 +146,13 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${SEMVER_NUM}.${REV_NUM}"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}.${REVISION_NUM}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\""
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\""
@@ -140,8 +163,9 @@ jobs:
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
--java-options "-Dcryptomator.buildNumber=\"appimage-${REV_NUM}\""
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NUM}\""
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
--resource-dir dist/linux/resources
- name: Patch Cryptomator.AppDir
@@ -165,7 +189,8 @@ 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/continuous/appimagetool-${{ matrix.arch }}.AppImage" -o appimagetool.AppImage
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
chmod +x appimagetool.AppImage
./appimagetool.AppImage --appimage-extract
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
@@ -177,7 +202,7 @@ jobs:
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Build AppImage
run: >
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${SEMVER_STR}-${{ matrix.arch }}.AppImage
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.AppImage
-u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.arch }}.AppImage.zsync"
--sign --sign-key=615D449FE6E6A235
- name: Create detached GPG signatures
@@ -185,7 +210,7 @@ jobs:
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: appimage-${{ matrix.arch }}
path: |
@@ -194,9 +219,10 @@ jobs:
cryptomator-*.asc
if-no-files-found: error
- name: Publish AppImage on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
if: inputs.upload-to-draft
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
draft: true
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
@@ -204,79 +230,24 @@ jobs:
cryptomator-*.zsync
cryptomator-*.asc
create-aur-bin-pr:
name: Create PR for aur-bin repo
if: github.event_name == 'workflow_dispatch' && inputs.create-pr || github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable'
collect-sha256sums:
name: Collect AppImage checksums
runs-on: ubuntu-latest
needs: [build, get-version]
container:
image: archlinux:base-devel
env:
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
PKGDEST: ${{ github.workspace }}/pkgdest
SRCDEST: ${{ github.workspace }}/srcdest
needs: [build]
if: inputs.upload-to-draft
outputs:
x64-sha256sum: ${{ steps.sha256sum.outputs.x64-sha256sum }}
aarch64-sha256sum: ${{ steps.sha256sum.outputs.aarch64-sha256sum }}
steps:
- name: Prepare pacman
run: |
pacman-key --init
pacman-key --populate archlinux
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip github-cli curl pacman-contrib
- name: Checkout cryptomator/aur-bin
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download AppImage artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
repository: 'cryptomator/aur-bin'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Create build user
pattern: appimage-*
path: appimage-artifacts
- name: Compute SHA256 sums
id: sha256sum
run: |
useradd -m builder
echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/builder
chown -R builder:builder "$GITHUB_WORKSPACE"
install -d -m 0755 -o builder -g builder "$PKGDEST" "$SRCDEST"
- name: Import Cryptomator release signing key
# try first ubuntu. on failure try openpgp keyservers
run: >
sudo -u builder gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
|| sudo -u builder gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
- name: Checkout release branch
run: |
git config --global safe.directory '*'
git checkout -b "release/${SEMVER_STR}"
- name: Update build file
run: |
sed -i -e "s|^pkgver=.*$|pkgver=${SEMVER_STR}|" PKGBUILD
sed -i -e 's|^pkgrel=.*$|pkgrel=1|' PKGBUILD
sudo -u builder updpkgsums
sudo -u builder makepkg --printsrcinfo > .SRCINFO
- name: Build package with makepkg
run: >
sudo -u builder
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
makepkg --syncdeps --cleanbuild --noconfirm --log
- name: Commit and push
run: |
git config user.name "cryptobot"
git config user.email "cryptobot@users.noreply.github.com"
git config push.autoSetupRemote true
git stage PKGBUILD .SRCINFO
git commit -m "Prepare release ${SEMVER_STR}"
git push
- name: Create pull request
id: create-pr
run: |
printf "Created by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" > pr_body.md
PR_URL=$(gh pr create --title "Release ${SEMVER_STR}" --body-file pr_body.md)
echo "url=$PR_URL" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "AUR-bin release PR for ${{ github.event.repository.name }} ${{ needs.get-version.outputs.semVerStr }} created."
SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.url }}|PR> on how to proceed."
SLACK_FOOTER: false
MSG_MINIMAL: true
read -ra X64_SUM < <(sha256sum appimage-artifacts/appimage-x86_64/cryptomator-*-x86_64.AppImage)
read -ra AARCH64_SUM < <(sha256sum appimage-artifacts/appimage-aarch64/cryptomator-*-aarch64.AppImage)
echo "x64-sha256sum=${X64_SUM[0]}" >> "$GITHUB_OUTPUT"
echo "aarch64-sha256sum=${AARCH64_SUM[0]}" >> "$GITHUB_OUTPUT"

115
.github/workflows/aur-bin.yml vendored Normal file
View File

@@ -0,0 +1,115 @@
name: PR for aur-bin repo
on:
release:
types: [published]
workflow_dispatch:
inputs:
src-tag:
description: 'Source or Release tag'
required: false
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.src-tag }}
create-aur-bin-pr:
name: Create PR for aur-bin repo
if: (github.event_name == 'workflow_dispatch') || (github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable')
runs-on: ubuntu-latest
needs: [get-version]
container:
image: archlinux:base-devel
env:
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
PKGDEST: ${{ github.workspace }}/pkgdest
SRCDEST: ${{ github.workspace }}/srcdest
steps:
- name: Prepare pacman
run: |
pacman-key --init
pacman-key --populate archlinux
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip github-cli curl pacman-contrib
- name: Checkout cryptomator/aur-bin
uses: actions/checkout@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: 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@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
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

View File

@@ -37,7 +37,7 @@ on:
jobs:
download-file:
name: Downloads the file into the VM
runs-on: ubuntu-latest
runs-on: ubuntu-slim
outputs:
fileName: ${{ steps.extractName.outputs.fileName}}
env:
@@ -51,19 +51,19 @@ 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@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
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-latest
runs-on: ubuntu-slim
needs: download-file
if: inputs.kaspersky
steps:
- name: Download artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ needs.download-file.outputs.fileName }}
path: upload
@@ -78,12 +78,12 @@ jobs:
local-dir: ./upload/
allowlist-avast:
name: Anti Virus Allowlisting Avast
runs-on: ubuntu-latest
runs-on: ubuntu-slim
needs: download-file
if: inputs.avast
steps:
- name: Download artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ needs.download-file.outputs.fileName }}
path: upload

View File

@@ -29,7 +29,7 @@ jobs:
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Cache SonarCloud packages
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
@@ -47,40 +47,3 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Draft a release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
draft: true
discussion_category_name: releases
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
generate_release_notes: true
body: |-
> [!NOTE]
> 🚧 Work in Progress 🚧
>
> Please be patient, the [builds are still running](https://github.com/cryptomator/cryptomator/actions). Binary packages can be found here in a few moments.
<!--REPLACE with auto-generated release notes (see below)
### What's New 🎉
### Bugfixes 🐛
### Other Changes 📎
END REPLACE-->
Feel free to also read our [CHANGELOG.md](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: -->

View File

@@ -74,10 +74,10 @@ jobs:
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON: ''
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "JDK update available"
SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ steps.determine.outputs.LATEST_JDK_VERSION }}. Check the Nextcloud collective for instructions."
SLACK_FOOTER: false
SLACK_FOOTER: ''
MSG_MINIMAL: true

View File

@@ -1,6 +1,8 @@
name: Build Debian Package
on:
schedule:
- cron: '0 22 20 * *'
workflow_dispatch:
inputs:
semver:
@@ -143,7 +145,7 @@ jobs:
run: |
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: linux-deb-package
path: |

157
.github/workflows/draft-release.yml vendored Normal file
View File

@@ -0,0 +1,157 @@
name: Draft a Cryptomator Release
on:
push:
tags:
- '*'
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '25.0.2+10.0.LTS'
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: ${{ env.JAVA_DIST }}
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Build and Test
run: xvfb-run mvn -B verify -Plinux
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
- name: Draft a release
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_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@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- 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 }}

View File

@@ -1,85 +0,0 @@
name: Create PR for flathub
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag'
required: true
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.tag }}
tarball:
name: Determines tarball url and compute checksum
runs-on: ubuntu-latest
needs: [get-version]
if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable'
outputs:
url: ${{ steps.url.outputs.url}}
sha512: ${{ steps.sha512.outputs.sha512}}
steps:
- name: Determine tarball url
id: url
run: |
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz"
echo "url=${URL}" >> "$GITHUB_OUTPUT"
env:
TAG: ${{ inputs.tag || github.event.release.tag_name}}
- name: Download source tarball and compute checksum
id: sha512
run: |
curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" ${{ steps.url.outputs.url }} --output cryptomator.tar.gz
TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1)
echo "sha512=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT"
flathub:
name: Create PR for flathub
runs-on: ubuntu-latest
needs: [tarball, get-version]
env:
FLATHUB_PR_URL: tbd
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: 'flathub/org.cryptomator.Cryptomator'
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Checkout release branch
run: |
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
- name: Update build file
run: |
sed -i -e 's/VERSION: [0-9]\+\.[0-9]\+\.[0-9]\+.*/VERSION: ${{ needs.get-version.outputs.semVerStr }}/g' org.cryptomator.Cryptomator.yaml
sed -i -e 's/sha512: [0-9A-Za-z_\+-]\{128\} #CRYPTOMATOR/sha512: ${{ needs.tarball.outputs.sha512 }} #CRYPTOMATOR/g' org.cryptomator.Cryptomator.yaml
sed -i -e 's;url: https://github.com/cryptomator/cryptomator/archive/refs/tags/[^[:blank:]]\+;url: ${{ needs.tarball.outputs.url }};g' org.cryptomator.Cryptomator.yaml
- name: Commit and push
run: |
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
git config push.autoSetupRemote true
git stage .
git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}"
git push
- name: Create pull request
run: |
printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update maven dependencies\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md
PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md)
echo "FLATHUB_PR_URL=$PR_URL" >> "$GITHUB_ENV"
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
if: github.event_name == 'release'
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "Flathub release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created."
SLACK_MESSAGE: "See <${{ env.FLATHUB_PR_URL }}|PR> on how to proceed.>."
SLACK_FOOTER: false
MSG_MINIMAL: true

View File

@@ -14,6 +14,9 @@ on:
semVerNum:
description: "The numerical part of the version string"
value: ${{ jobs.determine-version.outputs.semVerNum}}
semVerSuffix:
description: "The suffix of the version string"
value: ${{ jobs.determine-version.outputs.semVerSuffix}}
revNum:
description: "The revision number"
value: ${{ jobs.determine-version.outputs.revNum}}
@@ -32,6 +35,7 @@ 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:
@@ -54,25 +58,30 @@ jobs:
else
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_NUM=$(echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
SEM_VER_SUFFIX="${SEM_VER_STR#"$SEM_VER_NUM"}"
REVCOUNT=`git rev-list --count HEAD`
TYPE="unknown"
if [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+$ ]]; then
if [[ -z $SEM_VER_SUFFIX ]]; then
TYPE="stable"
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-alpha[1-9]+$ ]]; then
elif [[ $SEM_VER_SUFFIX =~ -alpha[1-9]+$ ]]; then
TYPE="alpha"
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-beta[1-9]+$ ]]; then
elif [[ $SEM_VER_SUFFIX =~ -beta[1-9]+$ ]]; then
TYPE="beta"
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-rc[1-9]$ ]]; then
elif [[ $SEM_VER_SUFFIX =~ -rc[1-9]+$ ]]; then
TYPE="rc"
fi
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
echo "semVerSuffix=${SEM_VER_SUFFIX}" >> $GITHUB_OUTPUT
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
echo "type=${TYPE}" >> $GITHUB_OUTPUT
env:
VERSION_STRING: ${{ inputs.version }}
- name: Validate Version
uses: skymatic/semver-validation-action@7a6ae1c9e121540d11c9c7e4e667c83d583aa153 # v3.0.0
uses: skymatic/semver-validation-action@7c80b6b03a18b42884761daa9862ff5683ec8c8a # v4.0.0
with:
version: ${{ steps.versions.outputs.semVerStr }}

264
.github/workflows/linux-flatpak.yml vendored Normal file
View File

@@ -0,0 +1,264 @@
name: Build flatpak
on:
release:
types: [published]
workflow_dispatch:
inputs:
src-tag:
description: 'Source or Release tag'
required: false
create-pr:
description: 'Create Flathub PR'
required: false
type: boolean
default: false
push:
branches-ignore:
- 'dependabot/**'
paths:
- '.github/workflows/get-version.yml'
- '.github/workflows/linux-flatpak.yml'
- 'dist/linux/flatpak/**'
- 'dist/linux/common/**'
- 'dist/linux/resources/**'
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.src-tag }}
build-flatpak:
name: "Build flatpak"
needs: [get-version]
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08
options: --privileged
strategy:
fail-fast: false
matrix:
variant:
- arch: x86_64
runner: ubuntu-24.04
- arch: aarch64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.variant.runner }}
permissions:
contents: read
env:
SRC_GIT_SHA: ${{ inputs.src-tag || github.sha}}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: flathub/org.cryptomator.Cryptomator
submodules: true
- name: Checkout build script
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: build-scripts
- name: Checkout app source
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
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@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
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

View File

@@ -3,6 +3,8 @@ name: Build Arch package
on:
release:
types: [published]
schedule:
- cron: '0 21 20 * *'
workflow_dispatch:
inputs:
version:
@@ -67,13 +69,13 @@ jobs:
sudo -u builder
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
makepkg --syncdeps --cleanbuild --noconfirm --log
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
- 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@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: pkgbuild-file
if-no-files-found: error
@@ -130,7 +132,6 @@ jobs:
- name: Determine pkgrel
id: pkgrel
run: |
TARGET_VERSION='${{ needs.get-version.outputs.semVerStr }}'
CURRENT_VERSION="$(sed -nE 's/^pkgver=(.*)$/\1/p' PKGBUILD | head -n1)"
CURRENT_REL="$(sed -nE 's/^pkgrel=([0-9]+).*$/\1/p' PKGBUILD | head -n1)"
@@ -141,11 +142,11 @@ jobs:
fi
echo "value=${NEXT_REL}" >> "$GITHUB_OUTPUT"
echo "dist-version=${VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT"
echo "dist-version=${TARGET_VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT"
env:
VERSION: ${{ needs.get-version.outputs.semVerStr }}
TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }}
- name: Download PKGBUILD template
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: pkgbuild-file
- name: Prepare PKGBUILD
@@ -191,10 +192,10 @@ jobs:
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON: ''
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "AUR release PR created for ${{ github.event.repository.name }} ${{ steps.pkgrel.outputs.dist-version }} ."
SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.url }}|PR> on how to proceed."
SLACK_FOOTER: false
SLACK_FOOTER: ''
MSG_MINIMAL: true

View File

@@ -9,13 +9,45 @@ name: Build macOS .dmg for x64
#######################################
on:
release:
types: [published]
schedule:
- cron: '0 20 20 * *'
workflow_call:
inputs:
semVerNum:
type: string
description: 'The Major.Minor.Patch part of the version'
required: true
revisionNum:
type: string
description: 'The revision number'
required: true
semVerSuffix:
type: string
description: 'The suffix of the version, including dash'
required: true
notarize:
description: 'Notarize'
default: true
type: boolean
upload-to-draft:
type: boolean
default: true
outputs:
sha256-dmg:
description: "SHA256 sum of the x64 dmg"
value: ${{ jobs.build.outputs.sha256sum}}
workflow_dispatch:
inputs:
version:
description: 'Version'
semVerNum:
description: 'The Major.Minor.Patch part of the version'
required: false
revisionNum:
description: 'The revision number'
required: false
semVerSuffix:
description: 'The suffix of the version, including dash'
required: false
default: '-SNAPSHOT'
notarize:
description: 'Notarize'
required: true
@@ -25,17 +57,17 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '25.0.2+10.0.LTS'
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.version }}
build-arm:
build:
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
runs-on: ${{ matrix.os }}
needs: [get-version]
outputs:
sha256sum: ${{ steps.sha256sum.outputs.value }}
strategy:
fail-fast: false
matrix:
@@ -77,7 +109,7 @@ jobs:
exit 1
fi
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
run : mvn versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
- name: Run maven
run: mvn -B clean package -Pmac -DskipTests
- name: Patch target dir
@@ -117,8 +149,8 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
--java-options "-Xss5m"
@@ -127,7 +159,7 @@ jobs:
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
@@ -137,7 +169,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-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NUM}\""
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources
- name: Patch Cryptomator.app
@@ -145,12 +178,10 @@ jobs:
mv appdir/Cryptomator.app Cryptomator.app
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NUM}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
env:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
- name: Generate license for dmg
run: >
@@ -239,16 +270,14 @@ jobs:
--eula "dist/mac/dmg/resources/license.rtf"
--icon ".background" 128 758
--icon ".VolumeIcon.icns" 512 758
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
env:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
- name: Codesign .dmg
run: |
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
env:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
if: inputs.notarize || github.event_name == 'schedule'
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
with:
app-path: 'Cryptomator-*.dmg'
@@ -256,8 +285,12 @@ jobs:
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
xcode-path: '/Applications/Xcode_16.app'
- id: sha256sum
run: |
read -ra CMD_OUTPUT < <(shasum -a256 Cryptomator-*.dmg)
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
- name: Add possible alpha/beta tags to installer name
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
run: mv Cryptomator-*.dmg "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg"
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -270,7 +303,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: dmg-${{ matrix.output-suffix }}
path: |
@@ -278,9 +311,10 @@ jobs:
Cryptomator-*.asc
if-no-files-found: error
- name: Publish dmg on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
if: inputs.upload-to-draft
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
draft: true
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |

View File

@@ -1,13 +1,45 @@
name: Build macOS .dmg for arm64
on:
release:
types: [published]
schedule:
- cron: '0 20 20 * *'
workflow_call:
inputs:
semVerNum:
type: string
description: 'The Major.Minor.Patch part of the version'
required: true
revisionNum:
type: string
description: 'The revision number'
required: true
semVerSuffix:
type: string
description: 'The suffix of the version, including dash'
required: true
notarize:
description: 'Notarize'
default: true
type: boolean
upload-to-draft:
type: boolean
default: true
outputs:
sha256-dmg:
description: "SHA256 sum of the arm64 dmg"
value: ${{ jobs.build.outputs.sha256sum}}
workflow_dispatch:
inputs:
version:
description: 'Version'
semVerNum:
description: 'The Major.Minor.Patch part of the version'
required: false
revisionNum:
description: 'The revision number'
required: false
semVerSuffix:
description: 'The suffix of the version, including dash'
required: false
default: '-SNAPSHOT'
notarize:
description: 'Notarize'
required: true
@@ -23,17 +55,17 @@ on:
env:
JAVA_DIST: 'temurin'
JAVA_VERSION: '25.0.2+10.0.LTS'
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.version }}
build:
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
runs-on: ${{ matrix.os }}
needs: [get-version]
outputs:
sha256sum: ${{ steps.sha256sum.outputs.value }}
strategy:
fail-fast: false
matrix:
@@ -75,7 +107,7 @@ jobs:
exit 1
fi
- name: Set version
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
run : mvn versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
- name: Run maven
run: mvn -B clean package -Pmac -DskipTests
- name: Patch target dir
@@ -115,8 +147,8 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
--java-options "-Xss5m"
@@ -125,7 +157,7 @@ jobs:
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
@@ -135,8 +167,9 @@ jobs:
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NUM}\""
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
--mac-package-identifier org.cryptomator
--resource-dir dist/mac/resources
- name: Patch Cryptomator.app
@@ -144,12 +177,10 @@ jobs:
mv appdir/Cryptomator.app Cryptomator.app
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NUM}|g" Cryptomator.app/Contents/Info.plist
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
env:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
- name: Generate license for dmg
run: >
@@ -238,16 +269,14 @@ jobs:
--eula "dist/mac/dmg/resources/license.rtf"
--icon ".background" 128 758
--icon ".VolumeIcon.icns" 512 758
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
env:
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
- name: Codesign .dmg
run: |
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
env:
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
if: inputs.notarize || github.event_name == 'schedule'
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
with:
app-path: 'Cryptomator-*.dmg'
@@ -255,8 +284,12 @@ jobs:
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
xcode-path: '/Applications/Xcode_16.app'
- id: sha256sum
run: |
read -ra CMD_OUTPUT < <(shasum -a256 Cryptomator-*.dmg)
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
- name: Add possible alpha/beta tags to installer name
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
run: mv Cryptomator-*.dmg "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg"
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -269,7 +302,7 @@ jobs:
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: dmg-${{ matrix.output-suffix }}
path: |
@@ -277,9 +310,10 @@ jobs:
Cryptomator-*.asc
if-no-files-found: error
- name: Publish dmg on GitHub Releases
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
if: inputs.upload-to-draft
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
draft: true
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |

View File

@@ -7,12 +7,12 @@ on:
jobs:
no-response:
runs-on: ubuntu-latest
runs-on: ubuntu-slim
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
days-before-stale: 14
days-before-close: 0

View File

@@ -5,35 +5,141 @@ on:
types: [published]
jobs:
get-version:
runs-on: ubuntu-latest
notify:
runs-on: ubuntu-slim
steps:
- name: Download source tarball
run: |
curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz --output cryptomator-${{ github.event.release.tag_name }}.tar.gz
- name: Sign source tarball with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
- name: Notify about DEB build
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
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
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/debian.yml|build deb Package>."
SLACK_FOOTER: ''
MSG_MINIMAL: true
- name: Notify about latest-version update
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
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@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON: ''
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/debian.yml|build deb Package>."
SLACK_FOOTER: false
MSG_MINIMAL: true
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) }} }'

View File

@@ -50,7 +50,7 @@ jobs:
exit 1
fi
- name: Cache NVD DB
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ~/.m2/repository/org/owasp/dependency-check-data/
key: dependency-check-${{ github.run_id }}

View File

@@ -7,12 +7,12 @@ on:
jobs:
stale:
runs-on: ubuntu-latest
runs-on: ubuntu-slim
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
days-before-stale: 365
days-before-close: 90

View File

@@ -1,13 +1,48 @@
name: Build Windows Installer
on:
release:
types: [published]
schedule:
- cron: '0 19 20 * *'
workflow_call:
inputs:
semVerNum:
type: string
description: 'The Major.Minor.Patch part of the version'
required: true
revisionNum:
type: string
description: 'The revision number'
required: true
semVerSuffix:
type: string
description: 'The suffix of the version, including dash'
required: true
sign:
description: 'Sign binaries'
default: true
type: boolean
upload-to-draft:
type: boolean
default: true
outputs:
sha256-msi:
description: "SHA256 sum of the x64 msi"
value: ${{ jobs.build-msi.outputs.sha256sum}}
sha256-exe:
description: "SHA256 sum of the x64 exe"
value: ${{ jobs.build-exe.outputs.sha256sum}}
workflow_dispatch:
inputs:
version:
description: 'Version'
semVerNum:
description: 'The Major.Minor.Patch part of the version'
required: false
revisionNum:
description: 'The revision number'
required: false
semVerSuffix:
description: 'The suffix of the version, including dash'
required: false
default: '-SNAPSHOT'
sign:
description: 'Sign binaries'
required: false
@@ -22,6 +57,9 @@ 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.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'
@@ -34,15 +72,11 @@ defaults:
shell: bash
jobs:
get-version:
uses: ./.github/workflows/get-version.yml
with:
version: ${{ inputs.version }}
build-msi:
name: Build .msi Installer
runs-on: ${{ matrix.os }}
needs: [ get-version ]
outputs:
sha256sum: ${{ steps.sha256sum.outputs.value }}
strategy:
matrix:
include:
@@ -94,7 +128,7 @@ jobs:
exit 1
fi
- name: Set version
run: mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
run: mvn versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
- name: Run maven
run: mvn -B clean package -Pwin -DskipTests
- name: Patch target dir
@@ -134,13 +168,13 @@ jobs:
--dest appdir
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}.${REVISION_NUM}"
--java-options "--enable-preview"
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Djava.net.useSystemProxies=true"
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
@@ -151,12 +185,13 @@ jobs:
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
--java-options "-Dcryptomator.loopbackAlias=\"cryptomator-vault\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
--java-options "-Dcryptomator.buildNumber=\"msi-${REVISION_NUM}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json\""
--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"
@@ -192,7 +227,7 @@ jobs:
& $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 == 'release'
if: inputs.sign || github.event_name == 'schedule'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\appdir
@@ -201,17 +236,6 @@ jobs:
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign DLLs with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
with:
base-dir: 'appdir'
file-extensions: 'dll,exe,ps1'
recursive: true
sign-description: 'Cryptomator'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
- name: Replace DLLs inside jars with signed ones
shell: pwsh
run: |
@@ -247,8 +271,8 @@ jobs:
--dest installer
--name Cryptomator
--vendor "Skymatic GmbH"
--copyright "(C) 2016 - 2025 Skymatic GmbH"
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
--copyright "(C) 2016 - 2026 Skymatic GmbH"
--app-version "${VERSION_NUM}.${REVISION_NUM}"
--win-menu
--win-dir-chooser
--win-shortcut-prompt
@@ -261,7 +285,7 @@ jobs:
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
- name: Sign MSI with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
if: inputs.sign || github.event_name == 'schedule'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\installer
@@ -270,8 +294,12 @@ jobs:
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- id: sha256sum
run: |
read -ra CMD_OUTPUT < <(sha256sum installer/Cryptomator-*.msi)
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
- name: Add possible alpha/beta tags and architecture to installer name
run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.arch }}.msi
run: mv installer/Cryptomator-*.msi "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.msi"
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -280,7 +308,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: msi-${{ matrix.arch }}
path: |
@@ -291,7 +319,9 @@ jobs:
build-exe:
name: Build .exe installer
runs-on: ${{ matrix.os }}
needs: [ get-version, build-msi ]
needs: [ build-msi ]
outputs:
sha256sum: ${{ steps.sha256sum.outputs.value }}
strategy:
matrix:
include:
@@ -311,7 +341,7 @@ jobs:
env:
WIX_VERSION: ${{ env.WIX_VERSION }}
- name: Download .msi
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: msi-${{ matrix.arch }}
path: dist/win/bundle/resources
@@ -338,10 +368,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'
@@ -355,21 +385,22 @@ jobs:
run: >
wix build
-define BundleName="Cryptomator"
-define BundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
-define BundleVersion="${VERSION_NUM}.${REVISION_NUM}"
-define BundleVendor="Skymatic GmbH"
-define BundleCopyright="(C) 2016 - 2025 Skymatic GmbH"
-define BundleCopyright="(C) 2016 - 2026 Skymatic GmbH"
-define AboutUrl="https://cryptomator.org"
-define HelpUrl="https://cryptomator.org/contact"
-define UpdateUrl="https://cryptomator.org/downloads/"
-ext "WixToolset.Util.wixext"
-ext "WixToolset.BootstrapperApplications.wixext"
./bundle/bundleWithWinfsp.wxs
-out "../../installer/unsigned/Cryptomator-Installer.exe"
-out "../../installer/Cryptomator-Installer.exe"
- name: Detach burn engine in preparation to sign
if: inputs.sign || github.event_name == 'schedule'
run: >
wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe
wix burn detach installer/Cryptomator-Installer.exe -engine tmp/engine.exe
- name: Sign WiX burn engine with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
if: inputs.sign || github.event_name == 'schedule'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\tmp
@@ -379,21 +410,14 @@ jobs:
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign burn engine with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
with:
base-dir: 'tmp'
file-extensions: 'exe'
sign-description: 'Cryptomator Bundle Installer'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
- name: Reattach signed burn engine to installer
run: >
wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
if: inputs.sign || github.event_name == 'schedule'
shell: pwsh
run: |
Move-Item -Path installer/Cryptomator-Installer.exe -Destination tmp/Cryptomator-Installer.exe
wix burn reattach tmp/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
- name: Sign EXE installer with Azure Trusted Signing
if: inputs.sign || github.event_name == 'release'
if: inputs.sign || github.event_name == 'schedule'
uses: ./.github/actions/win-sign-action
with:
base-dir: ${{ github.workspace }}\installer
@@ -403,18 +427,12 @@ jobs:
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
client-id: ${{ secrets.AZURE_CLIENT_ID }}
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Sign installer with Actalis CodeSigner
if: inputs.sign || github.event_name == 'release'
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
with:
base-dir: 'installer'
file-extensions: 'exe'
sign-description: 'Cryptomator Bundle Installer'
sign-url: 'https://cryptomator.org'
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
password: ${{ secrets.WIN_CODESIGN_PW }}
- id: sha256sum
run: |
read -ra CMD_OUTPUT < <(sha256sum installer/Cryptomator-*.exe)
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
- name: Add possible alpha/beta tags to installer name
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.executable-suffix }}.exe
run: mv installer/Cryptomator-Installer.exe "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.executable-suffix }}.exe"
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -423,7 +441,7 @@ jobs:
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
- name: Upload artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: exe-${{ matrix.executable-suffix }}
path: |
@@ -433,58 +451,22 @@ jobs:
publish:
name: Publish installers to the github release
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
if: inputs.upload-to-draft
runs-on: ubuntu-latest
needs: [ build-msi, build-exe ]
outputs:
download-url-msi-x64: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }}
download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }}
steps:
- name: Download installers
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
merge-multiple: true
- name: Publish installers on GitHub Releases
id: publish
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
draft: true
fail_on_unmatched_files: true
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
# do not change ordering of filelist, required for correct job output
files: |
*x64.msi
*x64.exe
*.asc
allowlist-msi-x64:
uses: ./.github/workflows/av-whitelist.yml
needs: [ publish ]
with:
url: ${{ needs.publish.outputs.download-url-msi-x64 }}
secrets: inherit
allowlist-exe-x64:
uses: ./.github/workflows/av-whitelist.yml
needs: [ publish, allowlist-msi-x64 ]
with:
url: ${{ needs.publish.outputs.download-url-exe-x64 }}
secrets: inherit
notify-winget:
name: Notify for winget-release
if: needs.get-version.outputs.versionType == 'stable'
needs: [publish, get-version]
runs-on: ubuntu-latest
steps:
- name: Slack Notification
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: 'Cryptobot'
SLACK_ICON: false
SLACK_ICON_EMOJI: ':bot:'
SLACK_CHANNEL: 'cryptomator-desktop'
SLACK_TITLE: "MSI packages of ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/winget.yml| release them to winget>."
SLACK_FOOTER: false
MSG_MINIMAL: true

View File

@@ -18,7 +18,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
- name: Submit package
uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # no_specific_version
uses: vedantmgoyal2009/winget-releaser@7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 # no_specific_version
with:
identifier: Cryptomator.Cryptomator
version: ${{ inputs.tag }}

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 -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 -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" />
<method v="2">
<option name="Make" enabled="true" />
</method>

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 -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 -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" />
<method v="2">
<option name="Make" enabled="true" />
</method>

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 -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 -Dcryptomator.hub.enableTrustOnFirstUse=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>

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 -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 -Dcryptomator.hub.enableTrustOnFirstUse=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>

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 -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 -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>

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 -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 -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
<method v="2">
<option name="Make" enabled="true" />
</method>

View File

@@ -7,7 +7,45 @@ 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).
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.18.0...HEAD)
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.19.2...HEAD)
### Changed
* Refactored release pipeline to allow immutable releases ([#4205](https://github.com/cryptomator/cryptomator/pull/4205))
## [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
### Added
* Self-Update Mechanism ([#3948](https://github.com/cryptomator/cryptomator/pull/3948))
@@ -20,31 +58,33 @@ Changes to prior versions can be found on the [Github release page](https://gith
* Support automatic app theme selection according to OS theme on Linux ([#4027](https://github.com/cryptomator/cryptomator/issues/4027))
* Admin configuration: Allow overwriting certain app properties by external config file ([#4105](https://github.com/cryptomator/cryptomator/pull/4105))
* New keychain backend using [secret service API](https://specifications.freedesktop.org/secret-service/0.2) for Linux ([#4025](https://github.com/cryptomator/cryptomator/pull/4025))
* Liquid Glass icon for macOS ([#4166](https://github.com/cryptomator/cryptomator/pull/4166))
### Fixed
* Fixed password reset/show recovery possible for vaults without masterkey file ([#4120](https://github.com/cryptomator/cryptomator/pull/4120))
* Fixed restore vault config failed due to selecting a directory instead of file ([#4141](https://github.com/cryptomator/cryptomator/issues/4141))
* Fixed leaking of cleartext paths into application log ([GHSA-j83j-mwhc-rcgw](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-j83j-mwhc-rcgw))
### Changed
* Disable user defined app start config on Windows ([#4132](https://github.com/cryptomator/cryptomator/issues/4132))
* 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.31
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.0
* `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.0
* `ch.qos.logback:*` from 1.5.19 to 1.5.32
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.1
* `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.1
* `com.github.ben-manes.caffeine:caffeine` from 3.2.2 to 3.2.3
* `com.google.dagger:*` from 2.57.2 to 2.59.1
* `com.google.dagger:*` from 2.57.2 to 2.59.2
* `org.apache.commons:commons-lang3` from 3.19.0 to 3.20.0
* `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0-beta3
* `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0
* `org.cryptomator:cryptolib` from 2.2.1 to 2.2.2
* `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.0
* `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.1
* `org.cryptomator:integrations-api` from 1.7.0 to 1.8.0-beta1
* `org.cryptomator:integrations-linux` from 1.6.1 to 1.7.0-beta4
* `org.cryptomator:integrations-mac` from 1.4.1 to 1.5.0-beta3
* `org.cryptomator:integrations-win` from 1.5.1 to 1.6.0
* `org.cryptomator:webdav-nio-adapter` from 3.0.0 to 3.0.1
* `org.cryptomator:webdav-nio-adapter-servlet` to 1.2.12

View File

@@ -82,7 +82,7 @@ ${JAVA_HOME}/bin/jpackage \
--vendor "Skymatic GmbH" \
--java-options "--enable-preview" \
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
--copyright "(C) 2016 - 2026 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--app-version "${VERSION}.${REVISION_NO}" \
@@ -99,6 +99,7 @@ ${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
@@ -123,7 +124,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/continuous/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
curl -L https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
chmod +x /tmp/appimagetool.AppImage
# create AppImage

View File

@@ -73,6 +73,7 @@
<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>
@@ -83,6 +84,15 @@
</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>

View File

@@ -46,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 - 2025 Skymatic GmbH" \
--copyright "(C) 2016 - 2026 Skymatic GmbH" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--java-options "-Dfile.encoding=\"utf-8\"" \
@@ -64,6 +64,7 @@ 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

View File

@@ -0,0 +1,15 @@
#!/bin/sh
# From: https://gitlab.gnome.org/GNOME/gnome-builder/-/blob/main/build-aux/flatpak/fusermount-wrapper.sh
if [ -z "$_FUSE_COMMFD" ]; then
FD_ARGS=
else
FD_ARGS="--env=_FUSE_COMMFD=${_FUSE_COMMFD} --forward-fd=${_FUSE_COMMFD}"
fi
if [ -e /proc/self/fd/3 ] && [ 3 != "$_FUSE_COMMFD" ]; then
FD_ARGS="$FD_ARGS --forward-fd=3"
fi
exec flatpak-spawn --host --forward-fd=1 --forward-fd=2 $FD_ARGS fusermount3 "$@"

View File

@@ -0,0 +1,182 @@
app-id: org.cryptomator.Cryptomator
command: cryptomator
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
separate-locales: false
finish-args:
# Required for FUSE, see https://github.com/flathub/org.cryptomator.Cryptomator/pull/68#issuecomment-1935136502
- --device=all
# Set the PATH environment variable in the application, as flatpak is resetting the shell's PATH
- --env=PATH=/app/bin/:/usr/bin/
# Allow filesystem access to the user's home dir
# Needed to manage vaults there
- --filesystem=home
# Reading system certificates
- --filesystem=host-etc:ro
# Allow access to the XDG data directory
# Needed to connect to KeePassXC's UNIX domain socket
- --filesystem=xdg-run/org.keepassxc.KeePassXC.BrowserServer
- --filesystem=xdg-run/app/org.keepassxc.KeePassXC/
# Share IPC namespace with the host, without it the X11 shared memory extension will not work
- --share=ipc
# Allow access to the network
- --share=network
# Show windows using X11
- --socket=x11
# Needed to reveal encrypted files
- --talk-name=org.freedesktop.FileManager1
# Run any command on the host
# Needed to spawn fusermount on the host
- --talk-name=org.freedesktop.Flatpak
# Allow desktop notifications
- --talk-name=org.freedesktop.Notifications
# Allow access to the GNOME secret service API and to talk to the GNOME keyring daemon
- --talk-name=org.freedesktop.secrets
- --talk-name=org.gnome.keyring
# Allow to talk to the KDE kwallet daemon
- --talk-name=org.kde.kwalletd5
- --talk-name=org.kde.kwalletd6
# Needed to talk to the gvfs daemons over D-Bus and list mounts using the GIO APIs
- --talk-name=org.gtk.vfs.*
# Allow access to appindicator icons
- --talk-name=org.ayatana
# Allow access to appindicator icons on KDE
- --talk-name=org.kde.StatusNotifierWatcher
cleanup:
- /include
- /lib/pkgconfig
modules:
- shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json
- name: libfuse
buildsystem: meson
config-opts:
- -Dexamples=false
- -Dinitscriptdir=
- -Duseroot=false
- -Dtests=false
# don't install rules on the host
- -Dudevrulesdir=/tmp/
sources:
- type: archive
url: https://github.com/libfuse/libfuse/releases/download/fuse-3.16.2/fuse-3.16.2.tar.gz
sha256: f797055d9296b275e981f5f62d4e32e089614fc253d1ef2985851025b8a0ce87
x-checker-data:
type: anitya
project-id: 861
url-template: https://github.com/libfuse/libfuse/releases/download/fuse-$version/fuse-$version.tar.gz
versions: {<: '3.17.0'}
- name: host-command-wrapper
buildsystem: simple
build-commands:
- install fusermount-wrapper.sh /app/bin/fusermount3
sources:
- type: file
path: build-aux/fusermount-wrapper.sh
- name: cryptomator
buildsystem: simple
build-options:
build-args:
- --share=network
env:
PATH: /app/bin:/usr/bin
MAVEN_OPTS: -Dmaven.repo.local=.m2/repository
JAVA_HOME: jdk
JMODS_PATH: jmods
VERSION: $FLATPAK_VERSION
REVISION_NO: '$FLATPAK_REVISION'
build-commands:
# Setup Java
- tar xvfz jdk.tar.gz --transform 's!^[^/]*!jdk!'
- mkdir jmods
- unzip -j openjfx.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods
# Setup Maven
- mkdir maven
- tar xf maven.tar.gz --strip-components=1 --exclude=jansi-native --directory=maven
# Build project
- maven/bin/mvn clean package -DskipTests -P"linux-$(uname -m)"
- 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/temurin25-binaries/releases/download/jdk-25.0.2%2B10/OpenJDK25U-jdk_x64_linux_hotspot_25.0.2_10.tar.gz
sha512: 29043fde119a031c2ca8d57aed445fedd9e7f74608fcdc7a809076ba84cfd1c31f08de2ecccf352e159fdcd1cae172395ed46363007552ff242057826c81ab3a
- type: file
dest-filename: jdk.tar.gz
only-arches:
- aarch64
url: https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.2%2B10/OpenJDK25U-jdk_aarch64_linux_hotspot_25.0.2_10.tar.gz
sha512: f1d3ccec3e1f1bed9d632f14b9223709d6e5c2e0d922125d068870dd3016492a2ca8f08924d4a9d0dc5eb2159fa09efee366a748fd0093475baf29e5c70c781a
- type: file
dest-filename: openjfx.zip
only-arches:
- x86_64
url: https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip
sha512: 21f550217101c513f9eb1d7947eba30cb79618238e6539ce770e54e84b01574cdaeba40af602391145f163dd8e43e3794395467413152f13ffffeff948b0ca1b
- type: file
dest-filename: openjfx.zip
only-arches:
- aarch64
url: https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip
sha512: a9268409b3803e386490bf1319d0f0a14173cebe862c12254cd51b430ee0a297437d9e38d5ebeae0da8899be898b312b103330d09dcfd3e63c1e7d15f2f14311
- 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'}

View File

@@ -94,6 +94,7 @@ 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
}

View File

@@ -24,7 +24,7 @@ rm -rf runtime dmg *.app *.dmg
# set variables
APP_NAME="Cryptomator"
VENDOR="Skymatic GmbH"
COPYRIGHT_YEARS="2016 - 2025"
COPYRIGHT_YEARS="2016 - 2026"
PACKAGE_IDENTIFIER="org.cryptomator"
MAIN_JAR_GLOB="cryptomator-*.jar"
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
@@ -125,6 +125,7 @@ ${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

1
dist/win/build.ps1 vendored
View File

@@ -167,6 +167,7 @@ $javaOptions = @(
"--java-options", "-Dcryptomator.showTrayIcon=true"
"--java-options", "-Dcryptomator.buildNumber=`"msi-$revisionNo`""
"--java-options", "-Dcryptomator.disableUpdateCheck=false"
"--java-options", "-Dcryptomator.hub.enableTrustOnFirstUse=true"
)

View File

@@ -4,15 +4,18 @@
:: This file must be located in the INSTALLDIR
set "LOOPBACK_ALIAS=%1"
set "ACTION=%2"
if "%ACTION%"=="" set "ACTION=install"
:: Log for debugging
echo LOOPBACK_ALIAS=%LOOPBACK_ALIAS%
echo ACTION=%ACTION%
:: Change to INSTALLDIR
cd %~dp0
:: Execute the PowerShell script
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File .\patchWebDAV.ps1^
-LoopbackAlias %LOOPBACK_ALIAS%
-LoopbackAlias %LOOPBACK_ALIAS% -Action %ACTION%
:: Return the exit code from PowerShell
exit /b %ERRORLEVEL%

View File

@@ -1,15 +1,17 @@
#Requires -RunAsAdministrator
Param(
[Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias
[Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias,
[string] $Action = "install"
)
New-Variable -Name "sysdir" -Value ([Environment]::SystemDirectory) -Option Constant -Scope Global
New-Variable -Name "hostsFile" -Value "$sysdir\drivers\etc\hosts" -Option Constant -Scope Global
# Adds an alias for 127.0.0.1 to the hosts file
function Add-AliasToHost {
param (
[string]$LoopbackAlias
)
$sysdir = [Environment]::SystemDirectory
$hostsFile = "$sysdir\drivers\etc\hosts"
$aliasLine = "127.0.0.1 $LoopbackAlias"
foreach ($line in Get-Content $hostsFile) {
@@ -18,9 +20,26 @@ function Add-AliasToHost {
}
}
Add-Content -Path $hostsFile -Encoding ascii -Value "`r`n$aliasLine"
$content = Get-Content $hostsFile
$content += "`r`n$aliasLine"
$content | Set-Content "$hostsFile.tmp" -Encoding ascii
Move-Item "$hostsFile.tmp" $hostsFile -Force
}
# Removes an alias for 127.0.0.1 from the hosts file
function Remove-AliasFromHost {
param (
[string]$LoopbackAlias
)
$aliasLine = "127.0.0.1 $LoopbackAlias"
$content = Get-Content $hostsFile
$newContent = $content | Where-Object { $_ -ne $aliasLine }
$newContent | Set-Content "$hostsFile.tmp" -Encoding ascii
Move-Item "$hostsFile.tmp" $hostsFile -Force
}
# Sets in the registry the webclient file size limit to the maximum value
function Set-WebDAVFileSizeLimit {
@@ -54,14 +73,20 @@ function Edit-ProviderOrder {
New-ItemProperty -Path $RegistryPath -Name $Name -Value $UpdatedOrder -PropertyType String -Force | Out-Null
}
if ($Action -eq "install") {
Add-AliasToHost $LoopbackAlias
Write-Output 'Ensured alias exists in hosts file'
Add-AliasToHost $LoopbackAlias
Write-Output 'Ensured alias exists in hosts file'
Set-WebDAVFileSizeLimit
Write-Output 'Set WebDAV file size limit'
Set-WebDAVFileSizeLimit
Write-Output 'Set WebDAV file size limit'
Edit-ProviderOrder
Write-Output 'Ensured correct provider order'
Edit-ProviderOrder
Write-Output 'Ensured correct provider order'
} elseif ($Action -eq "uninstall") {
Remove-AliasFromHost $LoopbackAlias
Write-Output 'Ensured alias removed from hosts file'
} else {
Write-Error "Invalid action: $Action. Only 'install' or 'uninstall' are valid."
}
exit 0

View File

@@ -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\cryptomator.config" Name="cryptomator.config" KeyPath="yes">
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\config.properties" Name="config.properties" KeyPath="yes">
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
<util:PermissionEx User="Administrators" GenericAll="yes"/>
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
@@ -158,9 +158,13 @@
<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;" Sequence="execute" Before="PatchWebDAV" />
<ns0:SetProperty Id="PatchWebDAV" Value="&quot;[INSTALLDIR]patchWebDAV.bat&quot; &quot;$(var.LoopbackAlias)&quot; install" 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"/>
@@ -214,9 +218,8 @@
<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,4 +231,4 @@
<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>

96
pom.xml
View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.19.0-SNAPSHOT</version>
<version>1.20.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -33,45 +33,46 @@
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.10.0-beta3</cryptomator.cryptofs.version>
<cryptomator.cryptofs.version>2.10.0</cryptomator.cryptofs.version>
<cryptomator.cryptolib.version>2.2.2</cryptomator.cryptolib.version>
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
<cryptomator.integrations.version>1.8.0</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.0</cryptomator.fuse.version>
<cryptomator.integrations.mac.version>1.5.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.7.0</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>6.0.1</cryptomator.fuse.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.3</caffeine.version>
<commons-lang3.version>3.20.0</commons-lang3.version>
<dagger.version>2.59.1</dagger.version>
<dagger.version>2.59.2</dagger.version>
<easybind.version>2.2</easybind.version>
<jackson.version>2.21.0</jackson.version>
<jackson.version>2.21.1</jackson.version>
<javafx.version>25.0.2</javafx.version>
<jwt.version>4.5.0</jwt.version>
<jwt.version>4.5.1</jwt.version>
<nimbus-jose.version>10.5</nimbus-jose.version>
<logback.version>1.5.31</logback.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>5.13.4</junit.jupiter.version>
<mockito.version>5.20.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.0.2-1</jetbrains.annotations.version>
<dependency-check.version>12.1.5</dependency-check.version>
<jetbrains.annotations.version>26.1.0</jetbrains.annotations.version>
<dependency-check.version>12.2.0</dependency-check.version>
<jacoco.version>0.8.14</jacoco.version>
<license-generator.version>2.7.0</license-generator.version>
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
<mvn-compiler.version>3.14.1</mvn-compiler.version>
<mvn-resources.version>3.3.1</mvn-resources.version>
<mvn-dependency.version>3.8.1</mvn-dependency.version>
<mvn-surefire.version>3.5.4</mvn-surefire.version>
<mvn-jar.version>3.4.2</mvn-jar.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.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 -->
<surefire.jacoco.args></surefire.jacoco.args>
@@ -93,6 +94,11 @@
<dependencies>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>webdav-nio-adapter-servlet</artifactId>
<version>${cryptomator.webdav-servlet.version}</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptolib</artifactId>
@@ -521,11 +527,57 @@
</profile>
<profile>
<id>linux</id>
<id>linux-aarch64</id>
<activation>
<os>
<family>unix</family>
<name>Linux</name>
<arch>aarch64</arch>
</os>
<property>
<name>idea.version</name>
</property>
</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>
</os>
<property>
<name>idea.version</name>

View File

@@ -13,6 +13,7 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.logging.LogbackConfiguratorFactory;
@@ -20,6 +21,7 @@ import org.cryptomator.networking.SSLContextProvider;
import org.cryptomator.networking.SSLContextWithMacKeychain;
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
import org.cryptomator.ui.fxapp.JfxRevealPathService;
import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider;
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
@@ -64,6 +66,7 @@ open module org.cryptomator.desktop {
uses org.cryptomator.event.NotificationHandler;
provides UiAppearanceProvider with JfxUiAppearanceProvider;
provides RevealPathService with JfxRevealPathService;
provides TrayMenuController with AwtTrayMenuController;
provides Configurator with LogbackConfiguratorFactory;
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;

View File

@@ -22,8 +22,6 @@ import javax.inject.Named;
import javax.inject.Singleton;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
@@ -76,8 +74,8 @@ public abstract class CommonsModule {
@Provides
@Singleton
static Optional<RevealPathService> provideRevealPathService() {
return RevealPathService.get().findFirst();
static RevealPathService provideRevealPathService() {
return RevealPathService.get().findFirst().orElseThrow();
}

View File

@@ -9,10 +9,13 @@ 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;
@@ -20,20 +23,22 @@ public class Environment {
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
private static final int DEFAULT_MIN_PW_LENGTH = 8;
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";
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 Environment() {}
@@ -57,6 +62,8 @@ 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);
}
public static Environment getInstance() {
@@ -145,6 +152,18 @@ 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);
}
private Optional<Path> getPath(String propertyName) {
String value = System.getProperty(propertyName);
return Optional.ofNullable(value).map(Paths::get);

View File

@@ -1,7 +1,7 @@
package org.cryptomator.common;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import java.util.Map;
import java.util.Properties;
@@ -13,10 +13,12 @@ public class SubstitutingProperties extends PropertiesDecorator {
private static final Pattern TEMPLATE = Pattern.compile("@\\{(\\w+)}");
private final Map<String, String> env;
private final Logger logger;
public SubstitutingProperties(Properties props, Map<String, String> systemEnvironment) {
public SubstitutingProperties(Properties props, Map<String, String> systemEnvironment, Logger logger) {
super(props);
this.env = systemEnvironment;
this.logger = logger;
}
@Override
@@ -44,7 +46,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
case "localappdata" -> resolveFrom("LOCALAPPDATA", Source.ENV);
case "userhome" -> resolveFrom("user.home", Source.PROPS);
default -> {
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Unknown variable {} in property value {}.", match.group(), value);
logger.warn("Unknown variable {} in property value {}.", match.group(), value);
yield match.group();
}
});
@@ -56,7 +58,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
case PROPS -> delegate.getProperty(key);
};
if (val == null) {
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
logger.warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
return "";
} else {
return Matcher.quoteReplacement(val);

View File

@@ -24,9 +24,12 @@ 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 {
@@ -78,6 +81,7 @@ 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();
@@ -118,6 +122,7 @@ 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());
@@ -149,6 +154,7 @@ public class Settings {
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
trustedHosts.addListener(this::somethingChanged);
}
@SuppressWarnings("deprecation")
@@ -207,6 +213,7 @@ public class Settings {
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
json.trustedHosts = Set.copyOf(trustedHosts);
return json;
}

View File

@@ -4,17 +4,23 @@ 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")
List<VaultSettingsJson> directories = List.of();
@JsonSetter(nulls = Nulls.AS_EMPTY)
List<VaultSettingsJson> directories = new ArrayList<>();
@JsonProperty("writtenByVersion")
String writtenByVersion;
@@ -99,4 +105,8 @@ class SettingsJson {
@JsonProperty("lastUpdateAttemptedByVersion")
String lastUpdateAttemptedByVersion;
@JsonProperty("trustedHosts")
@JsonSetter(nulls = Nulls.AS_EMPTY)
Set<String> trustedHosts = new HashSet<>();
}

View File

@@ -27,6 +27,8 @@ 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
@@ -42,7 +44,9 @@ class AdminPropertiesFactory {
"cryptomator.pluginDir", //
"cryptomator.p12Path", //
"cryptomator.mountPointsDir", //
"cryptomator.disableUpdateCheck");
"cryptomator.disableUpdateCheck", //
"cryptomator.hub.allowedHosts", //
"cryptomator.hub.enableTrustOnFirstUse");
/**

View File

@@ -36,7 +36,7 @@ public class Cryptomator {
static {
var adminProps = AdminPropertiesFactory.create();
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv());
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv(), EventualLogger.INSTANCE);
System.setProperties(lazyProcessedProps);
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
LOG = LoggerFactory.getLogger(Cryptomator.class);

View File

@@ -19,6 +19,7 @@ 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"), //
@@ -29,6 +30,7 @@ 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"), //

View File

@@ -1,17 +1,16 @@
package org.cryptomator.ui.common;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.mount.Mountpoint;
import org.cryptomator.integrations.mount.UnmountFailedException;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.application.HostServices;
import javafx.concurrent.Task;
import javafx.stage.Stage;
import java.io.IOException;
@@ -28,12 +27,12 @@ public class VaultService {
private static final Logger LOG = LoggerFactory.getLogger(VaultService.class);
private final Lazy<Application> application;
private final RevealPathService revealPathService;
private final ExecutorService executorService;
@Inject
public VaultService(Lazy<Application> application, ExecutorService executorService) {
this.application = application;
public VaultService(RevealPathService revealPathService, ExecutorService executorService) {
this.revealPathService = revealPathService;
this.executorService = executorService;
}
@@ -47,9 +46,9 @@ public class VaultService {
* @param vault The vault to reveal
*/
public Task<Vault> createRevealTask(Vault vault) {
Task<Vault> task = new RevealVaultTask(vault, application.get().getHostServices());
task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayName()));
task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), evt.getSource().getException()));
Task<Vault> task = new RevealVaultTask(vault, revealPathService);
task.setOnSucceeded(_ -> LOG.info("Revealed {}", vault.getDisplayName()));
task.setOnFailed(evt -> LOG.warn("Failed to reveal {}", vault.getDisplayName(), evt.getSource().getException()));
return task;
}
@@ -110,19 +109,18 @@ public class VaultService {
private static class RevealVaultTask extends Task<Vault> {
private final Vault vault;
private final HostServices hostServices;
private final RevealPathService rs;
public RevealVaultTask(Vault vault, HostServices hostServices) {
public RevealVaultTask(Vault vault, RevealPathService revealPathService) {
this.vault = vault;
this.hostServices = hostServices;
setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), getException()));
this.rs = revealPathService;
}
@Override
protected Vault call() {
protected Vault call() throws RevealFailedException {
switch (vault.getMountPoint()) {
case null -> LOG.warn("Not currently mounted");
case Mountpoint.WithPath m -> hostServices.showDocument(m.uri().toString());
case Mountpoint.WithPath m -> rs.reveal(m.path());
case Mountpoint.WithUri m -> LOG.info("Vault mounted at {}", m.uri()); // TODO show in UI?
}
return vault;

View File

@@ -58,8 +58,6 @@ 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
@@ -68,12 +66,11 @@ public class DecryptFileNamesViewController implements FxController {
public TableView<CipherAndCleartext> cipherToCleartextTable;
@Inject
public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, @DecryptNameWindow List<Path> pathsToDecrypt, ResourceBundle resourceBundle) {
public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, ResourceBundle resourceBundle) {
this.window = window;
this.vault = vault;
this.resourceBundle = resourceBundle;
this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList());
this.initialList = pathsToDecrypt;
}
@FXML
@@ -97,8 +94,7 @@ public class DecryptFileNamesViewController implements FxController {
});
cipherToCleartextTable.setOnDragDropped(event -> {
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
checkAndDecrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
cipherToCleartextTable.setItems(mapping);
decrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
}
});
cipherToCleartextTable.setOnDragExited(_ -> cipherToCleartextTable.setItems(mapping));
@@ -124,9 +120,7 @@ public class DecryptFileNamesViewController implements FxController {
});
}
});
if (!initialList.isEmpty()) {
checkAndDecrypt(initialList);
}
window.setOnHidden(_ -> mapping.clear());
}
private void copySingleCelltoClipboard() {
@@ -149,10 +143,18 @@ public class DecryptFileNamesViewController implements FxController {
fileChooser.setInitialDirectory(vault.getPath().toFile());
var ciphertextNodes = fileChooser.showOpenMultipleDialog(window);
if (ciphertextNodes != null) {
checkAndDecrypt(ciphertextNodes.stream().map(File::toPath).toList());
decrypt(ciphertextNodes.stream().map(File::toPath).toList());
}
}
public void decrypt(List<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

View File

@@ -28,23 +28,28 @@ public interface DecryptNameComponent {
@FxmlScene(FxmlFile.DECRYPTNAMES)
Lazy<Scene> decryptNamesView();
DecryptFileNamesViewController controller();
@DecryptNameWindow
Vault vault();
default void showDecryptFileNameWindow() {
default void showDecryptFileNameWindow(List<Path> pathsToDecrypt) {
Stage s = window();
s.setScene(decryptNamesView().get());
s.sizeToScene();
if (vault().isUnlocked()) {
controller().decrypt(pathsToDecrypt);
s.show();
s.requestFocus();
} else {
LOG.error("Aborted showing DecryptFileName window: vault state is not {}, but {}.", VaultState.Value.UNLOCKED, vault().getState());
s.close();
}
}
@Subcomponent.Factory
interface Factory {
DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner, @BindsInstance @DecryptNameWindow List<Path> pathsToDecrypt);
DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner);
}
}

View File

@@ -2,18 +2,17 @@ package org.cryptomator.ui.eventview;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants;
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
import org.cryptomator.common.Nullable;
import org.cryptomator.common.ObservableUtil;
import org.cryptomator.cryptofs.CryptoPath;
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
import org.cryptomator.event.FSEventBucket;
import org.cryptomator.event.FSEventBucketContent;
import org.cryptomator.event.FileSystemEventAggregator;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.ui.common.FxController;
@@ -46,7 +45,6 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.Function;
@@ -57,7 +55,6 @@ public class EventListCellController implements FxController {
private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
private final FileSystemEventAggregator fileSystemEventAggregator;
@Nullable
private final RevealPathService revealService;
private final ResourceBundle resourceBundle;
private final ObjectProperty<Map.Entry<FSEventBucket, FSEventBucketContent>> eventEntry;
@@ -82,15 +79,17 @@ public class EventListCellController implements FxController {
Button eventActionsButton;
@Inject
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator, Optional<RevealPathService> revealService, ResourceBundle resourceBundle) {
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator,
RevealPathService revealService,
ResourceBundle resourceBundle) {
this.fileSystemEventAggregator = fileSystemEventAggregator;
this.revealService = revealService.orElseGet(() -> null);
this.revealService = revealService;
this.resourceBundle = resourceBundle;
this.eventEntry = new SimpleObjectProperty<>(null);
this.eventMessage = new SimpleStringProperty();
this.eventDescription = new SimpleStringProperty();
this.eventIcon = new SimpleObjectProperty<>();
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1? "" : "("+ e.getValue().count() +")", "");
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1 ? "" : "(" + e.getValue().count() + ")", "");
this.vaultUnlocked = ObservableUtil.mapWithDefault(eventEntry.flatMap(e -> e.getKey().vault().unlockedProperty()), Function.identity(), false);
this.readableTime = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_TIME_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
this.readableDate = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_DATE_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
@@ -136,13 +135,8 @@ public class EventListCellController implements FxController {
eventMessage.setValue(resourceBundle.getString("eventView.entry.inUse.message"));
var indexFileName = fiiue.cleartextPath().lastIndexOf("/");
eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1));
if (revealService != null) {
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
} else {
addLocalizedAction("eventView.entry.inUse.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(fiiue.cleartextPath()).toString()));
addLocalizedAction("eventView.entry.inUse.copyEncrypted", () -> copyToClipboard(fiiue.ciphertextPath().toString()));
}
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
var user = userAndDevice[0];
@@ -156,11 +150,7 @@ public class EventListCellController implements FxController {
eventIcon.setValue(FontAwesome5Icon.TIMES);
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
} else {
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
}
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
}
@@ -168,46 +158,29 @@ public class EventListCellController implements FxController {
eventIcon.setValue(FontAwesome5Icon.CHECK);
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
} else {
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
}
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
}
private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) {
eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT);
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
} else {
addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
}
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
}
private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) {
eventIcon.setValue(FontAwesome5Icon.BAN);
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
} else {
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
}
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
}
private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) {
eventIcon.setValue(FontAwesome5Icon.TIMES);
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
if (revealService != null) {
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
} else {
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
}
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
}
private void addLocalizedAction(String localizationKey, Runnable action) {
@@ -270,7 +243,7 @@ public class EventListCellController implements FxController {
}
var mountPoint = v.getMountPoint().uri().getPath();
if(SystemUtils.IS_OS_WINDOWS) {
if (SystemUtils.IS_OS_WINDOWS) {
mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors
}
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute

View File

@@ -10,16 +10,20 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javafx.application.Application;
import javafx.application.Platform;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@FxApplicationScoped
public class FxApplication {
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
static final AtomicReference<Application> INSTANCE = new AtomicReference<>();
private final long startupTime;
private final Environment environment;
private final Settings settings;
@@ -33,7 +37,8 @@ public class FxApplication {
private final FxNotificationManager notificationManager;
@Inject
FxApplication(@Named("startupTime") long startupTime, //
FxApplication(Application fxApp,
@Named("startupTime") long startupTime, //
Environment environment, //
Settings settings, //
AppLaunchEventHandler launchEventHandler, //
@@ -55,6 +60,8 @@ public class FxApplication {
this.autoUnlocker = autoUnlocker;
this.fxFSEventList = fxFSEventList;
this.notificationManager = notificationManager;
INSTANCE.set(fxApp);
}
public void start() {

View File

@@ -64,17 +64,6 @@ abstract class FxApplicationModule {
return builder.build();
}
@Provides
@FxApplicationScoped
static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) {
return builder.build();
}
@Provides
@FxApplicationScoped
static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) {
return builder.build();
}
@Provides
@FxApplicationScoped
@@ -88,10 +77,4 @@ abstract class FxApplicationModule {
return factory.create();
}
@Provides
@FxApplicationScoped
static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) {
return factory.create();
}
}

View File

@@ -39,6 +39,7 @@ import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier;
@FxApplicationScoped
public class FxApplicationWindows {
@@ -47,15 +48,15 @@ public class FxApplicationWindows {
private final Stage primaryStage;
private final Optional<TrayIntegrationProvider> trayIntegration;
private final Lazy<MainWindowComponent> mainWindow;
private final Lazy<PreferencesComponent> preferencesWindow;
private final CachedLazy<MainWindowComponent> mainWindow;
private final CachedLazy<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 Lazy<EventViewComponent> eventViewWindow;
private final Lazy<NotificationComponent> notificationWindow;
private final CachedLazy<EventViewComponent> eventViewWindow;
private final CachedLazy<NotificationComponent> notificationWindow;
private final ExecutorService executor;
private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final ShareVaultComponent.Factory shareVaultWindow;
@@ -65,8 +66,8 @@ public class FxApplicationWindows {
@Inject
public FxApplicationWindows(@PrimaryStage Stage primaryStage, //
Optional<TrayIntegrationProvider> trayIntegration, //
Lazy<MainWindowComponent> mainWindow, //
Lazy<PreferencesComponent> preferencesWindow, //
MainWindowComponent.Builder mainWindowBuilder, //
PreferencesComponent.Builder preferencesWindowBuilder, //
QuitComponent.Builder quitWindowBuilder, //
UnlockComponent.Factory unlockWorkflowFactory, //
UpdateReminderComponent.Factory updateReminderWindowFactory, //
@@ -74,21 +75,21 @@ public class FxApplicationWindows {
ErrorComponent.Factory errorWindowFactory, //
VaultOptionsComponent.Factory vaultOptionsWindow, //
ShareVaultComponent.Factory shareVaultWindow, //
Lazy<EventViewComponent> eventViewWindow, //
Lazy<NotificationComponent> notificationWindow,
EventViewComponent.Factory eventViewWindowFactory, //
NotificationComponent.Factory notificationWindowFactory, //
ExecutorService executor, //
Dialogs dialogs) {
this.primaryStage = primaryStage;
this.trayIntegration = trayIntegration;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
this.mainWindow = new CachedLazy<>(mainWindowBuilder::build);
this.preferencesWindow = new CachedLazy<>(preferencesWindowBuilder::build);
this.quitWindowBuilder = quitWindowBuilder;
this.unlockWorkflowFactory = unlockWorkflowFactory;
this.updateReminderWindowFactory = updateReminderWindowFactory;
this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory;
this.eventViewWindow = eventViewWindow;
this.notificationWindow = notificationWindow;
this.eventViewWindow = new CachedLazy<>(eventViewWindowFactory::create);
this.notificationWindow = new CachedLazy<>(notificationWindowFactory::create);
this.executor = executor;
this.vaultOptionsWindow = vaultOptionsWindow;
this.shareVaultWindow = shareVaultWindow;
@@ -218,4 +219,29 @@ public class FxApplicationWindows {
LOG.error("Failed to display stage", error);
}
}
private static class CachedLazy<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;
}
}
}

View File

@@ -0,0 +1,37 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.integrations.common.DisplayName;
import org.cryptomator.integrations.common.OperatingSystem;
import org.cryptomator.integrations.common.Priority;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import java.nio.file.Path;
/**
* A {@link RevealPathService} service implementation using the JavaFX {@link javafx.application.HostServices#showDocument(String)} to reveal documents.
* <p>
* Internally the HostServices class uses GTK on Linux.
*
* @implNote {@link #reveal(Path)} only succeeds when the class {@link FxApplication} is initialized.
*/
@DisplayName("JavaFX HostServices (GTK)")
@OperatingSystem(OperatingSystem.Value.LINUX)
@Priority(10)
public class JfxRevealPathService implements RevealPathService {
@Override
public void reveal(Path p) throws RevealFailedException {
var fxApp = FxApplication.INSTANCE.get();
if (fxApp != null) {
fxApp.getHostServices().showDocument(p.toUri().toString());
} else {
throw new RevealFailedException("JavaFX Application not initialized");
}
}
@Override
public boolean isSupported() {
return true;
}
}

View File

@@ -4,9 +4,12 @@ import com.google.common.base.Throwables;
import org.cryptomator.common.Environment;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
@@ -25,6 +28,7 @@ import java.util.stream.Collectors;
@HealthCheckScoped
public class ReportWriter {
private static final Logger LOG = LoggerFactory.getLogger(ReportWriter.class);
private static final String REPORT_HEADER = """
*******************************************
* Cryptomator Vault Health Report *
@@ -43,14 +47,14 @@ public class ReportWriter {
private final Vault vault;
private final VaultConfig vaultConfig;
private final Application application;
private final RevealPathService revealPathService;
private final Path exportDestination;
@Inject
public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference<VaultConfig> vaultConfigRef, Application application, Environment env) {
public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference<VaultConfig> vaultConfigRef, RevealPathService revealPathService, Environment env) {
this.vault = vault;
this.vaultConfig = Objects.requireNonNull(vaultConfigRef.get());
this.application = application;
this.revealPathService = revealPathService;
this.exportDestination = env.getLogDir().orElse(Path.of(System.getProperty("user.home"))).resolve("healthReport_" + vault.getDisplayName() + "_" + TIME_STAMP.format(Instant.now()) + ".log");
}
@@ -92,7 +96,11 @@ public class ReportWriter {
}
private void reveal() {
application.getHostServices().showDocument(exportDestination.getParent().toUri().toString());
try {
revealPathService.reveal(exportDestination.getParent());
} catch (RevealFailedException e) {
LOG.warn("Failed to reveal export destination location of report", e);
}
}
}

View File

@@ -1,6 +1,5 @@
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;
@@ -12,8 +11,6 @@ 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;

View File

@@ -0,0 +1,179 @@
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();
}
}

View File

@@ -98,6 +98,13 @@ 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
@@ -168,6 +175,13 @@ 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
@@ -180,6 +194,11 @@ 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)
@@ -225,6 +244,11 @@ 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)

View File

@@ -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> authFlowScene;
private final Lazy<Scene> checkHostTrustScene;
private final Lazy<Scene> noKeychainScene;
private final CompletableFuture<ReceivedKey> result;
private final DeviceKey deviceKey;
@Inject
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) {
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) {
this.window = window;
this.keychainManager = keychainManager;
this.fsOwnerId = fsOwnerId;
window.setTitle(windowTitle);
window.setOnCloseRequest(_ -> result.cancel(true));
this.authFlowScene = authFlowScene;
this.checkHostTrustScene = checkHostTrustScene;
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(authFlowScene);
showWindow(checkHostTrustScene);
var jwe = result.get();
return jwe.decryptMasterkey(keypair.getPrivate());
} catch (NoKeychainAccessProviderException e) {

View File

@@ -0,0 +1,34 @@
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);
}
}

View File

@@ -1,6 +1,7 @@
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;
@@ -63,16 +64,21 @@ 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 {
Path filePath = vault.getPath().resolve(keyId.getSchemeSpecificPart());
// determine masterkey file path:
Path filePath = vault.getPath().resolve(Constants.MASTERKEY_FILENAME);
if (!Files.exists(filePath)) {
filePath = askUserForMasterkeyFilePath();
}
// unlock:
if (passphrase == null) {
askForPassphrase();
}
var masterkey = masterkeyFileAccess.load(filePath, passphrase);
//backup
// backup on successful unlock:
if (filePath.startsWith(vault.getPath())) {
try {
BackupHelper.attemptBackup(filePath);

View File

@@ -2,14 +2,17 @@ package org.cryptomator.ui.mainwindow;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.AutoAnimator;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.controls.FontAwesome5IconView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
@@ -19,10 +22,12 @@ import javafx.fxml.FXML;
@MainWindowScoped
public class VaultDetailController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(VaultDetailController.class);
private final ReadOnlyObjectProperty<Vault> vault;
private final Application application;
private final ObservableValue<FontAwesome5Icon> glyph;
private final BooleanBinding anyVaultSelected;
private final RevealPathService revealPathService;
private AutoAnimator spinAnimation;
@@ -31,11 +36,11 @@ public class VaultDetailController implements FxController {
@Inject
VaultDetailController(ObjectProperty<Vault> vault, Application application) {
VaultDetailController(ObjectProperty<Vault> vault, RevealPathService revealPathService) {
this.vault = vault;
this.application = application;
this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState);
this.anyVaultSelected = vault.isNotNull();
this.revealPathService = revealPathService;
}
public void initialize() {
@@ -61,7 +66,11 @@ public class VaultDetailController implements FxController {
@FXML
public void revealStorageLocation() {
application.getHostServices().showDocument(vault.get().getPath().toUri().toString());
try {
revealPathService.reveal(vault.get().getPath());
} catch (RevealFailedException e) {
LOG.warn("Failed to reveal vault storage location", e);
}
}
/* Observable Properties */

View File

@@ -1,9 +1,8 @@
package org.cryptomator.ui.mainwindow;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.tobiasdiez.easybind.EasyBind;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Nullable;
@@ -21,7 +20,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
@@ -31,7 +29,6 @@ import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.TransferMode;
import javafx.stage.FileChooser;
@@ -40,12 +37,8 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -60,10 +53,11 @@ public class VaultDetailUnlockedController implements FxController {
private final VaultService vaultService;
private final WrongFileAlertComponent.Builder wrongFileAlert;
private final Stage mainWindow;
private final Optional<RevealPathService> revealPathService;
private final RevealPathService revealPathService;
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;
@@ -84,7 +78,7 @@ public class VaultDetailUnlockedController implements FxController {
VaultStatisticsComponent.Builder vaultStatsBuilder, //
WrongFileAlertComponent.Builder wrongFileAlert, //
@MainWindow Stage mainWindow, //
Optional<RevealPathService> revealPathService, //
RevealPathService revealPathService, //
DecryptNameComponent.Factory decryptNameWindowFactory, //
ResourceBundle resourceBundle) {
this.vault = vault;
@@ -95,7 +89,8 @@ public class VaultDetailUnlockedController implements FxController {
this.revealPathService = revealPathService;
this.decryptNameWindowFactory = decryptNameWindowFactory;
this.resourceBundle = resourceBundle;
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
this.vaultStats = Caffeine.newBuilder().weakValues().build(this::buildVaultStats);
this.decryptNameWindows = Caffeine.newBuilder().weakValues().build(this::buildDecryptNameWindow);
this.vaultStatsBuilder = vaultStatsBuilder;
var mp = vault.flatMap(Vault::mountPointProperty);
this.accessibleViaPath = mp.map(m -> m instanceof Mountpoint.WithPath).orElse(false);
@@ -111,7 +106,7 @@ public class VaultDetailUnlockedController implements FxController {
public void initialize() {
revealEncryptedDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverLocateEncrypted));
revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealOrCopyPaths));
revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealPaths));
revealEncryptedDropZone.setOnDragExited(_ -> draggingOverLocateEncrypted.setValue(false));
decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName));
@@ -156,7 +151,7 @@ public class VaultDetailUnlockedController implements FxController {
if (cleartextFile != null) {
var ciphertextPath = getCiphertextPath(cleartextFile.toPath());
if (ciphertextPath != null) {
revealOrCopyPaths(List.of(ciphertextPath));
revealPaths(List.of(ciphertextPath));
}
}
}
@@ -167,7 +162,7 @@ public class VaultDetailUnlockedController implements FxController {
}
private void showDecryptNameWindow(List<Path> pathsToDecrypt) {
decryptNameWindowFactory.create(vault.get(), mainWindow, pathsToDecrypt).showDecryptFileNameWindow();
decryptNameWindows.get(vault.get()).showDecryptFileNameWindow(pathsToDecrypt);
}
private boolean startsWithVaultAccessPoint(Path path) {
@@ -188,38 +183,26 @@ public class VaultDetailUnlockedController implements FxController {
}
}
private void revealOrCopyPaths(List<Path> paths) {
revealPathService.ifPresentOrElse(svc -> revealPaths(svc, paths), () -> {
LOG.warn("No service provider to reveal files found.");
copyPathsToClipboard(paths);
});
}
private void revealPaths(RevealPathService service, List<Path> paths) {
private void revealPaths(List<Path> paths) {
paths.forEach(path -> {
try {
LOG.debug("Revealing {}", path);
service.reveal(path);
revealPathService.reveal(path);
} catch (RevealFailedException e) {
//TODO: show popup in ui
LOG.error("Revealing ciphertext file failed.", e);
}
});
}
private void copyPathsToClipboard(List<Path> paths) {
StringBuilder clipboardString = new StringBuilder();
paths.forEach(p -> clipboardString.append(p.toString()).append("\n"));
Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, clipboardString.toString()));
ciphertextPathsCopied.setValue(true);
CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> {
ciphertextPathsCopied.set(false);
});
}
private VaultStatisticsComponent buildVaultStats(Vault vault) {
return vaultStatsBuilder.vault(vault).build();
}
private DecryptNameComponent buildDecryptNameWindow(Vault vault) {
return decryptNameWindowFactory.create(vault, mainWindow);
}
@FXML
public void revealAccessLocation() {
vaultService.reveal(vault.get());
@@ -239,7 +222,7 @@ public class VaultDetailUnlockedController implements FxController {
@FXML
public void showVaultStatistics() {
vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
vaultStats.get(vault.get()).showVaultStatisticsWindow();
}
/* Getter/Setter */

View File

@@ -10,16 +10,18 @@ import org.cryptomator.integrations.common.NamedServiceProvider;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.integrations.quickaccess.QuickAccessService;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ToggleGroup;
@@ -41,7 +43,7 @@ public class GeneralPreferencesController implements FxController {
private final Settings settings;
private final Optional<AutoStartProvider> autoStartProvider;
private final List<QuickAccessService> quickAccessServices;
private final Application application;
private final RevealPathService revealPathService;
private final Environment environment;
private final List<KeychainAccessProvider> keychainAccessProviders;
private final KeychainManager keychain;
@@ -55,14 +57,21 @@ 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);
@Inject
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, //
List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, //
Environment environment, FxApplicationWindows appWindows, ExecutorService backgroundExecutor) {
GeneralPreferencesController(@PreferencesWindow Stage window, //
Settings settings, //
Optional<AutoStartProvider> autoStartProvider, //
List<KeychainAccessProvider> keychainAccessProviders, //
KeychainManager keychain, //
RevealPathService revealPathService, //
Environment environment, //
FxApplicationWindows appWindows, //
ExecutorService backgroundExecutor) {
this.window = window;
this.settings = settings;
this.autoStartProvider = autoStartProvider;
@@ -70,7 +79,7 @@ public class GeneralPreferencesController implements FxController {
this.keychain = keychain;
this.backgroundExecutor = backgroundExecutor;
this.quickAccessServices = QuickAccessService.get().toList();
this.application = application;
this.revealPathService = revealPathService;
this.environment = environment;
this.appWindows = appWindows;
}
@@ -98,6 +107,9 @@ 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) {
@@ -124,6 +136,10 @@ public class GeneralPreferencesController implements FxController {
return autoStartProvider.isPresent();
}
public boolean isHubTrustOnFirstUseEnabled() {
return environment.hubTrustOnFirstUse();
}
@FXML
public void toggleAutoStart() {
autoStartProvider.ifPresent(autoStart -> {
@@ -146,9 +162,18 @@ public class GeneralPreferencesController implements FxController {
return !quickAccessServices.isEmpty();
}
@FXML
public void resetTrustedHosts() {
settings.trustedHosts.clear();
}
@FXML
public void showLogfileDirectory() {
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
try {
revealPathService.reveal(environment.getLogDir().orElseThrow());
} catch (RevealFailedException e) {
LOG.warn("Failed to reveal log files directory.", e);
}
}
/* Helper classes */
@@ -196,4 +221,5 @@ public class GeneralPreferencesController implements FxController {
}
}
}
}

View File

@@ -3,18 +3,19 @@ package org.cryptomator.ui.preferences;
import org.cryptomator.common.Environment;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.revealpath.RevealFailedException;
import org.cryptomator.integrations.revealpath.RevealPathService;
import org.cryptomator.integrations.update.UpdateStep;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.updater.UpdateChecker;
import org.cryptomator.updater.FallbackUpdateInfo;
import org.cryptomator.updater.UpdateChecker;
import org.cryptomator.updater.UpdateService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
@@ -49,7 +50,7 @@ public class UpdatesPreferencesController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(UpdatesPreferencesController.class);
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
private final Application application;
private final RevealPathService revealPathService;
private final Environment environment;
private final ResourceBundle resourceBundle;
private final Settings settings;
@@ -72,8 +73,8 @@ public class UpdatesPreferencesController implements FxController {
public CheckBox checkForUpdatesCheckbox;
@Inject
UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
this.application = application;
UpdatesPreferencesController(RevealPathService revealPathService, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
this.revealPathService = revealPathService;
this.environment = environment;
this.resourceBundle = resourceBundle;
this.settings = settings;
@@ -106,9 +107,14 @@ public class UpdatesPreferencesController implements FxController {
updateService.setOnFailed(this::updateFailed);
}
@FXML
public void showLogfileDirectory() {
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
try {
revealPathService.reveal(environment.getLogDir().orElseThrow());
} catch (RevealFailedException e) {
LOG.warn("Failed to reveal log files directory.", e);
}
}
@FXML
@@ -254,7 +260,7 @@ public class UpdatesPreferencesController implements FxController {
public boolean isProhibitUpdateWhileUnlocked() {
// If the result of the last update check was from the fallback mechanism, we don't need to show the warning
return !unlockedVaults.isEmpty() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate());
return !unlockedVaults.isEmpty() && updateChecker.isUpdateAvailable() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate());
}
public BooleanBinding prohibitUpdateWhileUnlockedProperty() {

View File

@@ -18,7 +18,10 @@ import java.net.URISyntaxException;
public class ShareVaultController implements FxController {
private static final String SCHEME_PREFIX = "hub+";
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/";
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/" //
+ "?utm_source=cryptomator-desktop" //
+ "&utm_medium=app" //
+ "&utm_campaign=share-vault";
private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/security/best-practices/#sharing-of-vaults";
private final Stage window;

View File

@@ -0,0 +1,53 @@
<?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>

View File

@@ -0,0 +1,52 @@
<?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>

View File

@@ -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 2025 Skymatic GmbH"/>
<Label text="© 2016 2026 Skymatic GmbH"/>
</VBox>
</HBox>

View File

@@ -1,6 +1,7 @@
<?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?>
@@ -34,6 +35,8 @@
<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">

View File

@@ -9,90 +9,94 @@
<?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?>
<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}"/>
<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}"/>
<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.margin>
<Insets top="9"/>
</VBox.margin>
</Label>
<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}">
<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="FOLDER_OPEN" glyphSize="15"/>
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
</graphic>
</Button>
<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>
<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>
<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>
<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.margin>
<Insets top="9"/>
</VBox.margin>
</Label>
<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>
</ScrollPane>

View File

@@ -162,6 +162,12 @@ 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.
@@ -193,6 +199,9 @@ 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
@@ -306,6 +315,7 @@ 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
@@ -696,27 +706,19 @@ eventView.cell.actionsButton.tooltip=Event actions
eventView.entry.vaultLocked.description=Unlock "%s" for details
eventView.entry.conflictResolved.message=Resolved conflict
eventView.entry.conflictResolved.showDecrypted=Show decrypted file
eventView.entry.conflictResolved.copyDecrypted=Copy decrypted path
eventView.entry.conflict.message=Conflict resolution failed
eventView.entry.conflict.showDecrypted=Show decrypted, original file
eventView.entry.conflict.copyDecrypted=Copy decrypted, original path
eventView.entry.conflict.showEncrypted=Show conflicting, encrypted file
eventView.entry.conflict.copyEncrypted=Copy conflicting, encrypted path
eventView.entry.decryptionFailed.message=Decryption failed
eventView.entry.decryptionFailed.showEncrypted=Show encrypted file
eventView.entry.decryptionFailed.copyEncrypted=Copy encrypted path
eventView.entry.brokenDirFile.message=Broken directory link
eventView.entry.brokenDirFile.showEncrypted=Show broken, encrypted link
eventView.entry.brokenDirFile.copyEncrypted=Copy path of broken link
eventView.entry.brokenFileNode.message=Broken filesystem node
eventView.entry.brokenFileNode.showEncrypted=Show broken, encrypted node
eventView.entry.brokenFileNode.copyEncrypted=Copy path of broken, encrypted node
eventView.entry.brokenFileNode.copyDecrypted=Copy decrypted path
eventView.entry.inUse.message=File in use
eventView.entry.inUse.showDecrypted=Show decrypted file
eventView.entry.inUse.copyDecrypted=Copy decrypted path
eventView.entry.inUse.showEncrypted=Show encrypted file
eventView.entry.inUse.copyEncrypted=Copy encrypted path
eventView.entry.inUse.copyUserAndDevice=Copy locking user and device name
eventView.entry.inUse.ignoreLock=Ignore use status
@@ -725,4 +727,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

View File

@@ -630,26 +630,18 @@ eventView.clearListButton.tooltip=تفريغ القائمة
eventView.entry.vaultLocked.description=فتح "%s" للحصول على التفاصيل
eventView.entry.conflictResolved.message=تم حل التضارب
eventView.entry.conflictResolved.showDecrypted=إظهار الملف غير المشفر
eventView.entry.conflictResolved.copyDecrypted=نسخ المسار غير المشفر
eventView.entry.conflict.message=فشل حل التضارب
eventView.entry.conflict.showDecrypted=إظهار الملَف غير المشفر الأصلي
eventView.entry.conflict.copyDecrypted=نسخ المسار غير المشفر والأصلي
eventView.entry.conflict.showEncrypted=إظهار ملف متضارب ومشفر
eventView.entry.conflict.copyEncrypted=نسخ مسار التشفير المتعارض
eventView.entry.decryptionFailed.message=فشل فك التشفير
eventView.entry.decryptionFailed.showEncrypted=عرض ملَف المشفر
eventView.entry.decryptionFailed.copyEncrypted=نسخ مسار المشفر
eventView.entry.brokenDirFile.message=رابط الدليل المكسور
eventView.entry.brokenDirFile.showEncrypted=إظهار الرابط المكسور، المشفر
eventView.entry.brokenDirFile.copyEncrypted=نسخ مسار الرابط المكسور
eventView.entry.brokenFileNode.message=عقدة ملفات النظام التافلة
eventView.entry.brokenFileNode.showEncrypted=عرض العقدة المشفّرة التافلة
eventView.entry.brokenFileNode.copyEncrypted=نسخ مسار العقدة المشفّرة التافلة
eventView.entry.brokenFileNode.copyDecrypted=نسخ المسار غير المشفر
eventView.entry.inUse.showDecrypted=إظهار الملف غير المشفر
eventView.entry.inUse.copyDecrypted=نسخ المسار غير المشفر
eventView.entry.inUse.showEncrypted=عرض ملَف المشفر
eventView.entry.inUse.copyEncrypted=نسخ مسار المشفر
# Notifications

View File

@@ -18,6 +18,12 @@ generic.button.print=Отпечатване
generic.button.remove=Премахване
## Vault state
vault.state.locked=Заключено
vault.state.unlocked=Отключено
vault.state.missing=Липсващ
vault.state.migrationNeeded=Необходима е миграция
vault.state.processing=Обработване
vault.state.error=Грешка
# Error
error.message=Възникна грешка
@@ -98,6 +104,7 @@ addvault.new.readme.accessLocation.4=При желание можете да п
## Existing
addvaultwizard.existing.title=Добавяне на съществуващо хранилище
addvaultwizard.existing.instruction=Изберете файла „vault.cryptomator“ от съществуващото хранилище, но ако има само файл „masterkey.cryptomator“, изберете него.
addvaultwizard.existing.restore=Възстановяване…
addvaultwizard.existing.chooseBtn=Избиране…
addvaultwizard.existing.filePickerTitle=Избор на файл на хранилището
addvaultwizard.existing.filePickerMimeDesc=Хранилище на Криптоматор
@@ -145,6 +152,7 @@ unlock.error.customPath.description.hideawayNotDir=Временният, скр
unlock.error.customPath.description.couldNotBeCleaned=Хранилището не може да бъде монтирано на „%s“. Опитайте отново или изберете друг път.
unlock.error.customPath.description.notEmptyDir=Потребителският път на монтиране „%s“ не е празна папка. Изберете празна папка и опитайте отново.
unlock.error.customPath.description.generic=Избрали сте потребителски път за монтиране на това хранилище, но при използването му възникна следната грешка: %2$s
unlock.error.restartRequired.message=Хранилището не може да бъде отключено
unlock.error.title=Неуспешно отключване на „%s“
## Hub
hub.noKeychain.message=Няма достъп до ключа на устройството
@@ -369,6 +377,7 @@ main.vaultlist.contextMenu.unlock=Отключване…
main.vaultlist.contextMenu.unlockNow=Отключване сега
main.vaultlist.contextMenu.vaultoptions=Настройки на хранилището
main.vaultlist.contextMenu.reveal=Разкриване на диска
main.vaultlist.contextMenu.share=Споделяне…
main.vaultlist.addVaultButton.tooltip=Добавяне на хранилище
##Notification
## Vault Detail

View File

@@ -269,6 +269,8 @@ 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
@@ -358,6 +360,7 @@ 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
@@ -367,6 +370,7 @@ 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
@@ -406,6 +410,7 @@ 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
@@ -419,12 +424,15 @@ 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…
@@ -482,6 +490,7 @@ 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
@@ -512,6 +521,7 @@ 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.
@@ -665,6 +675,8 @@ 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
@@ -677,34 +689,31 @@ 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
eventView.entry.conflictResolved.showDecrypted=Vis dekrypteret fil
eventView.entry.conflictResolved.copyDecrypted=Kopiér dekrypteret sti
eventView.entry.conflict.message=Konfliktløsning mislykkedes
eventView.entry.conflict.showDecrypted=Vis dekrypteret, original fil
eventView.entry.conflict.copyDecrypted=Kopier dekrypteret, original sti
eventView.entry.conflict.showEncrypted=Vis modstridende, krypteret fil
eventView.entry.conflict.copyEncrypted=Kopier modstridende, krypteret sti
eventView.entry.decryptionFailed.message=Dekryptering mislykkedes
eventView.entry.decryptionFailed.showEncrypted=Vis krypteret fil
eventView.entry.decryptionFailed.copyEncrypted=Kopiér krypteret sti
eventView.entry.brokenDirFile.message=Brudt mappelink
eventView.entry.brokenDirFile.showEncrypted=Vis brudt, krypteret link
eventView.entry.brokenDirFile.copyEncrypted=Kopiér sti til brudt link
eventView.entry.brokenFileNode.message=Brudt filsystem-node
eventView.entry.brokenFileNode.showEncrypted=Vis brudt, krypteret node
eventView.entry.brokenFileNode.copyEncrypted=Kopiér sti af brudt, krypteret node
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.copyDecrypted=Kopiér dekrypteret sti
eventView.entry.inUse.showEncrypted=Vis krypteret fil
eventView.entry.inUse.copyEncrypted=Kopiér krypteret sti
eventView.entry.inUse.copyUserAndDevice=Kopiér låsebruger og enhedsnavn
eventView.entry.inUse.ignoreLock=Ignorér anvendelsesstatus
# Notifications
## FileIsInUse Notification
notification.inUse.message=Filen er låst af en anden enhed
notification.inUse.description=Filen åbnes af %s på enheden %s. Bed brugeren om at lukke filen og synkronisere igen. Ellers kan du ignorere låsen og åbne den alligevel.
notification.inUse.action=Ignorér s
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

View File

@@ -684,32 +684,21 @@ eventView.cell.actionsButton.tooltip=Ereignisaktionen
eventView.entry.vaultLocked.description=Entsperre "%s" für Details
eventView.entry.conflictResolved.message=Konflikt gelöst
eventView.entry.conflictResolved.showDecrypted=Entschlüsselte Datei anzeigen
eventView.entry.conflictResolved.copyDecrypted=Pfad der entschlüsselten Datei kopieren
eventView.entry.conflict.message=Konfliktlösung fehlgeschlagen
eventView.entry.conflict.showDecrypted=Entschlüsselte, ursprüngliche Datei anzeigen
eventView.entry.conflict.copyDecrypted=Entschlüsselten, ursprünglicher Pfad kopieren
eventView.entry.conflict.showEncrypted=Zeige verschlüsselte Konfliktdatei
eventView.entry.conflict.copyEncrypted=Verschlüsselten Konfliktpfad kopieren
eventView.entry.decryptionFailed.message=Entschlüsselung fehlgeschlagen
eventView.entry.decryptionFailed.showEncrypted=Verschlüsselte Datei anzeigen
eventView.entry.decryptionFailed.copyEncrypted=Pfad der verschlüsselten Datei kopieren
eventView.entry.brokenDirFile.message=Ungültiger Verzeichnislink
eventView.entry.brokenDirFile.showEncrypted=Defekten, verschlüsselten Link anzeigen
eventView.entry.brokenDirFile.copyEncrypted=Pfad des ungültigen Links kopieren
eventView.entry.brokenFileNode.message=Beschädigter Dateisystemknoten
eventView.entry.brokenFileNode.showEncrypted=Beschädigten, verschlüsselten Knoten anzeigen
eventView.entry.brokenFileNode.copyEncrypted=Pfad des beschädigten, verschlüsselten Knotens kopieren
eventView.entry.brokenFileNode.copyDecrypted=Pfad der entschlüsselten Datei kopieren
eventView.entry.inUse.message=Gesperrte Datei
eventView.entry.inUse.showDecrypted=Entschlüsselte Datei anzeigen
eventView.entry.inUse.copyDecrypted=Pfad der entschlüsselten Datei kopieren
eventView.entry.inUse.showEncrypted=Verschlüsselte Datei anzeigen
eventView.entry.inUse.copyEncrypted=Pfad der verschlüsselten Datei kopieren
eventView.entry.inUse.copyUserAndDevice=Benutzer- und Gerätenamen der Sperre kopieren
eventView.entry.inUse.ignoreLock=Sperre ignorieren
# Notifications
## FileIsInUse Notification
notification.inUse.message=Die Datei ist über ein anderes Gerät gesperrt
notification.inUse.action=Sperre ignorieren
notification.inUse.action=Verwendungsstatus ignorieren

View File

@@ -630,26 +630,18 @@ eventView.clearListButton.tooltip=Εκκαθάριση λίστας
eventView.entry.vaultLocked.description=Ξεκλείδωμα "%s" για λεπτομέρειες
eventView.entry.conflictResolved.message=Επίλυση σύγκρουσης
eventView.entry.conflictResolved.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
eventView.entry.conflictResolved.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
eventView.entry.conflict.message=Η επίλυση συγκρούσεων απέτυχε
eventView.entry.conflict.showDecrypted=Εμφάνιση αποκρυπτογραφημένων, αρχικό αρχείο
eventView.entry.conflict.copyDecrypted=Αντιγραφή αποκρυπτογραφημένων, αρχική διαδρομή
eventView.entry.conflict.showEncrypted=Εμφάνιση αντικρουόμενου, κρυπτογραφημένο αρχείο
eventView.entry.conflict.copyEncrypted=Αντιγραφή σύγκρουσης, κρυπτογραφημένη διαδρομή
eventView.entry.decryptionFailed.message=Η αποκρυπτογράφηση απέτυχε
eventView.entry.decryptionFailed.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
eventView.entry.decryptionFailed.copyEncrypted=Αντιγραφή κρυπτογραφημένης διαδρομής
eventView.entry.brokenDirFile.message=Μη λειτουργικός σύνδεσμος καταλόγου
eventView.entry.brokenDirFile.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου συνδέσμου
eventView.entry.brokenDirFile.copyEncrypted=Αντιγραφή διαδρομής του μη λειτουργικού συνδέσμου
eventView.entry.brokenFileNode.message=Μη λειτουργικός κόμβος συστήματος αρχείων
eventView.entry.brokenFileNode.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου κόμβου
eventView.entry.brokenFileNode.copyEncrypted=Αντιγραφή διαδρομής του μη λειτουργικού, κρυπτογραφημένου κόμβου
eventView.entry.brokenFileNode.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
eventView.entry.inUse.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
eventView.entry.inUse.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
eventView.entry.inUse.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
eventView.entry.inUse.copyEncrypted=Αντιγραφή κρυπτογραφημένης διαδρομής
# Notifications

View File

@@ -684,27 +684,24 @@ eventView.cell.actionsButton.tooltip=Acciones del evento
eventView.entry.vaultLocked.description=Desbloquear "%s" para más detalles
eventView.entry.conflictResolved.message=Conflicto resuelto
eventView.entry.conflictResolved.showDecrypted=Mostrar archivo descifrado
eventView.entry.conflictResolved.copyDecrypted=Copiar ruta descifrada
eventView.entry.conflict.message=Resolución de conflictos fallida
eventView.entry.conflict.showDecrypted=Mostrar archivo descifrado, original
eventView.entry.conflict.copyDecrypted=Copiar ruta descifrada, original
eventView.entry.conflict.showEncrypted=Mostrar archivo en conflicto, cifrado
eventView.entry.conflict.copyEncrypted=Copiar ruta en conflicto, cifrada
eventView.entry.decryptionFailed.message=Desencriptación fallida
eventView.entry.decryptionFailed.showEncrypted=Mostrar archivo cifrado
eventView.entry.decryptionFailed.copyEncrypted=Copiar ruta cifrada
eventView.entry.brokenDirFile.message=Enlace de directorio roto
eventView.entry.brokenDirFile.showEncrypted=Mostrar enlace roto, cifrado
eventView.entry.brokenDirFile.copyEncrypted=Copiar ruta del enlace roto
eventView.entry.brokenFileNode.message=Nodo de sistema de archivos roto
eventView.entry.brokenFileNode.showEncrypted=Mostrar nodo roto, cifrado
eventView.entry.brokenFileNode.copyEncrypted=Copiar ruta del enlace roto, encriptado
eventView.entry.brokenFileNode.copyDecrypted=Copiar ruta descifrada
eventView.entry.inUse.message=Fichero en uso
eventView.entry.inUse.showDecrypted=Mostrar archivo descifrado
eventView.entry.inUse.copyDecrypted=Copiar ruta descifrada
eventView.entry.inUse.showEncrypted=Mostrar archivo cifrado
eventView.entry.inUse.copyEncrypted=Copiar ruta cifrada
eventView.entry.inUse.ignoreLock=Ignorar el estado de uso
# Notifications
## FileIsInUse Notification
## 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

View File

@@ -683,26 +683,18 @@ eventView.cell.actionsButton.tooltip=Toiminnot
eventView.entry.vaultLocked.description=Avaa "%s" lisätietoja varten
eventView.entry.conflictResolved.message=Ratkaistu ristiriita
eventView.entry.conflictResolved.showDecrypted=Näytä purettu tiedosto
eventView.entry.conflictResolved.copyDecrypted=Kopioi purettu polku
eventView.entry.conflict.message=Ristiriidan ratkaisu epäonnistui
eventView.entry.conflict.showDecrypted=Näytä purettu, alkuperäinen tiedosto
eventView.entry.conflict.copyDecrypted=Näytä purettu, alkuperäinen polku
eventView.entry.conflict.showEncrypted=Näytä ristiriitainen, salattu tiedosto
eventView.entry.conflict.copyEncrypted=Kopioi ristiriitainen, salattu polku
eventView.entry.decryptionFailed.message=Salauksen purku epäonnistui
eventView.entry.decryptionFailed.showEncrypted=Näytä salattu tiedosto
eventView.entry.decryptionFailed.copyEncrypted=Kopioi salattu polku
eventView.entry.brokenDirFile.message=Virheellinen hakemiston linkki
eventView.entry.brokenDirFile.showEncrypted=Näytä virheellinen, salattu linkki
eventView.entry.brokenDirFile.copyEncrypted=Kopioi virheellisen polun linkki
eventView.entry.brokenFileNode.message=Virheellinen tiedostojärjestelmän solmu
eventView.entry.brokenFileNode.showEncrypted=Näytä virheellinen, salattu solmu
eventView.entry.brokenFileNode.copyEncrypted=Kopioi virheellisen, salatun solmun polku
eventView.entry.brokenFileNode.copyDecrypted=Kopioi purettu polku
eventView.entry.inUse.showDecrypted=Näytä purettu tiedosto
eventView.entry.inUse.copyDecrypted=Kopioi purettu polku
eventView.entry.inUse.showEncrypted=Näytä salattu tiedosto
eventView.entry.inUse.copyEncrypted=Kopioi salattu polku
# Notifications

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Actions d'évènement
eventView.entry.vaultLocked.description=Déverrouillez "%s" pour plus de détails
eventView.entry.conflictResolved.message=Conflit résolu
eventView.entry.conflictResolved.showDecrypted=Afficher le fichier déchiffré
eventView.entry.conflictResolved.copyDecrypted=Copier le chemin déchiffré
eventView.entry.conflict.message=La résolution des conflits a échoué
eventView.entry.conflict.showDecrypted=Afficher le fichier original déchiffré
eventView.entry.conflict.copyDecrypted=Copier le chemin original déchiffré
eventView.entry.conflict.showEncrypted=Afficher le fichier chiffré en conflit
eventView.entry.conflict.copyEncrypted=Copier le chemin chiffré en conflit
eventView.entry.decryptionFailed.message=Le déchiffrement a échoué
eventView.entry.decryptionFailed.showEncrypted=Afficher le fichier chiffré
eventView.entry.decryptionFailed.copyEncrypted=Copier le chemin chiffré
eventView.entry.brokenDirFile.message=Lien de répertoire cassé
eventView.entry.brokenDirFile.showEncrypted=Afficher le lien chiffré cassé
eventView.entry.brokenDirFile.copyEncrypted=Copier le chemin du lien cassé
eventView.entry.brokenFileNode.message=Nœud de système de fichiers cassé
eventView.entry.brokenFileNode.showEncrypted=Afficher le nœud chiffré cassé
eventView.entry.brokenFileNode.copyEncrypted=Copier le chemin du nœud chiffré cassé
eventView.entry.brokenFileNode.copyDecrypted=Copier le chemin déchiffré
eventView.entry.inUse.message=Fichier verrouillé
eventView.entry.inUse.message=Fichier en cours dutilisation
eventView.entry.inUse.showDecrypted=Afficher le fichier déchiffré
eventView.entry.inUse.copyDecrypted=Copier le chemin déchiffré
eventView.entry.inUse.showEncrypted=Afficher le fichier chiffré
eventView.entry.inUse.copyEncrypted=Copier le chemin chiffré
eventView.entry.inUse.copyUserAndDevice=Copier le verrouillage de l'utilisateur et le nom de l'appareil
eventView.entry.inUse.ignoreLock=Ignorer le verrou
eventView.entry.inUse.ignoreLock=Ignorer le statut dutilisation
# Notifications
## FileIsInUse Notification
notification.inUse.message=Le fichier est verrouillé par un autre appareil
notification.inUse.description=Le fichier est ouvert par %s sur lappareil %s. Demandez à lutilisateur de fermer le fichier et synchronisez à nouveau. Autrement vous pouvez ignorer le verrouillage et ouvrir quand même le fichier.
notification.inUse.action=Ignorer le verrou
notification.inUse.message=Le fichier est utilisé sur un autre appareil
notification.inUse.description=Le fichier est ouvert par %s sur %s. Demandez-lui à fermer le fichier et laissez la synchronisation se terminer. Vous pouvez ignorer le statut pour louvrir maintenant, mais cela pourrait entraîner des conflits ou écraser des modifications récentes.
notification.inUse.action=Ignorer le statut dutilisation

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Azioni dell'evento
eventView.entry.vaultLocked.description=Sblocca "%s" per i dettagli
eventView.entry.conflictResolved.message=Conflitto risolto
eventView.entry.conflictResolved.showDecrypted=Mostra file decifrato
eventView.entry.conflictResolved.copyDecrypted=Copia percorso decriptato
eventView.entry.conflict.message=Risoluzione dei conflitti fallita
eventView.entry.conflict.showDecrypted=Mostra il file originale decifrato
eventView.entry.conflict.copyDecrypted=Copia il percorso originale decifrato
eventView.entry.conflict.showEncrypted=Mostra file cifrato in conflitto
eventView.entry.conflict.copyEncrypted=Copia il percorso cifrato in conflitto
eventView.entry.decryptionFailed.message=Decifratura fallita
eventView.entry.decryptionFailed.showEncrypted=Mostra file cifrato
eventView.entry.decryptionFailed.copyEncrypted=Copia percorso cifrato
eventView.entry.brokenDirFile.message=Collegamento directory errato
eventView.entry.brokenDirFile.showEncrypted=Visualizza il link cifrato errato
eventView.entry.brokenDirFile.copyEncrypted=Copia il percorso errato del file cifrato
eventView.entry.brokenFileNode.message=Nodo file errato
eventView.entry.brokenFileNode.showEncrypted=Visualizza il nodo file cifrato errato
eventView.entry.brokenFileNode.copyEncrypted=Copia il percorso errato del nodo file cifrato
eventView.entry.brokenFileNode.copyDecrypted=Copia il percorso decifrato
eventView.entry.inUse.message=File bloccato
eventView.entry.inUse.message=File in uso
eventView.entry.inUse.showDecrypted=Mostra file decifrato
eventView.entry.inUse.copyDecrypted=Copia percorso decriptato
eventView.entry.inUse.showEncrypted=Mostra file cifrato
eventView.entry.inUse.copyEncrypted=Copia percorso cifrato
eventView.entry.inUse.copyUserAndDevice=Copia il nome del dispositivo e dell'utente che sta bloccando
eventView.entry.inUse.ignoreLock=Ignora blocco
eventView.entry.inUse.ignoreLock=Ignora lo stato di utilizzo
# Notifications
## FileIsInUse Notification
notification.inUse.message=Il file è bloccato da un altro dispositivo
notification.inUse.description=Il file è aperto da %s sul dispositivo %s. Chiedi all'utente di chiudere il file e sincronizzalo di nuovo. In caso contrario, puoi ignorare il blocco e aprirlo comunque.
notification.inUse.action=Ignora blocco
notification.inUse.message=Il file è in uso su un altro dispositivo
notification.inUse.description=Il file è aperto da %s su %s. Chiedigli di chiudere il file e permettere che la sincronizzazione termini. È possibile ignorare lo stato ed aprirlo ora, ma questo può causare conflitti o la sovrascrittura delle modifiche più recenti.
notification.inUse.action=Ignora lo Stato di Utilizzo

View File

@@ -626,16 +626,10 @@ decryptNames.dropZone.error.generic=ファイル名の復号化に失敗しま
# Event View
## event list entries
eventView.entry.inUse.message=ロックされたファイル
eventView.entry.inUse.showDecrypted=復号化されたファイルを表示
eventView.entry.inUse.copyDecrypted=復号化されたパスをコピー
eventView.entry.inUse.showEncrypted=暗号化されたファイルを表示
eventView.entry.inUse.copyEncrypted=暗号化されたパスをコピー
eventView.entry.inUse.copyUserAndDevice=ロックしているユーザーとデバイス名をコピー
eventView.entry.inUse.ignoreLock=ロックを無視する
# Notifications
## FileIsInUse Notification
notification.inUse.message=ファイルは別のデバイスによってロックされています
notification.inUse.action=ロックを無視する
## FileIsInUse Notification

View File

@@ -42,7 +42,7 @@ defaults.vault.vaultName=Vault
# Tray Menu
traymenu.showMainWindow=보기
traymenu.showPreferencesWindow=환경설정
traymenu.showPreferencesWindow=환경 설정
traymenu.lockAllVaults=모두 잠그기
traymenu.quitApplication=종료
traymenu.vault.unlock=잠금 해제
@@ -114,7 +114,7 @@ addvaultwizard.success.nextStepsInstructions="%s" Vault가 추가되었습니다
addvaultwizard.success.unlockNow=지금 잠금 해제
# Remove Vault
removeVault.title=Vault 제거
removeVault.title=Vault "%s" 제거
removeVault.message=Vault를 삭제하시겠습니까?
removeVault.description=이 행위는 Cryptomator에서만 이 Vault를 지웁니다. 나중에 다시 추가할 수 있습니다. 암호화된 파일은 하드디스크에서 삭제되지 않습니다.
@@ -132,18 +132,18 @@ forgetPassword.confirmBtn=비밀번호 삭제
# Unlock
unlock.title="%s" 잠금 해제
unlock.passwordPrompt="%s"의 비밀번호를 입력하십시오.
unlock.savePassword=비밀번호 기억
unlock.savePassword=비밀번호 기억하기
unlock.unlockBtn=잠금 해제
## Select
unlock.chooseMasterkey.message=마스터키 파일을 찾을 수 없습니다
unlock.chooseMasterkey.description=이 Vault의 Masterkey를 찾지 못했습니다. 마스터 키 위치를 수동으로 선택하여 주십시오.
unlock.chooseMasterkey.description= "%s" Vault의 마스터키를 찾지 못했습니다. 마스터키 위치를 수동으로 선택하여 주십시오.
unlock.chooseMasterkey.restoreInstead=대신 마스터키 파일 복구
unlock.chooseMasterkey.filePickerTitle=Masterkey 파일 선택
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
## Success
unlock.success.message=잠금 해제 성공
unlock.success.description="%s"이(가) 성공적으로 잠금 해제되었습니다. 이제 이 Vault를 마운트 지점으로 접근할 수 있습니다.
unlock.success.rememberChoice=선택 기억하기, 다시 묻지 않음
unlock.success.rememberChoice=선택 기억하 다시 묻지 않음
unlock.success.revealBtn=드라이브 표시
## Failure
unlock.error.customPath.message=Vault를 사용자 정의 경로에 마운트할 수 없습니다.
@@ -166,7 +166,7 @@ hub.auth.message=인증 대기 중…
hub.auth.description=자동으로 로그인 페이지로 리다이렉트 될 것입니다.
hub.auth.loginLink=수동으로 열려면 클릭하십시오.
### Receive Key
hub.receive.message=응답 처리중…
hub.receive.message=응답 처리 중…
hub.receive.description=Hub로부터 응답을 처리하고 있습니다. 잠시만 기다려 주십시오.
### Register Device
hub.register.message=새 기기
@@ -181,7 +181,7 @@ hub.register.legacy.description=이 기기로부터 첫번째 Hub 접근입니
hub.registerSuccess.message=기기 등록됨
hub.registerSuccess.description=등록에 성공하였습니다. Vault를 잠금 해제할 수 있습니다.
hub.registerSuccess.unlockBtn=잠금 해제
hub.registerSuccess.legacy.description=Vault에 접근하기 위해서는 이 기기를 Vault 소유가 추가적으로 허가해야 합니다.
hub.registerSuccess.legacy.description=Vault에 접근하기 위해서는 이 기기를 Vault 소유가 추가적으로 허가해야 합니다.
### Registration Failed
hub.registerFailed.message=기기 등록 실패
hub.registerFailed.description.generic=등록 중에 오류가 발생했습니다. 앱 로그에서 자세한 정보를 확인할 수 있습니다.
@@ -209,18 +209,18 @@ lock.forced.retryBtn=재시도
lock.forced.forceBtn=강제 잠금
## Failure
lock.fail.message=Vault 잠금에 실패하였습니다.
lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료 하십시오.
lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료하십시오.
# Migration
migration.title=Vault 업그레이드
## Start
migration.start.header=Vault 업그레이드
migration.start.text=Vault "%s"를 현재 버전의 Cryptomator에서 열기 위해서는 해당 vault를 새 버전으로 업그레이드해야 합니다. 업그레이드를 하기 전에 다음 사항들을 알고 있어야 합니다:
migration.start.text=Vault "%s"를 현재 버전의 Cryptomator에서 열기 위해서는 해당 Vault를 새 버전으로 업그레이드해야 합니다. 업그레이드를 하기 전에 다음 사항들을 알고 있어야 합니다:
migration.start.remarkUndone=이 업그레이드는 되돌릴 수 없습니다.
migration.start.remarkVersions=과거 버전의 Cryptomator는 업그레이드된 Vault를 열 수 없습니다.
migration.start.remarkCanRun=이 Vault를 열 때 사용하는 모든 기기가 현재 버전의 Cryptomator를 실행할 수 있는지 확인해야 합니다.
migration.start.remarkSynced=업그레이드하기 전에 해당 Vault가 모든 기기에 정상적으로 동기화되어야 합니다.
migration.start.confirm=나는 위 정보를 읽고 정말 이해했습니다.
migration.start.confirm=위 내용을 충분히 숙지하였음을 확인합니다.
## Run
migration.run.enterPassword="%s"의 비밀번호를 입력하십시오.
migration.run.startMigrationBtn=Vault 마이그레이션
@@ -231,8 +231,8 @@ migration.success.unlockNow=지금 잠금 해제
## Missing file system capabilities
migration.error.missingFileSystemCapabilities.title=지원하지 않는 파일 시스템
migration.error.missingFileSystemCapabilities.description=Vault가 부적절한 파일 시스템에 있기 때문에 마이그레이션이 시작되지 않았습니다.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=너무 긴 파일 이름을 파일 시스템에서 지원하지 않습니다.
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=너무 긴 경로를 파일 시스템에서 지원하지 않습니다.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=파일 시스템이 긴 파일 이름을 지원하지 않습니다.
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=파일 시스템이 긴 경로를 지원하지 않습니다.
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=파일 시스템이 읽기를 허용하지 않습니다.
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=파일 시스템이 쓰기를 허용하지 않습니다.
## Impossible
@@ -267,7 +267,7 @@ health.check.detail.checkSkipped=선택된 검사항목이 없습니다.
health.check.detail.checkFinished=검사가 성공적으로 완료되었습니다.
health.check.detail.checkFinishedAndFound=검사가 완료되었습니다. 검사 결과를 확인해주세요.
health.check.detail.checkFailed=오류로 인해 검사가 종료되었습니다.
health.check.detail.checkCancelled=검사가 취소되었습니다
health.check.detail.checkCancelled=검사가 취소되었습니다.
health.check.detail.listFilters.label=필터
health.check.detail.filterSeverity=중요도로 정렬
health.check.detail.filterFixState=해결 상태로 정렬
@@ -286,7 +286,7 @@ health.result.severityTip.crit=상태: 심각\nVault 구조가 손상되었습
health.result.fixStateFilter.all=모든 문제 해결 상태
health.result.fixStateFilter.fixable=문제 해결 가능
health.result.fixStateFilter.notFixable=문제 해결 불가
health.result.fixStateFilter.fixing=문제 해결중…
health.result.fixStateFilter.fixing=문제 해결 중…
health.result.fixStateFilter.fixed=문제 해결됨
health.result.fixStateFilter.fixFailed=문제 해결 실패
## Fix Application
@@ -295,7 +295,7 @@ health.fix.successTip=문제 해결이 성공적으로 완료되었습니다
health.fix.failTip=문제 해결 실패, 상세 정보는 로그를 참조하십시오.
# Preferences
preferences.title=환경설정
preferences.title=환경 설정
## General
preferences.general=일반
preferences.general.startHidden=Cryptomator를 시작할 때 창 숨김
@@ -309,9 +309,9 @@ preferences.general.quickAccessService=열린 Vault를 빠른 접근 위치에
preferences.interface=인터페이스
preferences.interface.theme=테마
preferences.interface.theme.automatic=자동
preferences.interface.theme.dark=어둡게
preferences.interface.theme.light=밝게
preferences.interface.unlockThemes=다크모드 해제
preferences.interface.theme.dark=다크 모드
preferences.interface.theme.light=라이트 모드
preferences.interface.unlockThemes=다크 모드 사용 권한을 얻어보세요!
preferences.interface.language=언어 (재시작 필요)
preferences.interface.language.auto=시스템 기본 설정
preferences.interface.interfaceOrientation=인터페이스 방향
@@ -356,7 +356,7 @@ preferences.contribute=후원하기
preferences.contribute.registeredFor=%s(으)로 후원자 인증 등록됨
preferences.contribute.noCertificate=Cryptomator를 후원하시고 후원자 인증을 받으십시오. 라이선스 키와 비슷하지만 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-)
preferences.contribute.getCertificate=아직 후원자 인증이 없으신가요? 어떻게 얻는지 배울 수 있습니다.
preferences.contribute.promptText=후원자 인증코드를 여기에 붙여넣기
preferences.contribute.promptText=후원자 인증 코드를 여기에 붙여넣기
preferences.contribute.thankYou=Cryptomator의 오픈 소스 개발을 지원해 주셔서 감사합니다!
preferences.contribute.donate=후원하기
preferences.contribute.sponsor=스폰서
@@ -364,7 +364,7 @@ preferences.contribute.removeCert.tooltip=인증서 제거
### Remove License Key Dialog
removeCert.title=인증서 제거
removeCert.message=서포터 인증서를 제거하시겠습니까?
removeCert.message=후원자 인증서를 제거하시겠습니까?
removeCert.description=Cryptomator의 핵심 기능은 영향을 받지 않습니다. Vault에 대한 접근이 제한되거나 보안이 약화되지 않습니다.
#<-- Add entries for donations and code/translation/documentation contribution -->
@@ -392,7 +392,7 @@ stats.read.accessCount=총 읽기 횟수: %d
stats.write.throughput.idle=쓰기: 대기 중
stats.write.throughput.kibs=쓰기: %.2f KiB/s
stats.write.throughput.mibs=쓰기: %.2f MiB/s
stats.write.total.data.none=데이터 기록됨: -
stats.write.total.data.none=데이터 기: -
stats.write.total.data.kib=데이터 쓰기: %.1f KiB
stats.write.total.data.mib=데이터 쓰기: %.1f MiB
stats.write.total.data.gib=데이터 쓰기: %.1f GiB
@@ -427,11 +427,11 @@ main.vaultlist.showEventsButton.tooltip=이벤트 뷰어 열기
main.vaultlist.showPreferencesButton.tooltip=환경 설정 표시
##Notification
main.notification.updateAvailable=업데이트가 있습니다.
main.notification.support=Cryptomator 지원하기.
main.notification.support=Cryptomator 지원해 주세요
main.notification.closeButton.tooltip=정보 표시줄 닫기
## Vault Detail
### Welcome
main.vaultDetail.welcomeOnboarding=파일 보호하기 위해 Cryptomator를 선택해주셔서 감사합니다. 만약 다른 도움이 필요하시면, 시작 안내서를 참조하시기 바랍니다.
main.vaultDetail.welcomeOnboarding=파일 보호 위해 Cryptomator를 선택해 주셔서 감사합니다. 도움이 필요하시면 시작 가이드를 확인해 주십시오:
main.vaultDetail.storageLocation=Vault 저장 위치
### Locked
main.vaultDetail.lockedStatus=잠김
@@ -695,32 +695,25 @@ eventView.cell.actionsButton.tooltip=이벤트별 동작
eventView.entry.vaultLocked.description="%s"를 잠금 해제하여 세부정보 보기
eventView.entry.conflictResolved.message=해결된 충돌
eventView.entry.conflictResolved.showDecrypted=복호화된 파일 보기
eventView.entry.conflictResolved.copyDecrypted=복호화된 경로 복사하기
eventView.entry.conflict.message=충돌 해결 실패
eventView.entry.conflict.showDecrypted=복호화된 원본 파일 보기
eventView.entry.conflict.copyDecrypted=복호화된 원본 경로 복사하기
eventView.entry.conflict.showEncrypted=충돌하는 암호화된 파일 보기
eventView.entry.conflict.copyEncrypted=충돌하는 암호화된 경로 복사하기
eventView.entry.decryptionFailed.message=복호화 실패
eventView.entry.decryptionFailed.showEncrypted=암호화된 파일 보기
eventView.entry.decryptionFailed.copyEncrypted=암호화된 경로 복사하기
eventView.entry.brokenDirFile.message=망가진 디렉터리 링크
eventView.entry.brokenDirFile.showEncrypted=망가진 암호화된 링크 보기
eventView.entry.brokenDirFile.copyEncrypted=망가진 링크의 경로 복사하리
eventView.entry.brokenFileNode.message=망가진 파일시스템 노드
eventView.entry.brokenFileNode.message=망가진 파일 시스템 노드
eventView.entry.brokenFileNode.showEncrypted=망가진 암호화된 노드 보기
eventView.entry.brokenFileNode.copyEncrypted=망가진 암호화된 노드의 경로 복사하기
eventView.entry.brokenFileNode.copyDecrypted=복호화된 경로 복사하기
eventView.entry.inUse.message=잠긴 파일
eventView.entry.inUse.message=파일 사용 중
eventView.entry.inUse.showDecrypted=복호화된 파일 보기
eventView.entry.inUse.copyDecrypted=복호화된 경로 복사하기
eventView.entry.inUse.showEncrypted=암호화된 파일 보기
eventView.entry.inUse.copyEncrypted=암호화된 경로 복사하기
eventView.entry.inUse.copyUserAndDevice=파일 잠금 사용자 및 기기 이름 복사
eventView.entry.inUse.ignoreLock=잠금 무시
eventView.entry.inUse.ignoreLock=사용 여부 상태 무시
# Notifications
## FileIsInUse Notification
notification.inUse.message=다른 기기에서 파일이 잠겼습니다.
notification.inUse.action=잠금 무시
notification.inUse.message=다른 기기에서 파일 사용 중
notification.inUse.description=해당 파일은 %s이(가) %s의 컴퓨터에서 열어 놓은 상태입니다. 파일을 닫고 동기화가 완료될 때까지 기다리도록 요청하세요. 상태를 무시하고 지금 파일을 열 수도 있지만, 이렇게 하면 충돌이 발생하거나 최신 변경 사항이 덮어쓰여질 수 있습니다.
notification.inUse.action=사용 여부 상태 무시

View File

@@ -694,31 +694,25 @@ eventView.cell.actionsButton.tooltip=Notikumu darbības
eventView.entry.vaultLocked.description=Atslēgt "%s", lai redzētu informāciju
eventView.entry.conflictResolved.message=Atrisināta nesaderība
eventView.entry.conflictResolved.showDecrypted=Parādīt atšifrēto datni
eventView.entry.conflictResolved.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu
eventView.entry.conflict.message=Nesaderības atrisināšana neizdevās
eventView.entry.conflict.showDecrypted=Parādīt atšifrēto, sākotnējo datni
eventView.entry.conflict.copyDecrypted=Ievietot starpliktuvē atšifrēto, sākotnējo datni
eventView.entry.conflict.showEncrypted=Parādīt nesaderīgo, šifrēto datni
eventView.entry.conflict.copyEncrypted=Ievietot starpliktuvē nesaderīgo, šifrēto ceļu
eventView.entry.decryptionFailed.message=Atšifrēšana neizdevās
eventView.entry.decryptionFailed.showEncrypted=Parādīt šifrēto datni
eventView.entry.decryptionFailed.copyEncrypted=Ievietot starpliktuvē šifrēto ceļu
eventView.entry.brokenDirFile.message=Bojāta mapes saite
eventView.entry.brokenDirFile.showEncrypted=Parādīt bojāto, šifrēto saiti
eventView.entry.brokenDirFile.copyEncrypted=Ievietot starpliktuvē bojātās saites ceļu
eventView.entry.brokenFileNode.message=Bojāts datņu sistēmas mezgls
eventView.entry.brokenFileNode.showEncrypted=Parādīt bojāto, šifrēto mezglu
eventView.entry.brokenFileNode.copyEncrypted=Ievietot starpliktuvē botjātā, šifrētā mezgla ceļu
eventView.entry.brokenFileNode.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu
eventView.entry.inUse.message=Aizslēgta datne
eventView.entry.inUse.message=Datne tiek izmantota
eventView.entry.inUse.showDecrypted=Parādīt atšifrēto datni
eventView.entry.inUse.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu
eventView.entry.inUse.showEncrypted=Parādīt šifrēto datni
eventView.entry.inUse.copyEncrypted=Ievietot starpliktuvē šifrēto ceļu
eventView.entry.inUse.copyUserAndDevice=Ievietot starpliktuvē lietotāju un ierīces, kurā aizslēgta, nosaukumu
eventView.entry.inUse.ignoreLock=Neņemt vērā slēgu
eventView.entry.inUse.ignoreLock=Neņemt vēra izmantošanas stāvokli
# Notifications
## FileIsInUse Notification
notification.inUse.action=Neņemt vērā slēgu
notification.inUse.message=Datne tiek izmantota citā ierīcē
notification.inUse.description=Datni atvēra %s ierīcē %s. Jāvaicā aizvērt datni un ļaut pabeigties sinhronizēšanai. Stāvokli tagad var neņemt vērā, lai atvērtu datni, bet tas var radīt nesaderības vai pārrakstīt jaunākas izmaiņas.
notification.inUse.action=Neņemt vēra izmantošanas stāvokli

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Acties op Gebeurtenissen
eventView.entry.vaultLocked.description=Ontgrendel "%s" voor details
eventView.entry.conflictResolved.message=Opgelost conflict
eventView.entry.conflictResolved.showDecrypted=Toon gedecodeerd bestand
eventView.entry.conflictResolved.copyDecrypted=Kopieer gedecodeerd pad
eventView.entry.conflict.message=Conflictoplossing is mislukt
eventView.entry.conflict.showDecrypted=Toon gedecodeerd, origineel bestand
eventView.entry.conflict.copyDecrypted=Kopieer gedecodeerd, origineel pad
eventView.entry.conflict.showEncrypted=Conflicterend, versleuteld bestand weergeven
eventView.entry.conflict.copyEncrypted=Conflicterend, versleuteld pad kopiëren
eventView.entry.decryptionFailed.message=Decodering mislukt
eventView.entry.decryptionFailed.showEncrypted=Toon gedecodeerd bestand
eventView.entry.decryptionFailed.copyEncrypted=Kopieer gedecodeerd pad
eventView.entry.brokenDirFile.message=Verbroken directory link
eventView.entry.brokenDirFile.showEncrypted=Toon verbroken gecodeerde link
eventView.entry.brokenDirFile.copyEncrypted=Kopieer pad van verbroken link
eventView.entry.brokenFileNode.message=Kapot bestandssysteemknooppunt
eventView.entry.brokenFileNode.showEncrypted=Toon verbroken gecodeerde link
eventView.entry.brokenFileNode.copyEncrypted=Kopieer pad van verbroken, versleuteld knooppunt
eventView.entry.brokenFileNode.copyDecrypted=Kopieer gedecodeerd pad
eventView.entry.inUse.message=Vergrendeld Bestand
eventView.entry.inUse.message=Bestand in gebruik
eventView.entry.inUse.showDecrypted=Toon gedecodeerd bestand
eventView.entry.inUse.copyDecrypted=Kopieer gedecodeerd pad
eventView.entry.inUse.showEncrypted=Toon gedecodeerd bestand
eventView.entry.inUse.copyEncrypted=Kopieer gedecodeerd pad
eventView.entry.inUse.copyUserAndDevice=Kopieer vergrendeling gebruikersnaam en apparaatnaam
eventView.entry.inUse.ignoreLock=Vergrendeling negeren
eventView.entry.inUse.ignoreLock=Gebruiksstatus negeren
# Notifications
## FileIsInUse Notification
notification.inUse.message=Bestand is vergrendeld door een ander apparaat
notification.inUse.description=Het bestand wordt geopend door %s op apparaat %s. Vraag de gebruiker om het bestand te sluiten en opnieuw te synchroniseren. Anders kunt u de vergrendeling negeren en het toch openen.
notification.inUse.action=Vergrendeling negeren
notification.inUse.message=Bestand is op een ander apparaat in gebruik
notification.inUse.description=Het bestand is geopend door %s op %s. Vraag ze om het bestand te sluiten en laat de synchronisatie eindigen. Je kunt de status negeren om het nu te openen, maar dit kan conflicten veroorzaken of nieuwere wijzigingen overschrijven.
notification.inUse.action=Gebruiksstatus negeren

View File

@@ -573,17 +573,13 @@ eventView.clearListButton.tooltip=ਸੂਚੀ ਨੂੰ ਮਿਟਾਓ
## event list entries
eventView.entry.vaultLocked.description=ਵੇਰਵਿਆਂ ਲਈ "%s" ਨੂੰ ਅਣ-ਲਾਕ ਕਰੋ
eventView.entry.conflictResolved.showDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
eventView.entry.conflictResolved.copyDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
eventView.entry.decryptionFailed.message=ਡਿਕ੍ਰਿਪਟ ਕਰਨਾ ਅਸਫ਼ਲ ਹੈ
eventView.entry.decryptionFailed.showEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
eventView.entry.decryptionFailed.copyEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
eventView.entry.brokenDirFile.message=ਡਾਇਰੈਕਟਰੀ ਲਿੰਕ ਖ਼ਰਾਬ ਹੈ
eventView.entry.brokenDirFile.showEncrypted=ਖ਼ਰਾਬ, ਇੰੰਕ੍ਰਿਪਟ ਕੀਤੇ ਲਿੰਕ ਨੂੰ ਵੇਖਾਓ
eventView.entry.brokenFileNode.copyDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
eventView.entry.inUse.showDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
eventView.entry.inUse.copyDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
eventView.entry.inUse.showEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
eventView.entry.inUse.copyEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
# Notifications

View File

@@ -695,31 +695,20 @@ eventView.cell.actionsButton.tooltip=Akcje zdarzeń
eventView.entry.vaultLocked.description=Odblokuj "%s" po szczegóły
eventView.entry.conflictResolved.message=Konflikt rozwiązany
eventView.entry.conflictResolved.showDecrypted=Pokaż odszyfrowany plik
eventView.entry.conflictResolved.copyDecrypted=Kopiuj odszyfrowaną ścieżkę
eventView.entry.conflict.message=Rozwiązywanie konfliktu nie powiodło się
eventView.entry.conflict.showDecrypted=Pokaż odszyfrowany, oryginalny plik
eventView.entry.conflict.copyDecrypted=Kopiuj odszyfrowaną, oryginalną ścieżkę
eventView.entry.conflict.showEncrypted=Pokaż sprzeczny, zaszyfrowany plik
eventView.entry.conflict.copyEncrypted=Kopiuj sprzeczną, zaszyfrowaną ścieżkę
eventView.entry.decryptionFailed.message=Odszyfrowywanie nie powiodło się
eventView.entry.decryptionFailed.showEncrypted=Pokaż zaszyfrowany plik
eventView.entry.decryptionFailed.copyEncrypted=Kopiuj zaszyfrowaną ścieżkę
eventView.entry.brokenDirFile.message=Uszkodzony link do katalogu
eventView.entry.brokenDirFile.showEncrypted=Pokaż uszkodzony, zaszyfrowany link
eventView.entry.brokenDirFile.copyEncrypted=Kopiuj ścieżkę uszkodzonego linku
eventView.entry.brokenFileNode.message=Uszkodzony węzeł systemu plików
eventView.entry.brokenFileNode.showEncrypted=Pokaż uszkodzony, zaszyfrowany węzeł
eventView.entry.brokenFileNode.copyEncrypted=Kopiuj ścieżkę uszkodzonego, zaszyfrowanego węzła
eventView.entry.brokenFileNode.copyDecrypted=Kopiuj odszyfrowaną ścieżkę
eventView.entry.inUse.message=Zablokowany plik
eventView.entry.inUse.showDecrypted=Pokaż odszyfrowany plik
eventView.entry.inUse.copyDecrypted=Kopiuj odszyfrowaną ścieżkę
eventView.entry.inUse.showEncrypted=Pokaż zaszyfrowany plik
eventView.entry.inUse.copyEncrypted=Kopiuj zaszyfrowaną ścieżkę
eventView.entry.inUse.copyUserAndDevice=Kopiowanie blokady użytkownika i nazwy urządzenia
eventView.entry.inUse.ignoreLock=Ignoruj blokadę
# Notifications
## FileIsInUse Notification
notification.inUse.action=Ignoruj blokadę
## FileIsInUse Notification

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Ações dos eventos
eventView.entry.vaultLocked.description=Desbloquear "%s" para detalhes
eventView.entry.conflictResolved.message=Conflito resolvido
eventView.entry.conflictResolved.showDecrypted=Mostrar o ficheiro desencriptado
eventView.entry.conflictResolved.copyDecrypted=Copiar o caminho desencriptado
eventView.entry.conflict.message=A resolução do conflito falhou
eventView.entry.conflict.showDecrypted=Mostrar o ficheiro original desencriptado
eventView.entry.conflict.copyDecrypted=Copie o caminho original desencriptado
eventView.entry.conflict.showEncrypted=Mostrar o ficheiro encriptado em conflito
eventView.entry.conflict.copyEncrypted=Copiar o caminho encriptado em conflito
eventView.entry.decryptionFailed.message=A desencriptação falhou
eventView.entry.decryptionFailed.showEncrypted=Mostrar ficheiro encriptado
eventView.entry.decryptionFailed.copyEncrypted=Copiar o caminho encriptado
eventView.entry.brokenDirFile.message=Link de diretório quebrado
eventView.entry.brokenDirFile.showEncrypted=Mostrar o link quebrado e encriptado
eventView.entry.brokenDirFile.copyEncrypted=Copiar caminho do link quebrado
eventView.entry.brokenFileNode.message=Nó do sistema de ficheiros avariado
eventView.entry.brokenFileNode.showEncrypted=Mostrar nó encriptado e danificado
eventView.entry.brokenFileNode.copyEncrypted=Copiar o caminho do nó encriptado e danificado
eventView.entry.brokenFileNode.copyDecrypted=Copiar o caminho desencriptado
eventView.entry.inUse.message=Ficheiro bloqueado
eventView.entry.inUse.message=Ficheiro em utilização
eventView.entry.inUse.showDecrypted=Mostrar o ficheiro desencriptado
eventView.entry.inUse.copyDecrypted=Copiar o caminho desencriptado
eventView.entry.inUse.showEncrypted=Mostrar ficheiro encriptado
eventView.entry.inUse.copyEncrypted=Copiar o caminho encriptado
eventView.entry.inUse.copyUserAndDevice=Copiar nome do utilizador e dispositivo bloqueado
eventView.entry.inUse.ignoreLock=Ignorar bloqueio
eventView.entry.inUse.ignoreLock=Ignorar estado de utilização
# Notifications
## FileIsInUse Notification
notification.inUse.message=O ficheiro está bloqueado por outro dispositivo
notification.inUse.description=O ficheiro foi aberto por %s no dispositivo %s. Peça ao utilizador para fechar o ficheiro e sincronizar novamente. Caso contrário, pode ignorar o bloqueio e abri-lo na mesma.
notification.inUse.action=Ignorar bloqueio
notification.inUse.message=O ficheiro está em utilização noutro dispositivo
notification.inUse.description=O ficheiro está aberto por %s em %s. Peça-lhes que fechem o ficheiro e deixem a sincronização terminar. Pode ignorar o estado para o abrir agora, mas isso pode causar conflitos ou sobrescrever alterações mais recentes.
notification.inUse.action=Ignorar estado de utilização

View File

@@ -27,15 +27,15 @@ vault.state.error=Erro
# Error
error.message=Ocorreu um erro
error.description=O Cryptomator não esperava que isso acontecesse. Você pode procurar soluções existentes para este erro. Ou, se ainda não foi relatado, sinta-se à vontade para fazê-lo.
error.hyperlink.lookup=Procurar este erro
error.description=O Cryptomator não esperava que isso acontecesse. Você pode buscar soluções existentes para esse erro. Se ainda não o relatou, sinta-se à vontade para fazê-lo.
error.hyperlink.lookup=Buscar este erro
error.hyperlink.report=Relatar este erro
error.technicalDetails=Detalhes:
error.existingSolutionDescription=O Cryptomator encontrou um erro inesperado, mas há uma solução pré-existente disponível no seguinte link.
error.hyperlink.solution=Procure a solução
error.lookupPermissionMessage=O Cryptomator pode procurar uma solução para este problema online. Isso enviará uma solicitação para nosso banco de dados de problemas a partir do seu endereço IP.
error.existingSolutionDescription=O Cryptomator não esperava que isso acontecesse. Encontramos uma solução existente para esse erro. Acesse o link a seguir.
error.hyperlink.solution=Busque a solução
error.lookupPermissionMessage=O Cryptomator pode buscar uma solução para esse problema on-line. Isso enviará uma solicitação para nosso banco de dados de problemas a partir do seu endereço IP.
error.dismiss=Ignorar
error.lookUpSolution=Procurar solução
error.lookUpSolution=Buscar solução
# Defaults
defaults.vault.vaultName=Cofre
@@ -66,7 +66,7 @@ addvaultwizard.new.directoryPickerButton=Escolher…
addvaultwizard.new.directoryPickerTitle=Selecionar diretório
addvaultwizard.new.fileAlreadyExists=Já existe um arquivo ou diretório com esse nome
addvaultwizard.new.locationDoesNotExist=Um diretório no caminho especificado não existe ou não pode ser acessado
addvaultwizard.new.locationIsNotWritable=Sem acesso de gravação no caminho especificado
addvaultwizard.new.locationIsNotWritable=Nenhum acesso de gravação no caminho especificado
addvaultwizard.new.locationIsOk=Local adequado para o seu cofre
addvaultwizard.new.invalidName=Nome do cofre inválido
addvaultwizard.new.validName=Nome do cofre válido
@@ -82,7 +82,7 @@ addvaultwizard.new.expertSettings.shorteningThreshold.title=Comprimento máximo
addvaultwizard.new.expertSettings.shorteningThreshold.valid=Válido
### Password
addvaultwizard.new.createVaultBtn=Criar cofre
addvaultwizard.new.generateRecoveryKeyChoice=Você não será capaz de acessar seus dados sem sua senha. Você quer uma chave de recuperação para o caso de perder sua senha?
addvaultwizard.new.generateRecoveryKeyChoice=Você será incapaz de acessar os seus dados sem a sua senha. Deseja uma chave de recuperação para caso perca a sua senha?
addvaultwizard.new.generateRecoveryKeyChoice.yes=Sim, por favor. Melhor prevenir do que remediar
addvaultwizard.new.generateRecoveryKeyChoice.no=Não, obrigado. Eu não perderei minha senha
### Information
@@ -100,33 +100,33 @@ addvault.new.readme.storageLocation.10=Se precisar de ajuda, visite a documenta
addvault.new.readme.accessLocation.fileName=BEM-VINDO(A).rtf
addvault.new.readme.accessLocation.1=🔐️ VOLUME ENCRIPTADO 🔐️
addvault.new.readme.accessLocation.2=Este é o local de acesso ao seu cofre.
addvault.new.readme.accessLocation.3=Todos os arquivos adicionados a este volume serão criptografados de forma transparente pelo Cryptomator. Você pode trabalhar nele como se fosse qualquer unidade/pasta. Esta é apenas uma visão não criptografada do seu conteúdo, mas seus arquivos permanecem criptografados na sua unidade o tempo todo.
addvault.new.readme.accessLocation.3=Qualquer arquivo adicionado a este volume será criptografado pelo Cryptomator. Você pode trabalhar nele como em qualquer outro disco ou pasta. Esta é apenas uma visualização descriptografada do conteúdo, seus arquivos permanecem criptografados no seu disco rígido o tempo todo.
addvault.new.readme.accessLocation.4=Fique à vontade para apagar este arquivo.
## Existing
addvaultwizard.existing.title=Adicionar cofre existente
addvaultwizard.existing.instruction=Escolha o arquivo "vault.cryptomator" do seu cofre existente. Caso exista apenas um arquivo chamado "masterkey.cryptomator", selecione outro.
addvaultwizard.existing.restore=Restaurar…
addvaultwizard.existing.chooseBtn=Selecionar…
addvaultwizard.existing.filePickerTitle=Selecionar arquivo do Cofre
addvaultwizard.existing.chooseBtn=Escolher…
addvaultwizard.existing.filePickerTitle=Selecionar arquivo do cofre
addvaultwizard.existing.filePickerMimeDesc=Cofre Cryptomator
## Success
addvaultwizard.success.nextStepsInstructions=Cofre "%s" adicionado.\nVocê precisa desbloquear este cofre para acessar ou adicionar conteúdo. Você também pode desbloqueá-lo a qualquer momento.
addvaultwizard.success.nextStepsInstructions=Cofre "%s" adicionado.\nVocê precisa desbloqueá-lo para acessar ou adicionar conteúdo. O desbloqueio dele pode ser feito a qualquer momento.
addvaultwizard.success.unlockNow=Desbloquear agora
# Remove Vault
removeVault.title=Remover o cofre "%s"
removeVault.message=Remover o cofre?
removeVault.description=Isso só fará com que o Cryptomator esqueça esse cofre. Você pode adicioná-lo novamente. Nenhum arquivo criptografado será excluído do seu disco rígido.
removeVault.description=Isto só fará com que o Cryptomator esqueça este cofre. Você pode adicioná-lo novamente. Nenhum arquivo criptografado será apagado do seu disco rígido.
# Change Password
changepassword.title=Alterar senha
changepassword.enterOldPassword=Digite a senha atual para o cofre "%s"
changepassword.finalConfirmation=Eu entendo que não poderei acessar os meus dados caso esqueça a minha senha
changepassword.finalConfirmation=Estou ciente que não conseguirei acessar os meus dados caso esqueça a minha senha
# Forget Password
forgetPassword.title=Esqueci a senha
forgetPassword.message=Esqueceu sua senha?
forgetPassword.description=Isso excluirá a senha que você salvou deste cofre, do chaveiro de senhas do sistema.
forgetPassword.description=Isto apagará a senha salva deste cofre do chaveiro do seu sistema.
forgetPassword.confirmBtn=Esqueci a senha
# Unlock
@@ -137,28 +137,29 @@ unlock.unlockBtn=Desbloquear
## Select
unlock.chooseMasterkey.message=O arquivo Masterkey foi não encontrado
unlock.chooseMasterkey.description=Não foi possível encontrar o arquivo masterkey para o cofre "%s". Selecione manualmente o arquivo contendo a chave.
unlock.chooseMasterkey.restoreInstead=Em vez disso, restaure o arquivo masterkey
unlock.chooseMasterkey.filePickerTitle=Selecione o arquivo Masterkey
unlock.chooseMasterkey.filePickerMimeDesc=Masterkey do Cryptomator
## Success
unlock.success.message=Desbloqueado com sucesso
unlock.success.description="%s" desbloqueado com êxito! Seu cofre agora está acessível na unidade virtual.
unlock.success.rememberChoice=Lembrar opção escolhida, não mostrar isto novamente
unlock.success.description=O conteúdo no cofre "%s" agora está acessível no seu ponto de montagem.
unlock.success.rememberChoice=Lembrar opção escolhida. Não perguntar novamente
unlock.success.revealBtn=Revelar volume
## Failure
unlock.error.customPath.message=Não foi possível iniciar o cofre no caminho personalizado
unlock.error.customPath.description.notSupported=Se você deseja continuar usando o caminho personalizado, por favor vá para as preferências e selecione um tipo de volume que o suporte. Caso contrário, vá para as opções de cofre e escolha um ponto de montagem suportado.
unlock.error.customPath.description.notExists=O caminho de montagem personalizado não existe. Crie o caminho em seu sistema de arquivos local, ou o altere nas opções de cofre.
unlock.error.customPath.description.inUse=A letra da unidade ou o caminho de montagem "%s" já está em uso.
unlock.error.customPath.description.hideawayNotDir=O arquivo oculto e temporário "%3$s" usado para desbloquear não pôde ser removido. Exclua-o manualmente.
unlock.error.customPath.description.couldNotBeCleaned=Seu cofre não pôde ser montado no caminho "%s". Tente novamente ou escolha um caminho diferente.
unlock.error.customPath.description.notEmptyDir=O caminho de montagem escolhido "%s" não é uma pasta vazia. Escolha uma pasta vazia e tente novamente.
unlock.error.customPath.description.notSupported=Se você deseja continuar usando o caminho personalizado, acesse as preferências e selecione um tipo de volume que seja compatível. Caso contrário, vá para as opções do cofre e escolha um ponto de montagem suportado.
unlock.error.customPath.description.notExists=O caminho de montagem personalizado não existe. Crie o caminho em seu sistema de arquivos local ou o altere nas opções do cofre.
unlock.error.customPath.description.inUse=A letra de unidade ou o caminho de montagem "%s" já está em uso.
unlock.error.customPath.description.hideawayNotDir=Não foi possível remover o arquivo oculto e temporário "%3$s". Verifique-o e depois apague ele manualmente.
unlock.error.customPath.description.couldNotBeCleaned=Não foi possível montar seu cofre no caminho "%s". Tente novamente ou escolha um caminho diferente.
unlock.error.customPath.description.notEmptyDir=O caminho de montagem personalizado escolhido "%s" não é uma pasta vazia. Escolha uma pasta vazia e tente novamente.
unlock.error.customPath.description.generic=Você selecionou um caminho de montagem personalizado para este cofre, mas falhou com a mensagem: %2$s
unlock.error.restartRequired.message=Não foi possível desbloquear o cofre
unlock.error.restartRequired.description=Altere o tipo de volume nas opções do cofre ou reinicie o Cryptomator.
unlock.error.title=Falha ao desbloquear "%s"
## Hub
hub.noKeychain.message=Não foi possível acessar a chave do dispositivo
hub.noKeychain.description=Para desbloquear os cofres centrais, é necessário a chave do dispositivo, protegida por um chaveiro. Para proceder, ative "%s" e selecione um chaveiro nas preferências.
hub.noKeychain.description=Para desbloquear os cofres do Hub, você precisa de uma chave do dispositivo que fica protegida usando um chaveiro. Para continuar, ative "%s" e escolha um chaveiro nas preferências.
hub.noKeychain.openBtn=Abrir preferências
### Waiting
hub.auth.message=Aguardando autenticação…
@@ -166,34 +167,34 @@ hub.auth.description=Você será redirecionado automaticamente para a página de
hub.auth.loginLink=Não foi redirecionado? Clique aqui para abrir.
### Receive Key
hub.receive.message=Processando resposta…
hub.receive.description=Cryptomator está recebendo e processando a resposta do Hub. Por favor, aguarde.
hub.receive.description=O Cryptomator está recebendo e processando a resposta do Hub. Aguarde.
### Register Device
hub.register.message=Novo dispositivo
hub.register.description=Este é o primeiro acesso do Hub neste dispositivo. Por favor, registre-o usando sua Chave de Conta.
hub.register.description=Este é o primeiro acesso do Hub neste dispositivo. Registre-o usando sua Chave de Conta.
hub.register.nameLabel=Nome do dispositivo
hub.register.invalidAccountKeyLabel=Chave da conta inválida
hub.register.registerBtn=Registrar
### Register Device Legacy
hub.register.legacy.occupiedMsg=Esse nome já está em uso
hub.register.legacy.description=Este é o primeiro acesso do Hub neste dispositivo. Por favor, registre-o.
hub.register.legacy.description=Este é o primeiro acesso do Hub neste dispositivo. Registre-o.
### Registration Success
hub.registerSuccess.message=Dispositivo registrado
hub.registerSuccess.description=Seu dispositivo foi registrado com sucesso. Agora você pode continuar a desbloquear o cofre.
hub.registerSuccess.description=Seu dispositivo foi registrado com sucesso. Prossiga com o desbloqueio do cofre.
hub.registerSuccess.unlockBtn=Desbloquear
hub.registerSuccess.legacy.description=Para acessar o cofre, seu dispositivo precisa ser autorizado pelo proprietário do cofre.
### Registration Failed
hub.registerFailed.message=Falha ao registrar o dispositivo
hub.registerFailed.description.generic=Um erro ocorreu no processo de registro. Para mais detalhes, verifique o log da aplicação.
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo já está registrado para um usuário diferente. Tente alterar a conta de usuário ou use um dispositivo diferente.
hub.registerFailed.description.generic=Ocorreu um erro no processo de registro. Para mais detalhes, verifique o registro da aplicação.
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo já está registrado para um usuário diferente. Tente alterar a conta de usuário ou use outro dispositivo.
### Archived
hub.archived.message=Cofre arquivado
hub.archived.description=Este cofre foi arquivo e está inacessível. Contate o proprietário dele.
### Unauthorized
hub.unauthorized.message=Acesso negado
hub.unauthorized.description=Você não está autorizado a abrir este cofre. Entre em contato com o proprietário do cofre para solicitar acesso.
hub.unauthorized.description=Você não tem autorização para abrir este cofre. Entre em contato com o proprietário dele para solicitar acesso.
### Requires Account Initialization
hub.requireAccountInit.message=Ação necessária
hub.requireAccountInit.description.0=Para prosseguir, por favor, complete os passos necessários
hub.requireAccountInit.description.0=Para prosseguir, complete as etapas obrigatórias no seu
hub.requireAccountInit.description.1=Perfil de usuário do Hub
hub.requireAccountInit.description.2=.
### License Exceeded
@@ -203,65 +204,65 @@ hub.invalidLicense.description=Sua instância do Cryptomator Hub tem uma licenç
# Lock
## Force
lock.forced.message=Bloqueio falhou
lock.forced.description=Trancar "%s" foi bloqueado por operações pendentes ou arquivos abertos. Você pode forçar o bloqueio deste cofre, no entanto, a interrupção pode resultar em perda de dados não salvos.
lock.forced.description=O fechamento do cofre "%s" foi impedido por arquivos abertos ou tarefas em andamento. Você pode forçar o fechamento, mas interromper a gravação de dados pode causar a perda de arquivos não salvos.
lock.forced.retryBtn=Tentar novamente
lock.forced.forceBtn=Forçar bloqueio
## Failure
lock.fail.message=O bloqueio do cofre falhou.
lock.fail.description=Cofre "%s" não pôde ser bloqueado. Certifique-se de que o trabalho não salvo está salvo em outro lugar e que operações de Leitura/Escrita importantes sejam concluídas. Para fechar o cofre, encerre o processo do Cryptomator.
lock.fail.description=Não foi possível bloquear o cofre "%s". Certifique-se de que o trabalho não salvo foi armazenado em outro local e que as operações importantes de leitura/gravação foram concluídas. Para fechar o cofre, encerre o processo do Cryptomator.
# Migration
migration.title=Atualizar cofre
## Start
migration.start.header=Atualizar cofre
migration.start.text=Para abrir o seu cofre "%s" nesta nova versão do Cryptomator, o cofre precisa ser atualizado para um formato mais recente. Antes de fazer isso, você deve saber o seguinte:
migration.start.text=Para abrir seu cofre "%s" nesta nova versão do Cryptomator, o cofre precisa ser atualizado para um formato mais recente. Antes de realizar esse procedimento, esteja ciente do seguinte:
migration.start.remarkUndone=Esta melhoria não pode ser desfeita.
migration.start.remarkVersions=Versões mais antigas do Cryptomator não conseguirão abrir o cofre na versão atualizada.
migration.start.remarkCanRun=Você deve ter certeza de que todos os dispositivos a partir dos quais você acessar o cofre podem executar esta versão do Cryptomator.
migration.start.remarkSynced=Você deve ter certeza de que o seu cofre está totalmente sincronizado neste dispositivo e em outros dispositivos, antes de atualizá-lo.
migration.start.confirm=Eu li e entendi a informação acima
migration.start.remarkCanRun=Garanta que todos os dispositivos que você usa para acessar o cofre consigam executar esta versão do Cryptomator.
migration.start.remarkSynced=Antes de atualizá-lo, tenha certeza de que seu cofre está totalmente sincronizado neste e em outros dispositivos.
migration.start.confirm=Li e estou ciente das informações acima
## Run
migration.run.enterPassword=Digite a senha para "%s"
migration.run.startMigrationBtn=Migrar cofre
migration.run.progressHint=Isso pode levar algum tempo…
## Success
migration.success.nextStepsInstructions="%s" migrado com sucesso.\nVocê agora pode desbloquear este cofre.
migration.success.nextStepsInstructions="%s" migrado com sucesso.\nAgora você pode desbloquear este cofre.
migration.success.unlockNow=Desbloquear agora
## Missing file system capabilities
migration.error.missingFileSystemCapabilities.title=Sistema de arquivos incompatível
migration.error.missingFileSystemCapabilities.description=A migração não foi iniciada porque o seu cofre está localizado em um sistema de arquivos inadequado.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=O sistema de arquivos não suporta arquivos com nome longo.
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=O sistema de arquivos não suporta arquivos com nomes longos.
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=O sistema de arquivos não suporta caminhos longos.
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=O sistema de arquivos não permite leitura.
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=O sistema de arquivos não permite ser gravado.
## Impossible
migration.impossible.heading=Não é possível migrar o cofre
migration.impossible.reason=O cofre não pode ser migrado automaticamente porque sua localização de armazenamento ou o ponto de acesso não é compatível.
migration.impossible.reason=O cofre não pode ser migrado automaticamente porque sua localização de armazenamento ou o ponto de acesso é incompatível.
migration.impossible.moreInfo=O cofre ainda pode ser aberto com uma versão mais antiga. Para instruções sobre como migrar um cofre manualmente, visite
# Health Check
## Start
health.title=Verificação de saúde de "%s"
health.intro.header=Verificação de Saúde
health.intro.text=A Verificação de Saúde é um grupo de verificações que detectam e tentam corrigir problemas na estrutura interna do cofre. Lembre-se de:
health.intro.remarkSync=Certifique-se de que todos os dispositivos estejam completamente sincronizados, isso resolve a maioria dos problemas.
health.intro.remarkFix=Nem todos os problemas podem ser corrigidos.
health.intro.remarkBackup=Se os dados estiverem corrompidos, apenas um backup poderá ajudar.
health.intro.affirmation=Eu li e entendi a informação acima
health.intro.text=A verificação de saúde é um grupo de verificações que detectam e tentam corrigir problemas na estrutura interna do cofre. Lembre-se de:
health.intro.remarkSync=Certifique-se de que todos os dispositivos estejam completamente sincronizados. Isso resolve a maioria dos problemas.
health.intro.remarkFix=A maioria dos problema podem ser corrigidos.
health.intro.remarkBackup=Caso os dados corrompam, apenas um backup poderá ajudar.
health.intro.affirmation=Li e estou ciente das informações acima
## Start Failure
health.fail.header=Erro ao carregar configuração do Cofre
health.fail.header=Erro ao carregar a configuração do cofre
health.fail.ioError=Ocorreu um erro ao acessar e ler o arquivo de configuração.
health.fail.parseError=Ocorreu um erro ao analisar a configuração do cofre.
health.fail.moreInfo=Mais informações
## Check Selection
health.checkList.description=Selecione as verificações na lista esquerda ou use os botões abaixo.
health.checkList.description=Selecione as verificações na lista à esquerda ou use os botões abaixo.
health.checkList.selectAllButton=Selecionar todas as verificações
health.checkList.deselectAllButton=Desmarcar todas as verificações
health.check.runBatchBtn=Executar Verificações
health.check.runBatchBtn=Executar verificações selecionadas
## Detail view
health.check.detail.noSelectedCheck=Para ver os resultados, selecione uma verificação na lista à esquerda.
health.check.detail.noSelectedCheck=Para ver os resultados, selecione uma verificação de saúde na lista à esquerda.
health.check.detail.checkScheduled=A verificação está programada.
health.check.detail.checkRunning=A verificação está rodando…
health.check.detail.checkRunning=A verificação está em execução…
health.check.detail.checkSkipped=A verificação não foi selecionada para execução.
health.check.detail.checkFinished=A verificação terminou com sucesso.
health.check.detail.checkFinishedAndFound=A verificação terminou. Verifique os resultados.
@@ -270,21 +271,21 @@ health.check.detail.checkCancelled=A verificação foi cancelada.
health.check.detail.listFilters.label=Filtro
health.check.detail.filterSeverity=Filtrar por severidade
health.check.detail.filterFixState=Filtrar por estado de correção
health.check.detail.fixAllSpecificBtn=Reparar todos deste tipo
health.check.exportBtn=Exportar Relatório
health.check.detail.fixAllSpecificBtn=Corrigir todos deste tipo
health.check.exportBtn=Exportar relatório
## Result view
health.result.severityFilter.all=Gravidade - Todas
health.result.severityFilter.good=Bom
health.result.severityFilter.all=Gravidade Todas
health.result.severityFilter.good=Boa
health.result.severityFilter.info=Informação
health.result.severityFilter.warn=Aviso
health.result.severityFilter.crit=Crítico
health.result.severityFilter.warn=Alerta
health.result.severityFilter.crit=Crítica
health.result.severityTip.good=Severidade: Boa\nEstrutura do cofre normal.
health.result.severityTip.info=Gravidade: Info\nEstrutura de cofre intacta, correção sugerida.
health.result.severityTip.warn=Gravidade: Aviso\nEstrutura de cofre corrompida, correção altamente aconselhada.
health.result.severityTip.crit=Gravidade: Crítica\nEstrutura do cofre corrompida, haverá perda de dados.
health.result.fixStateFilter.all=Estado de reparo - Todos
health.result.fixStateFilter.fixable=Reparável
health.result.fixStateFilter.notFixable=Não reparável
health.result.severityTip.info=Gravidade: Informação\nEstrutura de cofre intacta. Recomenda-se correção.
health.result.severityTip.warn=Gravidade: Alerta\nEstrutura de cofre corrompida. É altamente recomendado realizar a correção.
health.result.severityTip.crit=Gravidade: Crítica\nEstrutura do cofre corrompida. Os dados poderão ser perdidos.
health.result.fixStateFilter.all=Estado de reparo Todos
health.result.fixStateFilter.fixable=Corrigível
health.result.fixStateFilter.notFixable=Incorrigível
health.result.fixStateFilter.fixing=Corrigindo…
health.result.fixStateFilter.fixed=Corrigido
health.result.fixStateFilter.fixFailed=Correção falhou
@@ -301,13 +302,13 @@ preferences.general.startHidden=Ocultar janela ao iniciar o Cryptomator
preferences.general.autoCloseVaults=Bloquear cofres sem perguntar ao sair do aplicativo
preferences.general.debugLogging=Ativar registro de depuração
preferences.general.debugDirectory=Mostrar arquivos de registro
preferences.general.autoStart=Iniciar o Cryptomator ao inicializar o sistema
preferences.general.autoStart=Iniciar o Cryptomator com o sistema
preferences.general.keychainBackend=Armazenar senhas com
preferences.general.quickAccessService=Adicione cofres desbloqueados à área de acesso rápido
## Interface
preferences.interface=Interface
preferences.interface.theme=Aparência
preferences.interface.theme.automatic=Automático
preferences.interface.theme.automatic=Automática
preferences.interface.theme.dark=Escura
preferences.interface.theme.light=Clara
preferences.interface.unlockThemes=Desbloquear o modo escuro
@@ -317,35 +318,35 @@ preferences.interface.interfaceOrientation=Orientação da interface
preferences.interface.interfaceOrientation.ltr=Da esquerda para a direita
preferences.interface.interfaceOrientation.rtl=Da direita para a esquerda
preferences.interface.showTrayIcon=Mostrar ícone na barra do sistema (requer reinicialização)
preferences.interface.compactMode=Habilitar lista de cofres compactos
preferences.interface.compactMode=Ativar lista de cofres compacta
## Volume
preferences.volume=Volume Virtual
preferences.volume=Volume virtual
preferences.volume.type=Tipo de volume padrão
preferences.volume.type.automatic=Automático
preferences.volume.docsTooltip=Abra a documentação para saber mais sobre os diferentes tipos de volumes.
preferences.volume.fuseRestartRequired=Para aplicar as mudanças, o Cryptomator precisa ser reiniciado.
preferences.volume.tcp.port=Porta TCP Padrão
preferences.volume.docsTooltip=Acesse a documentação para saber mais sobre os diferentes tipos de volumes.
preferences.volume.fuseRestartRequired=Para aplicar as alterações, o Cryptomator precisa ser reiniciado.
preferences.volume.tcp.port=Porta TCP padrão
preferences.volume.supportedFeatures=O tipo de volume escolhido suporta os seguintes recursos:
preferences.volume.feature.mountAuto=Seleção automática de ponto de montagem
preferences.volume.feature.mountToDir=Diretório personalizado como ponto de montagem
preferences.volume.feature.mountToDriveLetter=Cartaz como ponto de montagem
preferences.volume.feature.mountFlags=Opções customizadas
preferences.volume.feature.readOnly=Montaria somente leitura
preferences.volume.feature.mountToDriveLetter=Letra de unidade como ponto de montagem
preferences.volume.feature.mountFlags=Opções personalizadas de montagem
preferences.volume.feature.readOnly=Montagem de somente leitura
## Updates
preferences.updates=Atualizações
preferences.updates.currentVersion=Versão atual: %s
preferences.updates.autoUpdateCheck=Buscar atualizações automaticamente
preferences.updates.checkNowBtn=Buscar Agora
preferences.updates.checkNowBtn=Verificar agora
preferences.updates.updateAvailable=Atualizar para versão %s disponível.
preferences.updates.lastUpdateCheck=Última verificação: %s
preferences.updates.lastUpdateCheck.never=nunca
preferences.updates.lastUpdateCheck.recently=recentemente
preferences.updates.lastUpdateCheck.daysAgo=%s dia atrás
preferences.updates.lastUpdateCheck.hoursAgo=%s horas atrás
preferences.updates.lastUpdateCheck.daysAgo=%s dias
preferences.updates.lastUpdateCheck.hoursAgo=%s horas
preferences.updates.prohibitedDueToUnlockedVaults.1=Por favor
preferences.updates.prohibitedDueToUnlockedVaults.2=bloqueie seus cofres
preferences.updates.prohibitedDueToUnlockedVaults.3=para instalar a atualização.
preferences.updates.checkFailed=Procura por atualizações falhou. Por favor, verifique sua conexão com a internet ou tente novamente mais tarde.
preferences.updates.checkFailed=A busca por atualizações falhou. Verifique sua conexão à rede ou tente novamente mais tarde.
preferences.updates.updateFailed=Falha na atualização. Instale-a manualmente.
preferences.updates.upToDate=O Cryptomator está atualizado.
preferences.updates.visitDownloadPage=Visite a página de download
@@ -354,16 +355,16 @@ preferences.updates.visitDownloadPage=Visite a página de download
preferences.contribute=Apoie-nos
preferences.contribute.registeredFor=Certificado de apoiador registrado para %s
preferences.contribute.noCertificate=Apoie o Cryptomator e receba um certificado de apoiador. Ele é como uma chave de licença para pessoas maravilhosas que usam software livre. ;-)
preferences.contribute.getCertificate=Não tem um ainda? Saiba como obtê-lo.
preferences.contribute.getCertificate=Ainda não tem um? Saiba como obtê-lo.
preferences.contribute.promptText=Cole o código do certificado de apoiador aqui
preferences.contribute.thankYou=Obrigado por apoiar o desenvolvimento open-source do Cryptomator!
preferences.contribute.thankYou=Obrigado por apoiar o desenvolvimento de código aberto do Cryptomator!
preferences.contribute.donate=Doar
preferences.contribute.sponsor=Patrocinador
preferences.contribute.sponsor=Apoiador
preferences.contribute.removeCert.tooltip=Remover certificado
### Remove License Key Dialog
removeCert.title=Excluir Certificado
removeCert.message=Excluir certificado de apoiador?
removeCert.title=Remover certificado
removeCert.message=Remover o certificado de apoiador?
removeCert.description=Os principais recursos do Cryptomador não são afetados por isso. Nem o acesso aos seus cofres é restrito, nem o nível de segurança é reduzido.
#<-- Add entries for donations and code/translation/documentation contribution -->
@@ -373,13 +374,13 @@ preferences.about.thirdPartyLicenses=Licenças de terceiros
# Vault Statistics
stats.title=Estatísticas para %s
stats.cacheHitRate=Taxa de Utilização do Cache
stats.cacheHitRate=Taxa de uso do cache
## Read
stats.read.throughput.idle=Leitura: ociosa
stats.read.throughput.kibs=Leitura: %.2f MiB/s
stats.read.throughput.kibs=Leitura: %.2f KiB/s
stats.read.throughput.mibs=Leitura: %.2f MiB/s
stats.read.total.data.none=Dados lidos: -
stats.read.total.data.kib=Dados lidos: %.1f GiB
stats.read.total.data.kib=Dados lidos: %.1f KiB
stats.read.total.data.mib=Dados lidos: %.1f MiB
stats.read.total.data.gib=Dados lidos: %.1f GiB
stats.decr.total.data.none=Dados descriptografados: -
@@ -414,7 +415,7 @@ main.vaultlist.emptyList.onboardingInstruction=Clique aqui para adicionar um cof
main.vaultlist.contextMenu.remove=Remover…
main.vaultlist.contextMenu.lock=Bloquear
main.vaultlist.contextMenu.unlock=Desbloquear…
main.vaultlist.contextMenu.unlockNow=Desbloquear Agora
main.vaultlist.contextMenu.unlockNow=Desbloquear agora
main.vaultlist.contextMenu.vaultoptions=Mostrar opções do cofre
main.vaultlist.contextMenu.reveal=Revelar volume
main.vaultlist.contextMenu.share=Compartilhar…
@@ -435,7 +436,7 @@ main.vaultDetail.storageLocation=Local de armazenamento do cofre
### Locked
main.vaultDetail.lockedStatus=BLOQUEADO
main.vaultDetail.unlockBtn=Desbloquear…
main.vaultDetail.unlockNowBtn=Desbloquear Agora
main.vaultDetail.unlockNowBtn=Desbloquear agora
main.vaultDetail.optionsBtn=Opções do cofre
main.vaultDetail.passwordSavedInKeychain=Senha salva
main.vaultDetail.share=Compartilhar…
@@ -450,94 +451,98 @@ main.vaultDetail.bytesPerSecondWritten=Gravação:
main.vaultDetail.throughput.idle=ocioso
main.vaultDetail.throughput.kbps=%.1f KiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Estatísticas do Cofre
main.vaultDetail.locateEncryptedFileBtn=Localizar Arquivo Criptografado
main.vaultDetail.stats=Estatísticas do cofre
main.vaultDetail.locateEncryptedFileBtn=Localizar arquivo criptografado
main.vaultDetail.locateEncryptedFileBtn.tooltip=Escolha um arquivo do seu cofre para localizar sua versão criptografada
main.vaultDetail.encryptedPathsCopied=Caminhos copiados para a Área de Transferência!
main.vaultDetail.locateEncrypted.filePickerTitle=Selecionar Arquivo no Cofre
main.vaultDetail.decryptName.buttonLabel=Descriptografar Nome do Arquivo
main.vaultDetail.encryptedPathsCopied=Caminhos copiados para a área de transferência!
main.vaultDetail.locateEncrypted.filePickerTitle=Selecionar arquivo no cofre
main.vaultDetail.decryptName.buttonLabel=Descriptografar nome do arquivo
main.vaultDetail.decryptName.tooltip=Escolha um arquivo criptografado do cofre para descriptografar seu nome
### Missing
main.vaultDetail.missing.info=O Cryptomator não encontrou um cofre neste caminho.
main.vaultDetail.missing.info=O Cryptomator não encontrou um cofre nesse caminho.
main.vaultDetail.missing.recheck=Verificar novamente
main.vaultDetail.missing.remove=Remover da lista de cofres…
main.vaultDetail.missing.changeLocation=Alterar Localização do Cofre…
main.vaultDetail.missing.changeLocation=Alterar localização do cofre…
### Missing Vault Config
main.vaultDetail.missingVaultConfig.info=A configuração do cofre está faltando.
main.vaultDetail.missingVaultConfig.restore=Restaurar configuração do cofre
### Needs Migration
main.vaultDetail.migrateButton=Atualizar Cofre
main.vaultDetail.migratePrompt=Seu cofre precisa ser atualizado para um novo formato antes de poder acessá-lo
main.vaultDetail.migrateButton=Atualizar cofre
main.vaultDetail.migratePrompt=Seu cofre precisa ser atualizado para um novo formato antes de conseguir acessá-lo
### Error
main.vaultDetail.error.info=Ocorreu um erro ao carregar o cofre do disco.
main.vaultDetail.error.reload=Recarregar
main.vaultDetail.error.windowTitle=Erro ao carregar cofre
main.vaultDetail.error.windowTitle=Erro ao carregar o cofre
# Wrong File Alert
wrongFileAlert.title=Como Encriptar Arquivos
wrongFileAlert.message=Você tentou encriptar esses arquivos?
wrongFileAlert.description=Para este propósito, o Cryptomator fornece um volume no seu gerenciador de arquivos do sistema.
wrongFileAlert.instruction.0=Para encriptar arquivos, siga estes passos:
wrongFileAlert.instruction.1=1. Desbloqueie o seu cofre.
wrongFileAlert.title=Como criptografar os arquivos
wrongFileAlert.message=Você tentou criptografar estes arquivos?
wrongFileAlert.description=Para esta finalidade, o Cryptomator fornece um volume no seu gerenciador de arquivos do sistema.
wrongFileAlert.instruction.0=Para criptografar os arquivos, siga as etapas abaixo:
wrongFileAlert.instruction.1=1. Desbloqueie o seu cofre;
wrongFileAlert.instruction.2=2. Clique em "Revelar" para abrir o volume no seu gerenciador de arquivos.
wrongFileAlert.instruction.3=3. Adicione seus arquivos a este volume.
wrongFileAlert.instruction.3=3. Adicione seus arquivos a esse volume;
wrongFileAlert.link=Para obter assistência, visite
# Vault Options
## General
vaultOptions.general=Geral
vaultOptions.general.vaultName=Nome do cofre
vaultOptions.general.autoLock.lockAfterTimePart1=Bloquear quando inativo por
vaultOptions.general.autoLock.lockAfterTimePart1=Bloquear quando inativo após
vaultOptions.general.autoLock.lockAfterTimePart2=minuto(s)
vaultOptions.general.unlockAfterStartup=Desbloqueie o cofre ao iniciar o Cryptomator
vaultOptions.general.autoLock.accessibleText=Bloquear tempo limite em minutos
vaultOptions.general.unlockAfterStartup=Desbloquear o cofre ao iniciar o Cryptomator
vaultOptions.general.actionAfterUnlock=Após desbloquear
vaultOptions.general.actionAfterUnlock.ignore=Não fazer nada
vaultOptions.general.actionAfterUnlock.reveal=Revelar volume
vaultOptions.general.actionAfterUnlock.ask=Perguntar
vaultOptions.general.startHealthCheckBtn=Iniciar Exame de Saúde
vaultOptions.general.startHealthCheckBtn=Iniciar verificação de saúde
## Mount
vaultOptions.mount=Montagem
vaultOptions.mount.info=Abra preferências de drive virtual para alterar as configurações padrão.
vaultOptions.mount.readonly=Somente Leitura
vaultOptions.mount.customMountFlags=Flags Personalizadas de Montagem
vaultOptions.mount.info=Abra preferências do volume virtual para alterar as configurações padrões.
vaultOptions.mount.readonly=Somente leitura
vaultOptions.mount.customMountFlags=Flags personalizadas de montagem
vaultOptions.mount.winDriveLetterOccupied=ocupado
vaultOptions.mount.mountPoint=Ponto de Montagem
vaultOptions.mount.mountPoint=Ponto de montagem
vaultOptions.mount.mountPoint.auto=Escolher automaticamente um local adequado
vaultOptions.mount.mountPoint.driveLetter=Usar letra de unidade atribuída
vaultOptions.mount.mountPoint.custom=Usar diretório escolhido
vaultOptions.mount.mountPoint.directoryPickerButton=Escolher…
vaultOptions.mount.mountPoint.directoryPickerTitle=Escolha um diretório
vaultOptions.mount.volumeType.default=Padrão (%s)
vaultOptions.mount.volumeType.restartRequired=Para usar esse tipo de volume, o Cryptomator precisa ser reiniciado.
vaultOptions.mount.volumeType.restartRequired=Para usar este tipo de volume, o Cryptomator precisa ser reiniciado.
vaultOptions.mount.volume.tcp.port=Porta TCP
vaultOptions.mount.volume.type=Tipo de Volume
vaultOptions.mount.volume.type=Tipo de volume
## Master Key
vaultOptions.masterkey=Senha
vaultOptions.masterkey.changePasswordBtn=Alterar senha
vaultOptions.masterkey.forgetSavedPasswordBtn=Esquecer a senha salva
vaultOptions.masterkey.recoveryKeyExplanation=Se você perder a sua senha, a única forma de restaurar acesso a um cofre é através de uma chave de recuperação.
vaultOptions.masterkey.recoveryKeyExplanation=Caso perca a sua senha, a única forma de restaurar acesso a um cofre é através de uma chave de recuperação.
vaultOptions.masterkey.showRecoveryKeyBtn=Mostrar chave de recuperação
vaultOptions.masterkey.recoverPasswordBtn=Redefinir senha
vaultOptions.masterkey.missingMasterkeyFile=Essas opções ficarão disponíveis se o arquivo masterkey estiver na pasta do cofre.
## Hub
vaultOptions.hub=Recuperação
vaultOptions.hub.convertInfo=Você pode usar a chave de recuperação para converter este cofre do Hub em um cofre protegido por senha de emergência.
vaultOptions.hub.convertBtn=Converter para Cofre protegido por senha
vaultOptions.hub.convertBtn=Converter para cofre protegido por senha
# Recovery Key
## Display Recovery Key
recoveryKey.display.title=Mostrar Chave de Recuperação
recoveryKey.create.message=Senha obrigatória
recoveryKey.create.description=Digite sua senha para mostrar a chave de recuperação de "%s":
recoveryKey.create.message=Requer senha
recoveryKey.create.description=Digite a senha para mostrar a chave de recuperação de "%s":
recoveryKey.recover.description=Digite a senha para "%s" para recuperar a configuração do cofre.
recoveryKey.display.description=A seguinte chave de recuperação pode ser usada para restaurar o acesso a "%s":
recoveryKey.display.StorageHints=Mantenha-a em um lugar bem seguro, por exemplo:\n • Guarde num gerenciador de senhas\n • Grave num flash drive USB\n • Imprima, ou escreva, num papel
recoveryKey.display.StorageHints=Mantenha-a em um lugar bem seguro. Por exemplo:\n • Guarde em um gerenciador de senhas;\n • Grave em um pen drive;\n • Imprima ou escreva em um papel.
## Reset Password
### Enter Recovery Key
recoveryKey.recover.title=Redefinir Senha
recoveryKey.recover.title=Redefinir senha
recoveryKey.recover.prompt=Digite a chave de recuperação para "%s":
recoveryKey.recover.correctKey=Esta é uma chave de recuperação válida
recoveryKey.recover.wrongKey=Esta chave de recuperação pertence a um outro cofre
recoveryKey.recover.invalidKey=Esta chave de recuperação não é válida
recoveryKey.printout.heading=Chave de Recuperação do Cryptomator\n"%s"\n
recoveryKey.recover.correctKey=Essa é uma chave de recuperação válida
recoveryKey.recover.wrongKey=Essa chave de recuperação pertence a um outro cofre
recoveryKey.recover.invalidKey=Essa chave de recuperação é inválida
recoveryKey.printout.heading=Chave de Recuperação do Cryptomator\n"%s"
### Reset Password
recoveryKey.recover.resetBtn=Redefinir
recoveryKey.recover.recoverBtn=Recuperar
@@ -545,41 +550,61 @@ recoveryKey.recover.recoverBtn=Recuperar
recoveryKey.recover.resetSuccess.message=Senha redefinida com sucesso
recoveryKey.recover.resetSuccess.description=Você pode desbloquear o seu cofre com a nova senha.
### Recovery Key Vault Config Reset Success
recoveryKey.recover.resetVaultConfigSuccess.message=Configuração do cofre recuperada
recoveryKey.recover.resetMasterkeyFileSuccess.message=Arquivo Masterkey recuperado
recoveryKey.recover.resetMasterkeyFileSuccess.description=Agora você pode desbloquear o seu cofre com a sua nova senha.
# Recover Vault Config File and/or Masterkey
##Add Existing Vault without recovery - Dialog
recover.existing.title=Cofre adicionado
recover.existing.message=O cofre foi adicionado com sucesso
recover.existing.description=O seu cofre "%s" foi adicionado à lista de cofre. Nenhum processo de recuperação foi necessário.
##Vault Already Exists - Dialog
recover.alreadyExists.title=O cofre já existe
recover.alreadyExists.message=Este cofre já foi adicionado
recover.alreadyExists.description=Seu cofre "%s" não foi adicionado novamente porque já está na sua lista de cofres.
##Invalid Selection - Dialog
recover.invalidSelection.title=Seleção inválida
recover.invalidSelection.message=Sua seleção não é um cofre
recover.invalidSelection.description=A pasta selecionada precisa ser um cofre válido do Cryptomator.
## Contact Hub Vault Owner - Dialog
contactHubVaultOwner.title=Cofre do Hub
contactHubVaultOwner.message=Este cofre foi criado com o Cryptomator Hub
contactHubVaultOwner.description=Entre em contato com o proprietário do cofre para restaurar o arquivo restante. Eles podem baixar o modelo de cofre no Cryptomator Hub.
##Dialog Title
recover.recoverVaultConfig.title=Recuperar a configuração do cofre
recover.recoverMasterkey.title=Recuperar Masterkey
## OnBoarding
recover.onBoarding.chooseMethod=Escolha o método de recuperação:
recover.onBoarding.useRecoveryKey=Usar chave de recuperação
recover.onBoarding.usePassword=Usar senha
recover.onBoarding.intro=Certifique-se de verificar o seguinte:
recover.onBoarding.pleaseConfirm=Antes de prosseguir, confirme que:
recover.onBoarding.otherwisePleaseConfirm=Caso contrário, confirme que:
recover.onBoarding.allMissing.intro=Se este cofre for gerenciado pelo Cryptomator Hub, o proprietário do cofre deve restaurá-lo para você.
recover.onBoarding.intro.ensure=Todos os arquivos estão sincronizados.
recover.onBoarding.affirmation=Li e estou ciente dos requisitos
###Vault Config Missing
recover.onBoarding.intro.recoveryKey=Você tem a chave de recuperação e sabe se as configurações avançadas foram usadas.
recover.onBoarding.intro.password=Você tem a senha do cofre e sabe se as configurações avançadas foram usadas.
###Masterkey Missing
recover.onBoarding.intro.masterkey.recoveryKey=Você tem a chave de recuperação do cofre.
## Expert Settings
recover.expertSettings.shorteningThreshold.title=Este valor deve corresponder ao usado antes da recuperação para garantir a compatibilidade com os dados criptografados anteriormente.
# Convert Vault
convertVault.title=Converter Cofre
convertVault.title=Converter cofre
convertVault.convert.convertBtn.before=Converter
convertVault.convert.convertBtn.processing=Convertendo...
convertVault.success.message=Conversão bem sucedida
convertVault.hubToPassword.success.description=Agora você pode desbloquear o cofre com a senha escolhida sem exigir acesso ao Hub.
convertVault.success.message=Conversão concluída
convertVault.hubToPassword.success.description=Agora você pode desbloquear o cofre com a senha escolhida sem solicitar acesso ao Hub.
# New Password
newPassword.promptText=Digite a nova senha
@@ -595,57 +620,57 @@ passwordStrength.messageLabel.4=Muito forte
# Quit
quit.title=Sair da aplicação
quit.message=Existem cofres desbloqueados
quit.description=Por favor, confirme que você deseja sair. O Cryptomador irá bloquear cuidadosamente todos os cofres abertos para evitar a perda de dados.
quit.lockAndQuitBtn=Bloquear e Sair
quit.message= cofres desbloqueados
quit.description=Confirme que deseja sair. O Cryptomador bloqueará cuidadosamente todos os cofres abertos para evitar a perda de dados.
quit.lockAndQuitBtn=Bloquear e sair
# Forced Quit
quit.forced.message=Alguns cofres não puderam ser trancados
quit.forced.description=O bloqueio dos cofres foi impedido por operações pendentes ou arquivos abertos. Você pode forçar o bloqueio dos cofres restantes, no entanto, essa interrupção de E/S pode resultar na perda de dados não salvos nos cofres — “seus dados”.
quit.forced.forceAndQuitBtn=Forçar e Sair
quit.forced.message=Alguns cofres não foram trancados
quit.forced.description=O bloqueio dos cofres foi impedido por operações pendentes ou arquivos abertos. Você pode forçar o bloqueio dos cofres restantes, mas essa interrupção de gravação de dados pode resultar na perda de arquivos não salvos nos cofres.
quit.forced.forceAndQuitBtn=Forçar e sair
# Update Reminder
updateReminder.title=Verificação de Atualizações
updateReminder.message=Verificar Atualizações?
updateReminder.description=Mantenha-se atualizado com novos recursos, correções de bugs e melhoramentos de segurança. Recomendamos verificar atualizações automaticamente.
updateReminder.notNow=Agora Não
updateReminder.title=Verificação de atualizações
updateReminder.message=Verificar atualizações?
updateReminder.description=Mantenha-se atualizado com novos recursos, correções de bugs e melhorias de segurança. Recomendamos verificar as atualizações automaticamente.
updateReminder.notNow=Agora não
updateReminder.yesOnce=Sim, uma vez
updateReminder.yesAutomatically=Sim, Automaticamente
updateReminder.yesAutomatically=Sim, automaticamente
#Dokany Support End
dokanySupportEnd.title=Aviso de descontinuação
dokanySupportEnd.message=Fim do suporte para o Dokany
dokanySupportEnd.description=O volume do tipo Dokany não é mais suportado pelo Cryptomator. Suas configurações serão ajustadas para usar o tipo de volume padrão agora. Você pode ver o tipo padrão nas preferências.
dokanySupportEnd.description=O volume do tipo Dokany não é mais compatível com o Cryptomator. Suas configurações serão ajustadas para usar o tipo de volume padrão. Veja o tipo padrão nas preferências.
dokanySupportEnd.preferencesBtn=Abrir preferências
#Retry If Readonly
retryIfReadonly.title=Acesso Restrito ao Cofre
retryIfReadonly.title=Acesso restrito ao cofre
retryIfReadonly.message=Sem acesso de gravação ao diretório do cofre
retryIfReadonly.description=O Cryptomator não pode escrever no diretório do cofre. Você pode alterar o cofre para somente leitura e tentar novamente. Esta opção pode ser desativada nas opções de cofre.
retryIfReadonly.retry=Alterar e Repetir
retryIfReadonly.description=O Cryptomator não pode gravar no diretório do cofre. Você pode alterar o cofre para somente leitura e tentar novamente. Esta opção pode ser desativada nas opções do cofre.
retryIfReadonly.retry=Alterar e repetir
# Share Vault
shareVault.title=Compartilhar Cofre
shareVault.title=Compartilhar cofre
shareVault.message=Gostaria de compartilhar o seu cofre com outras pessoas?
shareVault.description=Sempre tenha cuidado ao compartilhar seu cofre com outras pessoas. Em resumo, siga esses passos:
shareVault.instruction.1=1. Compartilhe o acesso da pasta do cofre criptografado via armazenamento na nuvem.
shareVault.description=Sempre tenha cuidado ao compartilhar seu cofre com terceiros. Em resumo, siga esses passos:
shareVault.instruction.1=1. Compartilhe o acesso da pasta do cofre criptografado via armazenamento em nuvem.
shareVault.instruction.2=2. Compartilhe a senha do cofre de forma segura.
shareVault.remarkBestPractices=Para mais informações, confira as sugestões de melhores práticas em nossos documentos.
shareVault.docsTooltip=Abra a documentação para aprender mais sobre compartilhamento de cofres.
shareVault.remarkBestPractices=Para mais informações, confira as sugestões de boas práticas em nossas documentações.
shareVault.docsTooltip=Abra a documentação para aprender mais sobre o compartilhamento de cofres.
shareVault.hubAd.description=A maneira segura de trabalhar em equipes
shareVault.hubAd.keyManagement=• Gerenciamento de chaves de Zero-Conhecimento
shareVault.hubAd.keyManagement=• Gerenciamento de chaves de conhecimento zero
shareVault.hubAd.authentication=• Autenticação forte
shareVault.hubAd.encryption=• Criptografia de ponta-a-ponta
shareVault.visitHub=Visite o Cryptomator Hub
shareVault.hub.message=Como compartilhar um cofre do Hub
shareVault.hub.description=Para compartilhar o conteúdo do cofre com outro membro da equipe, você deve realizar duas etapas:
shareVault.hub.instruction.1=1. Compartilhe o acesso da pasta do cofre criptografado via armazenamento na nuvem.
shareVault.hub.instruction.1=1. Compartilhe o acesso da pasta do cofre criptografado via armazenamento em nuvem.
shareVault.hub.instruction.2=2. Conceda acesso ao membro da equipe no Cryptomator Hub.
shareVault.hub.openHub=Abrir o Cryptomator Hub
# Decrypt File Names
decryptNames.title=Descriptografar Nomes de Arquivos
decryptNames.title=Descriptografar nomes de arquivos
decryptNames.filePicker.title=Selecione o arquivo criptografado
decryptNames.filePicker.extensionDescription=Arquivo criptografado do Cryptomator
decryptNames.copyTable.tooltip=Copiar tabela
@@ -653,10 +678,10 @@ decryptNames.clearTable.tooltip=Limpar tabela
decryptNames.column.encrypted=Criptografado
decryptNames.column.decrypted=Descriptografado
decryptNames.copyHint=Copiar conteúdo da célula com %s
decryptNames.dropZone.message=Arraste e solte arquivos ou clique para selecionar
decryptNames.dropZone.error.vaultInternalFiles=Selecionados arquivos internos do cofre sem nome descriptografável
decryptNames.dropZone.error.foreignFiles=Arquivos não pertencem ao cofre "%s"
decryptNames.dropZone.error.noDirIdBackup=Diretório dos arquivos selecionados não contém o arquivo dirId.c9r
decryptNames.dropZone.message=Arraste e solte os arquivos ou clique para selecionar
decryptNames.dropZone.error.vaultInternalFiles=Arquivos internos do cofre sem nome descriptografável selecionados
decryptNames.dropZone.error.foreignFiles=Os arquivos não pertencem ao cofre "%s"
decryptNames.dropZone.error.noDirIdBackup=O diretório dos arquivos selecionados não contém o arquivo dirId.c9r
decryptNames.dropZone.error.generic=Falha ao descriptografar os nomes de arquivos
@@ -667,35 +692,28 @@ eventView.clearListButton.tooltip=Limpar lista
eventView.filterVaults=Filtrar por cofre
eventView.cell.actionsButton.tooltip=Ações de evento
## event list entries
eventView.entry.vaultLocked.description=Desbloquear "%s" para detalhes
eventView.entry.vaultLocked.description=Desbloquear "%s" para mais detalhes
eventView.entry.conflictResolved.message=Conflito resolvido
eventView.entry.conflictResolved.showDecrypted=Mostrar arquivo descriptografado
eventView.entry.conflictResolved.copyDecrypted=Copiar caminho descriptografado
eventView.entry.conflict.message=Resolução de conflitos falhou
eventView.entry.conflict.message=Falha na resolução de conflitos
eventView.entry.conflict.showDecrypted=Mostrar arquivo original descriptografado
eventView.entry.conflict.copyDecrypted=Copiar caminho original descriptografado
eventView.entry.conflict.showEncrypted=Mostrar arquivo criptografado conflitante
eventView.entry.conflict.copyEncrypted=Copiar caminho criptografado conflitante
eventView.entry.decryptionFailed.message=Decriptação falhou
eventView.entry.decryptionFailed.message=Falha ao descriptografar
eventView.entry.decryptionFailed.showEncrypted=Mostrar arquivo criptografado
eventView.entry.decryptionFailed.copyEncrypted=Copiar caminho encriptado
eventView.entry.brokenDirFile.message=Link do diretório quebrado
eventView.entry.brokenDirFile.showEncrypted=Mostrar link quebrado e criptografado
eventView.entry.brokenDirFile.copyEncrypted=Copiar caminho do link quebrado
eventView.entry.brokenFileNode.message=Nó de sistema de arquivos danificado
eventView.entry.brokenFileNode.showEncrypted=Mostrar nó criptografado com falha
eventView.entry.brokenFileNode.copyEncrypted=Copiar caminho do nó quebrado criptografado
eventView.entry.brokenFileNode.copyDecrypted=Copiar caminho descriptografado
eventView.entry.inUse.message=Arquivo bloqueado
eventView.entry.inUse.message=Arquivo em uso
eventView.entry.inUse.showDecrypted=Mostrar arquivo descriptografado
eventView.entry.inUse.copyDecrypted=Copiar caminho descriptografado
eventView.entry.inUse.showEncrypted=Mostrar arquivo criptografado
eventView.entry.inUse.copyEncrypted=Copiar caminho encriptado
eventView.entry.inUse.ignoreLock=Ignorar bloqueio
eventView.entry.inUse.copyUserAndDevice=Copiar usuário de bloqueio e nome do dispositivo
eventView.entry.inUse.ignoreLock=Ignorar status de uso
# Notifications
## FileIsInUse Notification
notification.inUse.message=O arquivo está bloqueado por outro dispositivo
notification.inUse.description=O arquivo é aberto por %s no dispositivo %s. Solicite ao usuário para fechá-lo e o sincronizar novamente. Caso contrário, você pode ignorar o bloqueio e abri-lo mesmo assim.
notification.inUse.action=Ignorar bloqueio
notification.inUse.message=O arquivo está sendo usado em outro dispositivo
notification.inUse.description=O arquivo é aberto por %s em %s. Solicite a eles para fechá-lo e deixar a sincronização finalizar. Você pode ignorar o status para abri-lo agora, mas isso pode causar conflitos ou substituir novas alterações.
notification.inUse.action=Ignorar status de uso

View File

@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Действия события
eventView.entry.vaultLocked.description=Разблокируйте "%s" для деталей
eventView.entry.conflictResolved.message=Решённый конфликт
eventView.entry.conflictResolved.showDecrypted=Показать расшифрованный файл
eventView.entry.conflictResolved.copyDecrypted=Скопировать расшифрованный путь
eventView.entry.conflict.message=Не удалось решить конфликты
eventView.entry.conflict.showDecrypted=Показать расшифрованный исходный файл
eventView.entry.conflict.copyDecrypted=Скопировать расшифрованный исходный путь
eventView.entry.conflict.showEncrypted=Показать конфликтующий зашифрованный файл
eventView.entry.conflict.copyEncrypted=Скопировать конфликтующий зашифрованный путь
eventView.entry.decryptionFailed.message=Ошибка дешифрования
eventView.entry.decryptionFailed.showEncrypted=Показать зашифрованный файл
eventView.entry.decryptionFailed.copyEncrypted=Скопировать зашифрованный путь
eventView.entry.brokenDirFile.message=Повреждённая ссылка на папку
eventView.entry.brokenDirFile.showEncrypted=Показать повреждённую зашифрованную ссылку
eventView.entry.brokenDirFile.copyEncrypted=Скопировать путь повреждённой ссылки
eventView.entry.brokenFileNode.message=Повреждённый узел файловой системы
eventView.entry.brokenFileNode.showEncrypted=Показать повреждённый зашифрованный узел
eventView.entry.brokenFileNode.copyEncrypted=Скопировать путь повреждённого зашифрованного узла
eventView.entry.brokenFileNode.copyDecrypted=Скопировать расшифрованный путь
eventView.entry.inUse.message=Заблокированный файл
eventView.entry.inUse.message=Файл используется
eventView.entry.inUse.showDecrypted=Показать расшифрованный файл
eventView.entry.inUse.copyDecrypted=Скопировать расшифрованный путь
eventView.entry.inUse.showEncrypted=Показать зашифрованный файл
eventView.entry.inUse.copyEncrypted=Скопировать зашифрованный путь
eventView.entry.inUse.copyUserAndDevice=Скопировать блокирующее имя пользователя и устройства
eventView.entry.inUse.ignoreLock=Игнорировать блокировку
eventView.entry.inUse.ignoreLock=Игнорировать статус использования
# Notifications
## FileIsInUse Notification
notification.inUse.message=Файл заблокирован другим устройством
notification.inUse.description=Файл открыт %s на устройстве %s. Попросите пользователя закрыть файл и синхронизировать снова. В противном случае вы можете проигнорировать блокировку и всё равно открыть его.
notification.inUse.action=Игнорировать блокировку
notification.inUse.message=Файл используется на другом устройстве
notification.inUse.description=Файл открыт %s на %s. Попросите закрыть файл и завершить синхронизацию. Вы можете проигнорировать статус и открыть его, но есть риск конфликтов или перезаписи новых изменений.
notification.inUse.action=Игнорировать статус использования

View File

@@ -647,33 +647,22 @@ eventView.cell.actionsButton.tooltip=Akcie udalostí
eventView.entry.vaultLocked.description=Podrobnosti získate odomknutím „%s“
eventView.entry.conflictResolved.message=Vyriešený konflikt
eventView.entry.conflictResolved.showDecrypted=Zobraziť dešifrovaný súbor
eventView.entry.conflictResolved.copyDecrypted=Skopírujte dešifrovanú cestu
eventView.entry.conflict.message=Riešenie konfliktu zlyhalo
eventView.entry.conflict.showDecrypted=Zobraziť dešifrovaný pôvodný súbor
eventView.entry.conflict.copyDecrypted=Kopírovať dešifrovanú, pôvodnú cestu
eventView.entry.conflict.showEncrypted=Zobraziť konfliktný zašifrovaný súbor
eventView.entry.conflict.copyEncrypted=Kopírovať konfliktnú zašifrovanú cestu
eventView.entry.decryptionFailed.message=Dešifrovanie zlyhalo
eventView.entry.decryptionFailed.showEncrypted=Zobraziť zašifrovaný súbor
eventView.entry.decryptionFailed.copyEncrypted=Skopírujte zašifrovanú cestu
eventView.entry.brokenDirFile.message=Nefunkčný odkaz na adresár
eventView.entry.brokenDirFile.showEncrypted=Zobraziť nefunkčný, šifrovaný odkaz
eventView.entry.brokenDirFile.copyEncrypted=Kopírovať cestu nefunkčného odkazu
eventView.entry.brokenFileNode.message=Poškodený uzol súborového systému
eventView.entry.brokenFileNode.showEncrypted=Zobraziť nefunkčný, zašifrovaný uzol
eventView.entry.brokenFileNode.copyEncrypted=Kopírovať cestu nefunkčného, šifrovaného uzla
eventView.entry.brokenFileNode.copyDecrypted=Skopírujte dešifrovanú cestu
eventView.entry.inUse.message=Zamknutý súbor
eventView.entry.inUse.message=Súbor sa používa
eventView.entry.inUse.showDecrypted=Zobraziť dešifrovaný súbor
eventView.entry.inUse.copyDecrypted=Skopírujte dešifrovanú cestu
eventView.entry.inUse.showEncrypted=Zobraziť zašifrovaný súbor
eventView.entry.inUse.copyEncrypted=Skopírujte zašifrovanú cestu
eventView.entry.inUse.copyUserAndDevice=Kopírovať zamykaného užívateľa a meno zariadenia
eventView.entry.inUse.ignoreLock=Ignorovať zamknutie
# Notifications
## FileIsInUse Notification
notification.inUse.message=Súbor je blokovaný iným zariadením
notification.inUse.description=Súbor je otvorený %s na zariadení %s. Požiadajte užívateľa o zatvorenie súboru a synchronizujte znovu. Alebo môžte ignorovať uzamknutie a napriek tomu ho otvoriť.
notification.inUse.action=Ignorovať zamknutie
notification.inUse.message=Súbor je používaný iným zariadením

View File

@@ -678,31 +678,20 @@ eventView.filterVaults=Filtrera efter valv
eventView.entry.vaultLocked.description=Lås upp "%s" för detaljer
eventView.entry.conflictResolved.message=Löst konflikt
eventView.entry.conflictResolved.showDecrypted=Visa dekrypterad fil
eventView.entry.conflictResolved.copyDecrypted=Kopiera dekrypterad sökväg
eventView.entry.conflict.message=Konfliktlösning misslyckades
eventView.entry.conflict.showDecrypted=Visa dekrypterad, originalfil
eventView.entry.conflict.copyDecrypted=Kopiera dekrypterad, ursprunglig sökväg
eventView.entry.conflict.showEncrypted=Visa krypterad fil som inte kunde synkroniseras
eventView.entry.conflict.copyEncrypted=Kopiera krypterad sökväg som inte kunde synkroniseras
eventView.entry.decryptionFailed.message=Dekryptering misslyckades
eventView.entry.decryptionFailed.showEncrypted=Visa krypterad fil
eventView.entry.decryptionFailed.copyEncrypted=Kopiera krypterad sökväg
eventView.entry.brokenDirFile.message=Trasig kataloglänk
eventView.entry.brokenDirFile.showEncrypted=Visa trasig, krypterad länk
eventView.entry.brokenDirFile.copyEncrypted=Kopiera sökväg för trasig länk
eventView.entry.brokenFileNode.message=Trasig filsystemsnod
eventView.entry.brokenFileNode.showEncrypted=Visa trasig krypterad nod
eventView.entry.brokenFileNode.copyEncrypted=Kopiera sökväg för trasig, krypterad nod
eventView.entry.brokenFileNode.copyDecrypted=Kopiera dekrypterad sökväg
eventView.entry.inUse.message=Låst fil
eventView.entry.inUse.showDecrypted=Visa dekrypterad fil
eventView.entry.inUse.copyDecrypted=Kopiera dekrypterad sökväg
eventView.entry.inUse.showEncrypted=Visa krypterad fil
eventView.entry.inUse.copyEncrypted=Kopiera krypterad sökväg
eventView.entry.inUse.copyUserAndDevice=Kopiera blockerande användare och enhet
eventView.entry.inUse.ignoreLock=Ignorera lås
# Notifications
## FileIsInUse Notification
notification.inUse.action=Ignorera lås
## FileIsInUse Notification

View File

@@ -18,6 +18,10 @@ generic.button.print=Yazdır
generic.button.remove=Kaldır
## Vault state
vault.state.locked=Kilitli
vault.state.unlocked=Kilidi açık
vault.state.missing=Kayıt
vault.state.processing=İşleniyor
vault.state.error=Hata
# Error
@@ -180,6 +184,7 @@ hub.registerFailed.message=Cihaz kaydı başarısız oldu
hub.registerFailed.description.generic=Kayıt işleminde bir hata oluştu. Daha fazla bilgi için uygulama günlüğüne bakın.
hub.registerFailed.description.deviceAlreadyExists=Bu cihaz zaten farklı bir kullanıcı için kayıtlı. Kullanıcı hesabını değiştirmeyi veya farklı bir cihaz kullanmayı deneyin.
### Archived
hub.archived.message=Kasa arşivlendi
### Unauthorized
hub.unauthorized.message=Erişim engellendi
hub.unauthorized.description=Bu kasayı açma yetkiniz yok. Erişim talebinde bulunmak için kasanın sahibiyle iletişime geçin.
@@ -332,6 +337,7 @@ preferences.updates.lastUpdateCheck.never=asla
preferences.updates.lastUpdateCheck.recently=az önce
preferences.updates.lastUpdateCheck.daysAgo=%s gün önce
preferences.updates.lastUpdateCheck.hoursAgo=%s saat önce
preferences.updates.prohibitedDueToUnlockedVaults.1=Lütfen
preferences.updates.checkFailed=Güncelleme kontrolü başarısız oldu. Lütfen internet bağlantınızı kontrol edin veya daha sonra tekrar deneyin.
preferences.updates.upToDate=Cryptomator güncel.
@@ -608,12 +614,24 @@ shareVault.hub.instruction.2=2. Cryptomator Hub'da ekip üyesine erişim izni ve
shareVault.hub.openHub=Cryptomator Hub'ı
# Decrypt File Names
decryptNames.copyTable.tooltip=Tabloyu kopyala
decryptNames.clearTable.tooltip=Tabloyu temizle
decryptNames.column.encrypted=Şifrelendi
decryptNames.column.decrypted=Şifresi çözüldü
# Event View
eventView.title=Etkinlikler
eventView.filter.allVaults=Tümü
eventView.clearListButton.tooltip=Listeyi temizle
eventView.filterVaults=Kasayı süz
## event list entries
eventView.entry.inUse.message=Kullanılan dosya
eventView.entry.inUse.ignoreLock=Kullanım durumunu yok say
# Notifications
## FileIsInUse Notification
## FileIsInUse Notification
notification.inUse.message=Dosya başka bir cihazda kullanılıyor
notification.inUse.description=Dosya %s tarafından %s tarihinde açıldı. Onlardan dosyayı kapatmalarını ve senkronizasyonun tamamlanmasını beklemelerini isteyin. Dosyayı şimdi açmak için durumu görmezden gelebilirsiniz, ancak bu çakışmalara veya yeni değişikliklerin üzerine yazılmasına neden olabilir.
notification.inUse.action=Kullanım Durumunu Yok Say

View File

@@ -696,33 +696,25 @@ eventView.cell.actionsButton.tooltip=Дії події
eventView.entry.vaultLocked.description=Розблокуйте «%s» для отримання деталей
eventView.entry.conflictResolved.message=Конфлікт вирішено
eventView.entry.conflictResolved.showDecrypted=Показати розшифрований файл
eventView.entry.conflictResolved.copyDecrypted=Копіювати розшифрований шлях
eventView.entry.conflict.message=Не вдалося вирішити конфлікт
eventView.entry.conflict.showDecrypted=Показати розшифрований оригінальний файл
eventView.entry.conflict.copyDecrypted=Скопіювати розшифрований оригінальний шлях
eventView.entry.conflict.showEncrypted=Показати конфліктуючий зашифрований файл
eventView.entry.conflict.copyEncrypted=Скопіювати конфліктуючий зашифрований шлях
eventView.entry.decryptionFailed.message=Не вдалося розшифрувати
eventView.entry.decryptionFailed.showEncrypted=Показати зашифрований файл
eventView.entry.decryptionFailed.copyEncrypted=Копіювати зашифрований шлях
eventView.entry.brokenDirFile.message=Пошкоджене посилання на каталог
eventView.entry.brokenDirFile.showEncrypted=Показати пошкоджене зашифроване посилання
eventView.entry.brokenDirFile.copyEncrypted=Копіювати шлях пошкодженого посилання
eventView.entry.brokenFileNode.message=Пошкоджений вузол файлової системи
eventView.entry.brokenFileNode.showEncrypted=Показати пошкоджений зашифрований вузол
eventView.entry.brokenFileNode.copyEncrypted=Скопіювати шлях до пошкодженого зашифрованого вузла
eventView.entry.brokenFileNode.copyDecrypted=Копіювати розшифрований шлях
eventView.entry.inUse.message=Заблокований файл
eventView.entry.inUse.message=Файл використовується
eventView.entry.inUse.showDecrypted=Показати розшифрований файл
eventView.entry.inUse.copyDecrypted=Копіювати розшифрований шлях
eventView.entry.inUse.showEncrypted=Показати зашифрований файл
eventView.entry.inUse.copyEncrypted=Копіювати зашифрований шлях
eventView.entry.inUse.copyUserAndDevice=Копіювати ім'я користувача та пристрою, що блокують
eventView.entry.inUse.ignoreLock=Ігнорування блокування
eventView.entry.inUse.ignoreLock=Ігнорувати статус використання
# Notifications
## FileIsInUse Notification
notification.inUse.message=Файл заблоковано іншим пристроєм
notification.inUse.description=Файл відкрито в «%s» на пристрої «%s». Попросіть користувача закрити файл і повторити синхронізацію. В іншому випадку можна проігнорувати блокування і відкрити файл.
notification.inUse.action=Ігнорування блокування
notification.inUse.message=Файл використовується на іншому пристрої
notification.inUse.description=Файл відкритий користувачем %s на пристрої %s. Попросіть його закрити файл, щоб завершити синхронізацію. Ви можете ігнорувати цей статус і відкрити файл зараз, але це може призвести до конфліктів або перезапису новіших змін.
notification.inUse.action=Ігнорувати статус використання

View File

@@ -629,26 +629,18 @@ eventView.clearListButton.tooltip=Xóa danh sách
eventView.entry.vaultLocked.description=Mở khóa "%s" để xem chi tiết
eventView.entry.conflictResolved.message=Đã xử lý xung đột
eventView.entry.conflictResolved.showDecrypted=Xem tệp đã giải mã
eventView.entry.conflictResolved.copyDecrypted=Sao chép đường dẫn giải mã
eventView.entry.conflict.message=Giải quyết xung đột thất bại
eventView.entry.conflict.showDecrypted=Xem tệp gốc đã giải mã
eventView.entry.conflict.copyDecrypted=Sao chép đường dẫn gốc đã giải mã
eventView.entry.conflict.showEncrypted=Hiển thị tệp bị xung đột, đã mã hóa
eventView.entry.conflict.copyEncrypted=Sao chép đường dẫn mã hóa bị xung đột
eventView.entry.decryptionFailed.message=Giải mã thất bại
eventView.entry.decryptionFailed.showEncrypted=Hiển thị tệp đã mã hóa
eventView.entry.decryptionFailed.copyEncrypted=Sao chép đường dẫn tệp đã mã hóa
eventView.entry.brokenDirFile.message=Liên kết thư mục hỏng
eventView.entry.brokenDirFile.showEncrypted=Hiển thị liên kết mã hóa bị hỏng
eventView.entry.brokenDirFile.copyEncrypted=Sao chép đường dẫn liên kết bị hỏng
eventView.entry.brokenFileNode.message=Nút tập tin bị hỏng
eventView.entry.brokenFileNode.showEncrypted=Hiển thị nút mã hóa bị hỏng
eventView.entry.brokenFileNode.copyEncrypted=Sao chép đường dẫn nút mã hóa bị hỏng
eventView.entry.brokenFileNode.copyDecrypted=Sao chép đường dẫn giải mã
eventView.entry.inUse.showDecrypted=Xem tệp đã giải mã
eventView.entry.inUse.copyDecrypted=Sao chép đường dẫn giải mã
eventView.entry.inUse.showEncrypted=Hiển thị tệp đã mã hóa
eventView.entry.inUse.copyEncrypted=Sao chép đường dẫn tệp đã mã hóa
# Notifications

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