mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-23 08:24:52 +00:00
Compare commits
248
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
426b33ae1a | ||
|
|
8e52161fa4 | ||
|
|
f1540e13eb | ||
|
|
2c2165cb22 | ||
|
|
03cb5f6be9 | ||
|
|
8a98048835 | ||
|
|
2fe7fe886c | ||
|
|
459112f5b8 | ||
|
|
ea86124c7d | ||
|
|
cd9e315447 | ||
|
|
1266926ebb | ||
|
|
1e6bc1f043 | ||
|
|
1230e787a9 | ||
|
|
da8493f25f | ||
|
|
4a0a4309c2 | ||
|
|
c52ae0120d | ||
|
|
e6cef947c1 | ||
|
|
08009ca1a0 | ||
|
|
d27a52752b | ||
|
|
c745fca2ea | ||
|
|
d1fcf528b0 | ||
|
|
b6133e481c | ||
|
|
d67aa9c10a | ||
|
|
19a9595f2e | ||
|
|
f21ae0e11c | ||
|
|
54f805a0c9 | ||
|
|
aa239418cf | ||
|
|
8f0c6eb994 | ||
|
|
4751c81d06 | ||
|
|
0e1132133c | ||
|
|
c7d77091f5 | ||
|
|
3a4039d663 | ||
|
|
f94d204604 | ||
|
|
d76dbe0ddb | ||
|
|
235a23ce77 | ||
|
|
357b30684b | ||
|
|
517e12a586 | ||
|
|
96aa8fe180 | ||
|
|
7bffc317ff | ||
|
|
7dde389671 | ||
|
|
a40f17e8f1 | ||
|
|
699e22d31a | ||
|
|
5d5b9137cc | ||
|
|
02767b0caf | ||
|
|
ddd8c572e7 | ||
|
|
aa8ccf53df | ||
|
|
c199561a9e | ||
|
|
e341983ffe | ||
|
|
f10b28ecb1 | ||
|
|
95eed96a37 | ||
|
|
9de0981594 | ||
|
|
4d6ce70afe | ||
|
|
e75deb282c | ||
|
|
87fe209b54 | ||
|
|
21cffb13c7 | ||
|
|
ee92cc4e9f | ||
|
|
9eca05aeb7 | ||
|
|
57c84627f9 | ||
|
|
a653fc3627 | ||
|
|
db31fcad98 | ||
|
|
b82c73f789 | ||
|
|
70d3507e5d | ||
|
|
518b45d149 | ||
|
|
65f2eea6c9 | ||
|
|
12a4a8218a | ||
|
|
76dd4f079c | ||
|
|
309a8fc705 | ||
|
|
0b77e6ce04 | ||
|
|
d22e912ca4 | ||
|
|
690726efc3 | ||
|
|
d9c0c4980b | ||
|
|
4e5eef5a16 | ||
|
|
81b2081033 | ||
|
|
b35fb011e0 | ||
|
|
fa1e9c7ca7 | ||
|
|
75bb64b333 | ||
|
|
1f40045b41 | ||
|
|
1951e5e82c | ||
|
|
0be39c5b7d | ||
|
|
6886188ff4 | ||
|
|
3a2de81369 | ||
|
|
4de3ea2892 | ||
|
|
0beb2e3334 | ||
|
|
537d79f3c2 | ||
|
|
10d5f4f530 | ||
|
|
ab0e7e1ea9 | ||
|
|
967a3aa10b | ||
|
|
821649c821 | ||
|
|
7cf75e9dba | ||
|
|
dfe126f827 | ||
|
|
80ce98991c | ||
|
|
579d42ec74 | ||
|
|
047108683f | ||
|
|
830c9196e7 | ||
|
|
7208f23da1 | ||
|
|
b0a4a0bcfa | ||
|
|
f7b13dd121 | ||
|
|
a5ccfa01d2 | ||
|
|
62bf4c5b45 | ||
|
|
7168d7e31d | ||
|
|
3c633f8a34 | ||
|
|
b606a4ee8e | ||
|
|
1af094692f | ||
|
|
c86d8ac6b8 | ||
|
|
b06d65ab43 | ||
|
|
390bdc33e0 | ||
|
|
2284d1fcee | ||
|
|
62a439e10e | ||
|
|
ddad663489 | ||
|
|
f08e7d9b92 | ||
|
|
6c2865a09d | ||
|
|
5a0d15c937 | ||
|
|
53757dab93 | ||
|
|
e1d4d3e85b | ||
|
|
859fe238b8 | ||
|
|
23df40796b | ||
|
|
b98b1326b2 | ||
|
|
2f8831b561 | ||
|
|
4948b9b586 | ||
|
|
838018e72e | ||
|
|
d5433e7a3f | ||
|
|
194e19bf5e | ||
|
|
1358e7098d | ||
|
|
4be327608a | ||
|
|
929ef37ded | ||
|
|
6fce1807f0 | ||
|
|
c1d72650cc | ||
|
|
dab368e44b | ||
|
|
7548174e4f | ||
|
|
b513060744 | ||
|
|
311a9ef70b | ||
|
|
7331144f9d | ||
|
|
d576b36f7b | ||
|
|
7a6340aac6 | ||
|
|
6b82abcd80 | ||
|
|
97bde05422 | ||
|
|
3bc50eee47 | ||
|
|
b95a220de7 | ||
|
|
2896e18429 | ||
|
|
29db91f976 | ||
|
|
1e3dfe3de1 | ||
|
|
dcb1f5e80f | ||
|
|
5c75eeab27 | ||
|
|
8e4bff8c19 | ||
|
|
e066f155b2 | ||
|
|
c3c7a23ccd | ||
|
|
c816411644 | ||
|
|
f752490624 | ||
|
|
d196b66b4e | ||
|
|
d803a1d71e | ||
|
|
c75430cde2 | ||
|
|
f44c3531c7 | ||
|
|
596829bc77 | ||
|
|
096aeb0c54 | ||
|
|
1a12684557 | ||
|
|
998664acc3 | ||
|
|
c52b3fc4ad | ||
|
|
1fde3650ca | ||
|
|
57614f59e4 | ||
|
|
a95883f98b | ||
|
|
6cf18c33a1 | ||
|
|
232e6fbf71 | ||
|
|
d15731d5a4 | ||
|
|
45d7421e4f | ||
|
|
4d0aabfe6e | ||
|
|
db0e266fde | ||
|
|
6281561ab4 | ||
|
|
b981f0dc19 | ||
|
|
bc41429982 | ||
|
|
a31621cfde | ||
|
|
aa68224d63 | ||
|
|
b8413a21a3 | ||
|
|
af59196f19 | ||
|
|
3a58f560fc | ||
|
|
a2cca0b624 | ||
|
|
0c4ba94f10 | ||
|
|
0f8b6a8f51 | ||
|
|
8ca6946fe6 | ||
|
|
e583b70931 | ||
|
|
d9134b49ad | ||
|
|
6f6d0d8771 | ||
|
|
9d3d67b605 | ||
|
|
8da27e36d6 | ||
|
|
1123fa28a3 | ||
|
|
be3ecc536a | ||
|
|
94c8dd911b | ||
|
|
7e888b0e3b | ||
|
|
a066afe37d | ||
|
|
21c8cbf709 | ||
|
|
ea236d45a3 | ||
|
|
fab5ac3726 | ||
|
|
57666a620c | ||
|
|
e49fe23b35 | ||
|
|
254a7696c8 | ||
|
|
62bd43ef9b | ||
|
|
b73510ec0f | ||
|
|
3fba62ef72 | ||
|
|
7daca70a15 | ||
|
|
90228a1d5c | ||
|
|
92d11d8bd5 | ||
|
|
300a811510 | ||
|
|
4090413290 | ||
|
|
29bfbdc524 | ||
|
|
688090095e | ||
|
|
98db9649c3 | ||
|
|
7a4c8f113e | ||
|
|
50f6a87788 | ||
|
|
d601088948 | ||
|
|
4d62f2303d | ||
|
|
792734b486 | ||
|
|
f884861373 | ||
|
|
da3c5e901f | ||
|
|
f7ccc326de | ||
|
|
246eb19c3c | ||
|
|
c073b26d8f | ||
|
|
227a3258ec | ||
|
|
3279df68c7 | ||
|
|
bf6598302b | ||
|
|
dccb4ef072 | ||
|
|
cbae2413ae | ||
|
|
113ab66a49 | ||
|
|
34eb336e5f | ||
|
|
2cab0c59a1 | ||
|
|
3822136e69 | ||
|
|
1fa4d42d27 | ||
|
|
d5bc0914f4 | ||
|
|
ee5165320d | ||
|
|
84a966993f | ||
|
|
3fb0e9328f | ||
|
|
eb369ba5bd | ||
|
|
738bd56270 | ||
|
|
5e29f08d2b | ||
|
|
b3a5265b98 | ||
|
|
d7d439947d | ||
|
|
2eda795219 | ||
|
|
18ce7d333d | ||
|
|
f32d0c463f | ||
|
|
7275ec9a61 | ||
|
|
0fa34dba33 | ||
|
|
49f070977a | ||
|
|
35f39a9eaf | ||
|
|
b9d21b1b7a | ||
|
|
2e698317e5 | ||
|
|
9c88fbab5e | ||
|
|
1a51642eb1 | ||
|
|
fdaf5d1c36 | ||
|
|
00e824a1ba | ||
|
|
9c5ab7e311 |
+5
-32
@@ -3,10 +3,7 @@ updates:
|
|||||||
- package-ecosystem: "maven"
|
- package-ecosystem: "maven"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "monthly"
|
||||||
day: "monday"
|
|
||||||
time: "06:00"
|
|
||||||
timezone: "Etc/UTC"
|
|
||||||
ignore:
|
ignore:
|
||||||
- dependency-name: "org.cryptomator:integrations-api"
|
- dependency-name: "org.cryptomator:integrations-api"
|
||||||
versions: ["2.0.0-alpha1"]
|
versions: ["2.0.0-alpha1"]
|
||||||
@@ -14,37 +11,13 @@ updates:
|
|||||||
versions: ["2.0.1.MR"]
|
versions: ["2.0.1.MR"]
|
||||||
- dependency-name: "org.openjfx:*"
|
- dependency-name: "org.openjfx:*"
|
||||||
update-types: ["version-update:semver-major"]
|
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:
|
groups:
|
||||||
java-test-dependencies:
|
maven-dependencies:
|
||||||
patterns:
|
|
||||||
- "org.junit.jupiter:*"
|
|
||||||
- "org.mockito:*"
|
|
||||||
- "org.hamcrest:*"
|
|
||||||
- "com.google.jimfs:jimfs"
|
|
||||||
maven-build-plugins:
|
|
||||||
patterns:
|
|
||||||
- "org.apache.maven.plugins:*"
|
|
||||||
- "org.jacoco:jacoco-maven-plugin"
|
|
||||||
- "org.owasp:dependency-check-maven"
|
|
||||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
|
||||||
- "org.codehaus.mojo:license-maven-plugin"
|
|
||||||
javafx:
|
|
||||||
patterns:
|
|
||||||
- "org.openjfx:*"
|
|
||||||
java-production-dependencies:
|
|
||||||
patterns:
|
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"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/" # even for `.github/workflows`
|
directory: "/" # even for `.github/workflows`
|
||||||
|
|||||||
@@ -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: -->
|
||||||
|
|
||||||
@@ -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.
|
||||||
@@ -1,13 +1,44 @@
|
|||||||
name: Build AppImage
|
name: Build AppImage
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
schedule:
|
||||||
types: [published]
|
- 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:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
semVerNum:
|
||||||
description: 'Version'
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
required: false
|
required: false
|
||||||
|
revisionNum:
|
||||||
|
description: 'The revision number'
|
||||||
|
required: false
|
||||||
|
semVerSuffix:
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: false
|
||||||
|
default: '-SNAPSHOT'
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
@@ -20,29 +51,29 @@ on:
|
|||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
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:
|
jobs:
|
||||||
get-version:
|
|
||||||
uses: ./.github/workflows/get-version.yml
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version }} #okay if not defined
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build AppImage
|
name: Build AppImage
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [get-version]
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
appimage-suffix: x86_64
|
arch: x86_64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
|
||||||
openjfx-sha: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
|
openjfx-sha: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
|
||||||
|
appimagetool-sha: 'ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0'
|
||||||
- os: ubuntu-24.04-arm
|
- os: ubuntu-24.04-arm
|
||||||
appimage-suffix: aarch64
|
arch: aarch64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
|
||||||
openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
|
openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
|
||||||
|
appimagetool-sha: 'f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
@@ -55,7 +86,7 @@ jobs:
|
|||||||
- name: Download OpenJFX jmods
|
- name: Download OpenJFX jmods
|
||||||
id: download-jmods
|
id: download-jmods
|
||||||
run: |
|
run: |
|
||||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
curl --silent --fail-with-body --proto "=https" -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||||
echo "${{ matrix.openjfx-sha }} openjfx-jmods.zip" | shasum -a256 --check
|
echo "${{ matrix.openjfx-sha }} openjfx-jmods.zip" | shasum -a256 --check
|
||||||
mkdir -p openjfx-jmods
|
mkdir -p openjfx-jmods
|
||||||
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||||
@@ -73,7 +104,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Set version
|
- 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
|
- name: Run maven
|
||||||
run: mvn -B clean package -Plinux -DskipTests
|
run: mvn -B clean package -Plinux -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
@@ -94,13 +125,15 @@ jobs:
|
|||||||
${JAVA_HOME}/bin/jlink
|
${JAVA_HOME}/bin/jlink
|
||||||
--verbose
|
--verbose
|
||||||
--output runtime
|
--output runtime
|
||||||
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
|
--module-path "${JMOD_PATHS}"
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
|
||||||
--strip-native-commands
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
--strip-debug
|
--strip-debug
|
||||||
--compress zip-0
|
--compress zip-0
|
||||||
|
env:
|
||||||
|
JMOD_PATHS: ${{ steps.jep-493-check.outputs.jmod_paths }}
|
||||||
- name: Run jpackage
|
- name: Run jpackage
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jpackage
|
${JAVA_HOME}/bin/jpackage
|
||||||
@@ -113,13 +146,13 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
--app-version "${VERSION_NUM}.${REVISION_NUM}"
|
||||||
--java-options "--enable-preview"
|
--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 "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
--java-options "-Xmx256m"
|
--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 "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\""
|
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\""
|
||||||
@@ -130,8 +163,9 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NUM}\""
|
||||||
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
|
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
|
||||||
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
||||||
--resource-dir dist/linux/resources
|
--resource-dir dist/linux/resources
|
||||||
- name: Patch Cryptomator.AppDir
|
- name: Patch Cryptomator.AppDir
|
||||||
@@ -155,7 +189,8 @@ jobs:
|
|||||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||||
- name: Download AppImageKit
|
- name: Download AppImageKit
|
||||||
run: |
|
run: |
|
||||||
curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage
|
curl --silent --fail-with-body --proto "=https" -L "https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${{ matrix.arch }}.AppImage" -o appimagetool.AppImage
|
||||||
|
echo "${{ matrix.appimagetool-sha }} appimagetool.AppImage" | shasum -a256 --check
|
||||||
chmod +x appimagetool.AppImage
|
chmod +x appimagetool.AppImage
|
||||||
./appimagetool.AppImage --appimage-extract
|
./appimagetool.AppImage --appimage-extract
|
||||||
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
|
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
|
||||||
@@ -167,26 +202,27 @@ jobs:
|
|||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
run: >
|
run: >
|
||||||
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage
|
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.AppImage
|
||||||
-u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync"
|
-u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.arch }}.AppImage.zsync"
|
||||||
--sign --sign-key=615D449FE6E6A235
|
--sign --sign-key=615D449FE6E6A235
|
||||||
- name: Create detached GPG signatures
|
- name: Create detached GPG signatures
|
||||||
run: |
|
run: |
|
||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: appimage-${{ matrix.appimage-suffix }}
|
name: appimage-${{ matrix.arch }}
|
||||||
path: |
|
path: |
|
||||||
cryptomator-*.AppImage
|
cryptomator-*.AppImage
|
||||||
cryptomator-*.AppImage.zsync
|
cryptomator-*.AppImage.zsync
|
||||||
cryptomator-*.asc
|
cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish AppImage on GitHub Releases
|
- name: Publish AppImage on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: inputs.upload-to-draft
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||||
with:
|
with:
|
||||||
|
draft: true
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
files: |
|
files: |
|
||||||
@@ -194,67 +230,24 @@ jobs:
|
|||||||
cryptomator-*.zsync
|
cryptomator-*.zsync
|
||||||
cryptomator-*.asc
|
cryptomator-*.asc
|
||||||
|
|
||||||
create-aur-bin-pr:
|
collect-sha256sums:
|
||||||
name: Create PR for aur-bin repo
|
name: Collect AppImage checksums
|
||||||
needs: [build, get-version]
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable'
|
needs: [build]
|
||||||
|
if: inputs.upload-to-draft
|
||||||
|
outputs:
|
||||||
|
x64-sha256sum: ${{ steps.sha256sum.outputs.x64-sha256sum }}
|
||||||
|
aarch64-sha256sum: ${{ steps.sha256sum.outputs.aarch64-sha256sum }}
|
||||||
steps:
|
steps:
|
||||||
- name: Download AppImages
|
- name: Download AppImage artifacts
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||||
with:
|
with:
|
||||||
path: downloads/
|
pattern: appimage-*
|
||||||
merge-multiple: true
|
path: appimage-artifacts
|
||||||
- name: Compute sha256 hash of AppImages
|
- name: Compute SHA256 sums
|
||||||
id: checksums
|
id: sha256sum
|
||||||
run: |
|
run: |
|
||||||
X64_SHA256=$(sha256sum downloads/cryptomator-*-x86_64.AppImage | cut -d ' ' -f1)
|
read -ra X64_SUM < <(sha256sum appimage-artifacts/appimage-x86_64/cryptomator-*-x86_64.AppImage)
|
||||||
echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT"
|
read -ra AARCH64_SUM < <(sha256sum appimage-artifacts/appimage-aarch64/cryptomator-*-aarch64.AppImage)
|
||||||
AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1)
|
echo "x64-sha256sum=${X64_SUM[0]}" >> "$GITHUB_OUTPUT"
|
||||||
echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT"
|
echo "aarch64-sha256sum=${AARCH64_SUM[0]}" >> "$GITHUB_OUTPUT"
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
repository: 'cryptomator/aur-bin'
|
|
||||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get -y install makepkg pacman-package-manager
|
|
||||||
- name: Checkout release branch
|
|
||||||
run: |
|
|
||||||
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
|
|
||||||
- name: Update build file
|
|
||||||
run: |
|
|
||||||
sed -i -e 's|^pkgver=.*$|pkgver=${{ needs.get-version.outputs.semVerStr }}|' PKGBUILD
|
|
||||||
sed -i -e 's|^pkgrel=.*$|pkgrel=1|' PKGBUILD
|
|
||||||
sed -i -e "s|^sha256sums_x86_64=.*$|sha256sums_x86_64=('${{ steps.checksums.outputs.x64-sha256sum }}'|" PKGBUILD
|
|
||||||
sed -i -e "s|^sha256sums_aarch64=.*$|sha256sums_aarch64=('${{ steps.checksums.outputs.aarch64-sha256sum}}'|" PKGBUILD
|
|
||||||
makepkg --printsrcinfo > .SRCINFO
|
|
||||||
- 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
|
|
||||||
id: create-pr
|
|
||||||
run: |
|
|
||||||
printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update build instructions\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md
|
|
||||||
URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md)
|
|
||||||
echo "PR_URL=$URL" >> "$GITHUB_OUTPUT"
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
|
||||||
SLACK_ICON: false
|
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
|
||||||
SLACK_TITLE: "AUR-bin release PR for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created."
|
|
||||||
SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.PR_URL }}|PR> on how to proceed."
|
|
||||||
SLACK_FOOTER: false
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
name: Create PR for AUR
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
tag:
|
|
||||||
description: 'Release tag'
|
|
||||||
required: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
get-version:
|
|
||||||
uses: ./.github/workflows/get-version.yml
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.tag }}
|
|
||||||
tarball:
|
|
||||||
name: Determines tarball url and compute checksum
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [get-version]
|
|
||||||
if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable'
|
|
||||||
env:
|
|
||||||
INPUT_TAG: ${{ inputs.tag }}
|
|
||||||
outputs:
|
|
||||||
url: ${{ steps.url.outputs.url}}
|
|
||||||
sha256: ${{ steps.sha256.outputs.sha256}}
|
|
||||||
steps:
|
|
||||||
- name: Determine tarball url
|
|
||||||
id: url
|
|
||||||
run: |
|
|
||||||
URL="";
|
|
||||||
if [[ -n "${INPUT_TAG}" ]]; then
|
|
||||||
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${INPUT_TAG}.tar.gz"
|
|
||||||
else
|
|
||||||
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz"
|
|
||||||
fi
|
|
||||||
echo "url=${URL}" >> "$GITHUB_OUTPUT"
|
|
||||||
- name: Download source tarball and compute checksum
|
|
||||||
id: sha256
|
|
||||||
run: |
|
|
||||||
curl --silent --fail-with-body -L -H "Accept: application/vnd.github+json" ${{ steps.url.outputs.url }} --output cryptomator.tar.gz
|
|
||||||
TARBALL_SHA256=$(sha256sum cryptomator.tar.gz | cut -d ' ' -f1)
|
|
||||||
echo "sha256=${TARBALL_SHA256}" >> "$GITHUB_OUTPUT"
|
|
||||||
aur:
|
|
||||||
name: Create PR for AUR
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [tarball, get-version]
|
|
||||||
env:
|
|
||||||
AUR_PR_URL: tbd
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
repository: 'cryptomator/aur'
|
|
||||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install makepkg pacman-package-manager
|
|
||||||
- name: Checkout release branch
|
|
||||||
run: |
|
|
||||||
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
|
|
||||||
- name: Update build file
|
|
||||||
run: |
|
|
||||||
sed -i -e 's|^pkgver=.*$|pkgver=${{ needs.get-version.outputs.semVerStr }}|' PKGBUILD
|
|
||||||
sed -i -e 's|^pkgrel=.*$|pkgrel=1|' PKGBUILD
|
|
||||||
sed -i -e "s|^sha256sums=.*$|sha256sums=('${{ needs.tarball.outputs.sha256 }}'|" PKGBUILD
|
|
||||||
makepkg --printsrcinfo > .SRCINFO
|
|
||||||
- name: Commit and push
|
|
||||||
run: |
|
|
||||||
git config user.name "${{ github.actor }}"
|
|
||||||
git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
|
|
||||||
git config push.autoSetupRemote true
|
|
||||||
git stage .
|
|
||||||
git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}"
|
|
||||||
git push
|
|
||||||
- name: Create pull request
|
|
||||||
run: |
|
|
||||||
printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update build instructions\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md
|
|
||||||
PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md)
|
|
||||||
echo "AUR_PR_URL=$PR_URL" >> "$GITHUB_ENV"
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
|
||||||
- name: Slack Notification
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
|
||||||
SLACK_ICON: false
|
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
|
||||||
SLACK_TITLE: "AUR release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created."
|
|
||||||
SLACK_MESSAGE: "See <${{ env.AUR_PR_URL }}|PR> on how to proceed."
|
|
||||||
SLACK_FOOTER: false
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
@@ -37,7 +37,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
download-file:
|
download-file:
|
||||||
name: Downloads the file into the VM
|
name: Downloads the file into the VM
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
outputs:
|
outputs:
|
||||||
fileName: ${{ steps.extractName.outputs.fileName}}
|
fileName: ${{ steps.extractName.outputs.fileName}}
|
||||||
env:
|
env:
|
||||||
@@ -49,21 +49,21 @@ jobs:
|
|||||||
url="${INPUT_URL}"
|
url="${INPUT_URL}"
|
||||||
echo "fileName=${url##*/}" >> $GITHUB_OUTPUT
|
echo "fileName=${url##*/}" >> $GITHUB_OUTPUT
|
||||||
- name: Download file
|
- name: Download file
|
||||||
run: curl "${INPUT_URL}" -L -o "${{steps.extractName.outputs.fileName}}" --fail-with-body
|
run: curl --silent --fail-with-body --proto "=https" -L "${INPUT_URL}" -o "${{steps.extractName.outputs.fileName}}"
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.extractName.outputs.fileName }}
|
name: ${{ steps.extractName.outputs.fileName }}
|
||||||
path: ${{ steps.extractName.outputs.fileName }}
|
path: ${{ steps.extractName.outputs.fileName }}
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
allowlist-kaspersky:
|
allowlist-kaspersky:
|
||||||
name: Anti Virus Allowlisting Kaspersky
|
name: Anti Virus Allowlisting Kaspersky
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
needs: download-file
|
needs: download-file
|
||||||
if: inputs.kaspersky
|
if: inputs.kaspersky
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: ${{ needs.download-file.outputs.fileName }}
|
name: ${{ needs.download-file.outputs.fileName }}
|
||||||
path: upload
|
path: upload
|
||||||
@@ -78,12 +78,12 @@ jobs:
|
|||||||
local-dir: ./upload/
|
local-dir: ./upload/
|
||||||
allowlist-avast:
|
allowlist-avast:
|
||||||
name: Anti Virus Allowlisting Avast
|
name: Anti Virus Allowlisting Avast
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
needs: download-file
|
needs: download-file
|
||||||
if: inputs.avast
|
if: inputs.avast
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: ${{ needs.download-file.outputs.fileName }}
|
name: ${{ needs.download-file.outputs.fileName }}
|
||||||
path: upload
|
path: upload
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Cache SonarCloud packages
|
- name: Cache SonarCloud packages
|
||||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
with:
|
with:
|
||||||
path: ~/.sonar/cache
|
path: ~/.sonar/cache
|
||||||
key: ${{ runner.os }}-sonar
|
key: ${{ runner.os }}-sonar
|
||||||
@@ -47,40 +47,3 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
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: -->
|
|
||||||
|
|||||||
@@ -74,10 +74,10 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
SLACK_ICON: false
|
SLACK_ICON: ''
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
SLACK_TITLE: "JDK update available"
|
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_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
|
MSG_MINIMAL: true
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
name: Build Debian Package
|
name: Build Debian Package
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 22 20 * *'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
semver:
|
semver:
|
||||||
@@ -71,11 +73,11 @@ jobs:
|
|||||||
- name: Download OpenJFX jmods
|
- name: Download OpenJFX jmods
|
||||||
id: download-jmods
|
id: download-jmods
|
||||||
run: |
|
run: |
|
||||||
curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
|
curl --silent --fail-with-body --proto "=https" -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
|
||||||
echo "${{ env.OPENJFX_JMODS_AMD64_HASH }} openjfx-amd64.zip" | shasum -a256 --check
|
echo "${{ env.OPENJFX_JMODS_AMD64_HASH }} openjfx-amd64.zip" | shasum -a256 --check
|
||||||
mkdir -p jmods/amd64
|
mkdir -p jmods/amd64
|
||||||
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64
|
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64
|
||||||
curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
|
curl --silent --fail-with-body --proto "=https" -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
|
||||||
echo "${{ env.OPENJFX_JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | shasum -a256 --check
|
echo "${{ env.OPENJFX_JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | shasum -a256 --check
|
||||||
mkdir -p jmods/aarch64
|
mkdir -p jmods/aarch64
|
||||||
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64
|
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64
|
||||||
@@ -143,7 +145,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: linux-deb-package
|
name: linux-deb-package
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -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 }}
|
||||||
@@ -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 -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
|
|
||||||
@@ -14,6 +14,9 @@ on:
|
|||||||
semVerNum:
|
semVerNum:
|
||||||
description: "The numerical part of the version string"
|
description: "The numerical part of the version string"
|
||||||
value: ${{ jobs.determine-version.outputs.semVerNum}}
|
value: ${{ jobs.determine-version.outputs.semVerNum}}
|
||||||
|
semVerSuffix:
|
||||||
|
description: "The suffix of the version string"
|
||||||
|
value: ${{ jobs.determine-version.outputs.semVerSuffix}}
|
||||||
revNum:
|
revNum:
|
||||||
description: "The revision number"
|
description: "The revision number"
|
||||||
value: ${{ jobs.determine-version.outputs.revNum}}
|
value: ${{ jobs.determine-version.outputs.revNum}}
|
||||||
@@ -32,6 +35,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
semVerNum: ${{ steps.versions.outputs.semVerNum }}
|
semVerNum: ${{ steps.versions.outputs.semVerNum }}
|
||||||
semVerStr: ${{ steps.versions.outputs.semVerStr }}
|
semVerStr: ${{ steps.versions.outputs.semVerStr }}
|
||||||
|
semVerSuffix: ${{ steps.versions.outputs.semVerSuffix }}
|
||||||
revNum: ${{ steps.versions.outputs.revNum }}
|
revNum: ${{ steps.versions.outputs.revNum }}
|
||||||
type: ${{ steps.versions.outputs.type}}
|
type: ${{ steps.versions.outputs.type}}
|
||||||
steps:
|
steps:
|
||||||
@@ -54,25 +58,30 @@ jobs:
|
|||||||
else
|
else
|
||||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||||
fi
|
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`
|
REVCOUNT=`git rev-list --count HEAD`
|
||||||
|
|
||||||
TYPE="unknown"
|
TYPE="unknown"
|
||||||
if [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
if [[ -z $SEM_VER_SUFFIX ]]; then
|
||||||
TYPE="stable"
|
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"
|
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"
|
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"
|
TYPE="rc"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
|
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
|
||||||
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
|
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
|
||||||
|
echo "semVerSuffix=${SEM_VER_SUFFIX}" >> $GITHUB_OUTPUT
|
||||||
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
|
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
|
||||||
echo "type=${TYPE}" >> $GITHUB_OUTPUT
|
echo "type=${TYPE}" >> $GITHUB_OUTPUT
|
||||||
env:
|
env:
|
||||||
VERSION_STRING: ${{ inputs.version }}
|
VERSION_STRING: ${{ inputs.version }}
|
||||||
- name: Validate Version
|
- name: Validate Version
|
||||||
uses: skymatic/semver-validation-action@7a6ae1c9e121540d11c9c7e4e667c83d583aa153 # v3.0.0
|
uses: skymatic/semver-validation-action@7c80b6b03a18b42884761daa9862ff5683ec8c8a # v4.0.0
|
||||||
with:
|
with:
|
||||||
version: ${{ steps.versions.outputs.semVerStr }}
|
version: ${{ steps.versions.outputs.semVerStr }}
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
name: Build Arch package
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
schedule:
|
||||||
|
- cron: '0 21 20 * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version'
|
||||||
|
required: false
|
||||||
|
create-pr:
|
||||||
|
description: 'Create a PR for aur repo'
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'dependabot/**'
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/linux-makepkg.yml'
|
||||||
|
- 'dist/linux/makepkg/**'
|
||||||
|
- 'dist/linux/common/**'
|
||||||
|
- 'dist/linux/resources/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
get-version:
|
||||||
|
uses: ./.github/workflows/get-version.yml
|
||||||
|
with:
|
||||||
|
version: ${{ inputs.version }}
|
||||||
|
|
||||||
|
makepkg:
|
||||||
|
name: Build with makepkg
|
||||||
|
needs: [get-version]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: archlinux:base-devel
|
||||||
|
env:
|
||||||
|
PKGDEST: ${{ github.workspace }}/pkgdest
|
||||||
|
SRCDEST: ${{ github.workspace }}/srcdest
|
||||||
|
steps:
|
||||||
|
- name: Prepare pacman
|
||||||
|
run: |
|
||||||
|
pacman-key --init
|
||||||
|
pacman-key --populate archlinux
|
||||||
|
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
path: cryptomator
|
||||||
|
- name: Create build user
|
||||||
|
run: |
|
||||||
|
useradd -m builder
|
||||||
|
echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/builder
|
||||||
|
chown -R builder:builder "$GITHUB_WORKSPACE"
|
||||||
|
install -d -m 0755 -o builder -g builder "$PKGDEST" "$SRCDEST"
|
||||||
|
- name: Prepare PKGBUILD
|
||||||
|
# cannot use github.workspace due to https://github.com/actions/runner/issues/2058
|
||||||
|
run: |
|
||||||
|
export SOURCES="${SOURCES_1}${GITHUB_WORKSPACE}${SOURCES_2}"
|
||||||
|
envsubst '$PKG_VERSION $PKG_RELEASE $SOURCES $SOURCES_SHA' < cryptomator/dist/linux/makepkg/PKGBUILD.template > PKGBUILD
|
||||||
|
env:
|
||||||
|
PKG_VERSION: ${{ needs.get-version.outputs.semVerNum }}
|
||||||
|
PKG_RELEASE: 1
|
||||||
|
SOURCES_1: '"${_src_app_dir}::git+file://'
|
||||||
|
SOURCES_2: '/cryptomator"'
|
||||||
|
SOURCES_SHA: "'SKIP'"
|
||||||
|
- name: Build package with makepkg
|
||||||
|
run: >
|
||||||
|
sudo -u builder
|
||||||
|
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
|
||||||
|
makepkg --syncdeps --cleanbuild --noconfirm --log
|
||||||
|
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: arch-package
|
||||||
|
if-no-files-found: error
|
||||||
|
path: |
|
||||||
|
${{ env.PKGDEST }}/*.pkg.tar.zst
|
||||||
|
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: pkgbuild-file
|
||||||
|
if-no-files-found: error
|
||||||
|
path: |
|
||||||
|
cryptomator/dist/linux/makepkg/PKGBUILD.template
|
||||||
|
|
||||||
|
create-pr:
|
||||||
|
name: Create PR for aur repo
|
||||||
|
if: github.event_name == 'workflow_dispatch' && inputs.create-pr || github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [get-version, makepkg]
|
||||||
|
container:
|
||||||
|
image: archlinux:base-devel
|
||||||
|
env:
|
||||||
|
PKGDEST: ${{ github.workspace }}/pkgdest
|
||||||
|
SRCDEST: ${{ github.workspace }}/srcdest
|
||||||
|
steps:
|
||||||
|
- name: Prepare pacman
|
||||||
|
run: |
|
||||||
|
pacman-key --init
|
||||||
|
pacman-key --populate archlinux
|
||||||
|
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip github-cli curl
|
||||||
|
- name: Download source tarball and compute checksum
|
||||||
|
id: sha256
|
||||||
|
run: |
|
||||||
|
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz"
|
||||||
|
curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" ${URL} --output cryptomator.tar.gz
|
||||||
|
TARBALL_SHA256=$(sha256sum cryptomator.tar.gz | cut -d ' ' -f1)
|
||||||
|
echo "value=${TARBALL_SHA256}" >> "$GITHUB_OUTPUT"
|
||||||
|
env:
|
||||||
|
TAG: ${{ needs.get-version.outputs.semVerStr || github.event.release.tag_name }}
|
||||||
|
- name: Checkout cryptomator/aur repo
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
repository: 'cryptomator/aur'
|
||||||
|
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
|
- name: Create build user
|
||||||
|
run: |
|
||||||
|
useradd -m builder
|
||||||
|
echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/builder
|
||||||
|
chown -R builder:builder "$GITHUB_WORKSPACE"
|
||||||
|
install -d -m 0755 -o builder -g builder "$PKGDEST" "$SRCDEST"
|
||||||
|
- name: Import Cryptomator release signing key
|
||||||
|
# try first ubuntu. on failure try openpgp keyservers
|
||||||
|
run: >
|
||||||
|
sudo -u builder gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
|
||||||
|
|| sudo -u builder gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
|
||||||
|
- name: Checkout release branch
|
||||||
|
run: |
|
||||||
|
git config --global safe.directory '*'
|
||||||
|
git checkout -b release/${VERSION}
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.get-version.outputs.semVerStr }}
|
||||||
|
- name: Determine pkgrel
|
||||||
|
id: pkgrel
|
||||||
|
run: |
|
||||||
|
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: Download PKGBUILD template
|
||||||
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
|
with:
|
||||||
|
name: pkgbuild-file
|
||||||
|
- name: Prepare PKGBUILD
|
||||||
|
run: |
|
||||||
|
envsubst '$PKG_VERSION $PKG_RELEASE $SOURCES $SOURCES_SHA' < PKGBUILD.template > PKGBUILD
|
||||||
|
sudo -u builder makepkg --printsrcinfo > .SRCINFO
|
||||||
|
env:
|
||||||
|
PKG_VERSION: ${{ needs.get-version.outputs.semVerNum }}
|
||||||
|
PKG_RELEASE: ${{ steps.pkgrel.outputs.value }}
|
||||||
|
SOURCES: |-
|
||||||
|
"cryptomator-${pkgver//_/-}.tar.gz::https://github.com/cryptomator/cryptomator/archive/refs/tags/${pkgver//_/-}.tar.gz"
|
||||||
|
"cryptomator-${pkgver//_/-}.tar.gz.asc::https://github.com/cryptomator/cryptomator/releases/download/${pkgver//_/-}/cryptomator-${pkgver//_/-}.tar.gz.asc"
|
||||||
|
SOURCES_SHA: |-
|
||||||
|
'${{steps.sha256.outputs.value}}'
|
||||||
|
'SKIP'
|
||||||
|
- name: Build package with makepkg
|
||||||
|
run: >
|
||||||
|
sudo -u builder
|
||||||
|
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
|
||||||
|
makepkg --syncdeps --cleanbuild --noconfirm --log
|
||||||
|
- name: Commit and push
|
||||||
|
run: |
|
||||||
|
git config user.name "cryptobot"
|
||||||
|
git config user.email "cryptobot@users.noreply.github.com"
|
||||||
|
git config push.autoSetupRemote true
|
||||||
|
git stage PKGBUILD .SRCINFO
|
||||||
|
git commit -m "Prepare release ${DIST_VERSION}"
|
||||||
|
git push
|
||||||
|
env:
|
||||||
|
DIST_VERSION: ${{ steps.pkgrel.outputs.dist-version }}
|
||||||
|
- name: Create pull request
|
||||||
|
id: create-pr
|
||||||
|
run: |
|
||||||
|
printf "Created by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" > pr_body.md
|
||||||
|
PR_URL=$(gh pr create --title "Release $DIST_VERSION" --body-file pr_body.md)
|
||||||
|
echo "url=$PR_URL" >> "$GITHUB_OUTPUT"
|
||||||
|
env:
|
||||||
|
DIST_VERSION: ${{ steps.pkgrel.outputs.dist-version }}
|
||||||
|
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
|
- name: Slack Notification
|
||||||
|
if: github.event_name == 'release'
|
||||||
|
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||||
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
|
SLACK_ICON: ''
|
||||||
|
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: ''
|
||||||
|
MSG_MINIMAL: true
|
||||||
@@ -9,13 +9,45 @@ name: Build macOS .dmg for x64
|
|||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
schedule:
|
||||||
types: [published]
|
- 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:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
semVerNum:
|
||||||
description: 'Version'
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
required: false
|
required: false
|
||||||
|
revisionNum:
|
||||||
|
description: 'The revision number'
|
||||||
|
required: false
|
||||||
|
semVerSuffix:
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: false
|
||||||
|
default: '-SNAPSHOT'
|
||||||
notarize:
|
notarize:
|
||||||
description: 'Notarize'
|
description: 'Notarize'
|
||||||
required: true
|
required: true
|
||||||
@@ -25,17 +57,17 @@ on:
|
|||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
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:
|
jobs:
|
||||||
get-version:
|
build:
|
||||||
uses: ./.github/workflows/get-version.yml
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version }}
|
|
||||||
|
|
||||||
build-arm:
|
|
||||||
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [get-version]
|
outputs:
|
||||||
|
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -59,7 +91,7 @@ jobs:
|
|||||||
- name: Download OpenJFX jmods
|
- name: Download OpenJFX jmods
|
||||||
id: download-jmods
|
id: download-jmods
|
||||||
run: |
|
run: |
|
||||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
curl --silent --fail-with-body --proto "=https" -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||||
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
|
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
|
||||||
mkdir -p openjfx-jmods/
|
mkdir -p openjfx-jmods/
|
||||||
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||||
@@ -77,7 +109,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Set version
|
- 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
|
- name: Run maven
|
||||||
run: mvn -B clean package -Pmac -DskipTests
|
run: mvn -B clean package -Pmac -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
@@ -117,8 +149,8 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
--app-version "${VERSION_NUM}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
@@ -127,7 +159,7 @@ jobs:
|
|||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||||
--java-options "-Dsun.java2d.metal=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.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||||
@@ -137,34 +169,20 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
--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
|
--mac-package-identifier org.cryptomator
|
||||||
--resource-dir dist/mac/resources
|
--resource-dir dist/mac/resources
|
||||||
- name: Patch Cryptomator.app
|
- name: Patch Cryptomator.app
|
||||||
run: |
|
run: |
|
||||||
mv appdir/Cryptomator.app Cryptomator.app
|
mv appdir/Cryptomator.app Cryptomator.app
|
||||||
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
||||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
|
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
|
||||||
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
|
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
||||||
env:
|
env:
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
|
||||||
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
|
||||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||||
- name: Build and install DockTilePlugin
|
|
||||||
env:
|
|
||||||
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
|
|
||||||
run: |
|
|
||||||
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
|
|
||||||
-scheme DockTilePlugin \
|
|
||||||
-configuration Release \
|
|
||||||
-destination "platform=macOS,arch=x86_64" \
|
|
||||||
-derivedDataPath ${DERIVED_DATA_PATH} \
|
|
||||||
-quiet \
|
|
||||||
clean build
|
|
||||||
mkdir -p Cryptomator.app/Contents/PlugIns
|
|
||||||
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
|
|
||||||
rm -rf ${DERIVED_DATA_PATH}
|
|
||||||
- name: Generate license for dmg
|
- name: Generate license for dmg
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
mvn -B license:add-third-party
|
||||||
@@ -252,16 +270,14 @@ jobs:
|
|||||||
--eula "dist/mac/dmg/resources/license.rtf"
|
--eula "dist/mac/dmg/resources/license.rtf"
|
||||||
--icon ".background" 128 758
|
--icon ".background" 128 758
|
||||||
--icon ".VolumeIcon.icns" 512 758
|
--icon ".VolumeIcon.icns" 512 758
|
||||||
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
|
||||||
env:
|
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
|
||||||
- name: Codesign .dmg
|
- name: Codesign .dmg
|
||||||
run: |
|
run: |
|
||||||
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
||||||
env:
|
env:
|
||||||
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||||
- name: Notarize .dmg
|
- 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
|
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
||||||
with:
|
with:
|
||||||
app-path: 'Cryptomator-*.dmg'
|
app-path: 'Cryptomator-*.dmg'
|
||||||
@@ -269,8 +285,12 @@ jobs:
|
|||||||
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||||
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||||
xcode-path: '/Applications/Xcode_16.app'
|
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
|
- 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
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
run: |
|
run: |
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
@@ -283,7 +303,7 @@ jobs:
|
|||||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: dmg-${{ matrix.output-suffix }}
|
name: dmg-${{ matrix.output-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -291,9 +311,10 @@ jobs:
|
|||||||
Cryptomator-*.asc
|
Cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish dmg on GitHub Releases
|
- name: Publish dmg on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: inputs.upload-to-draft
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||||
with:
|
with:
|
||||||
|
draft: true
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
@@ -1,13 +1,45 @@
|
|||||||
name: Build macOS .dmg for arm64
|
name: Build macOS .dmg for arm64
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
schedule:
|
||||||
types: [published]
|
- 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:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
semVerNum:
|
||||||
description: 'Version'
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
required: false
|
required: false
|
||||||
|
revisionNum:
|
||||||
|
description: 'The revision number'
|
||||||
|
required: false
|
||||||
|
semVerSuffix:
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: false
|
||||||
|
default: '-SNAPSHOT'
|
||||||
notarize:
|
notarize:
|
||||||
description: 'Notarize'
|
description: 'Notarize'
|
||||||
required: true
|
required: true
|
||||||
@@ -23,17 +55,17 @@ on:
|
|||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
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:
|
jobs:
|
||||||
get-version:
|
|
||||||
uses: ./.github/workflows/get-version.yml
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version }}
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [get-version]
|
outputs:
|
||||||
|
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -57,7 +89,7 @@ jobs:
|
|||||||
- name: Download OpenJFX jmods
|
- name: Download OpenJFX jmods
|
||||||
id: download-jmods
|
id: download-jmods
|
||||||
run: |
|
run: |
|
||||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
curl --silent --fail-with-body --proto "=https" -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||||
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
|
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
|
||||||
mkdir -p openjfx-jmods/
|
mkdir -p openjfx-jmods/
|
||||||
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||||
@@ -75,7 +107,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Set version
|
- 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
|
- name: Run maven
|
||||||
run: mvn -B clean package -Pmac -DskipTests
|
run: mvn -B clean package -Pmac -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
@@ -115,8 +147,8 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
--app-version "${VERSION_NUM}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
@@ -125,7 +157,7 @@ jobs:
|
|||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||||
--java-options "-Dsun.java2d.metal=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.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||||
@@ -135,35 +167,21 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
--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 "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
||||||
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||||
--mac-package-identifier org.cryptomator
|
--mac-package-identifier org.cryptomator
|
||||||
--resource-dir dist/mac/resources
|
--resource-dir dist/mac/resources
|
||||||
- name: Patch Cryptomator.app
|
- name: Patch Cryptomator.app
|
||||||
run: |
|
run: |
|
||||||
mv appdir/Cryptomator.app Cryptomator.app
|
mv appdir/Cryptomator.app Cryptomator.app
|
||||||
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
||||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
|
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
|
||||||
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
|
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
||||||
env:
|
env:
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
|
||||||
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
|
||||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||||
- name: Build and install DockTilePlugin
|
|
||||||
env:
|
|
||||||
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
|
|
||||||
run: |
|
|
||||||
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
|
|
||||||
-scheme DockTilePlugin \
|
|
||||||
-configuration Release \
|
|
||||||
-destination "platform=macOS,arch=arm64" \
|
|
||||||
-derivedDataPath ${DERIVED_DATA_PATH} \
|
|
||||||
-quiet \
|
|
||||||
clean build
|
|
||||||
mkdir -p Cryptomator.app/Contents/PlugIns
|
|
||||||
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
|
|
||||||
rm -rf ${DERIVED_DATA_PATH}
|
|
||||||
- name: Generate license for dmg
|
- name: Generate license for dmg
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
mvn -B license:add-third-party
|
||||||
@@ -251,16 +269,14 @@ jobs:
|
|||||||
--eula "dist/mac/dmg/resources/license.rtf"
|
--eula "dist/mac/dmg/resources/license.rtf"
|
||||||
--icon ".background" 128 758
|
--icon ".background" 128 758
|
||||||
--icon ".VolumeIcon.icns" 512 758
|
--icon ".VolumeIcon.icns" 512 758
|
||||||
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
|
||||||
env:
|
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
|
||||||
- name: Codesign .dmg
|
- name: Codesign .dmg
|
||||||
run: |
|
run: |
|
||||||
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
||||||
env:
|
env:
|
||||||
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||||
- name: Notarize .dmg
|
- 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
|
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
||||||
with:
|
with:
|
||||||
app-path: 'Cryptomator-*.dmg'
|
app-path: 'Cryptomator-*.dmg'
|
||||||
@@ -268,8 +284,12 @@ jobs:
|
|||||||
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||||
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||||
xcode-path: '/Applications/Xcode_16.app'
|
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
|
- 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
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
run: |
|
run: |
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
@@ -282,7 +302,7 @@ jobs:
|
|||||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: dmg-${{ matrix.output-suffix }}
|
name: dmg-${{ matrix.output-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -290,9 +310,10 @@ jobs:
|
|||||||
Cryptomator-*.asc
|
Cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish dmg on GitHub Releases
|
- name: Publish dmg on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: inputs.upload-to-draft
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||||
with:
|
with:
|
||||||
|
draft: true
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
no-response:
|
no-response:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||||
with:
|
with:
|
||||||
days-before-stale: 14
|
days-before-stale: 14
|
||||||
days-before-close: 0
|
days-before-close: 0
|
||||||
|
|||||||
@@ -5,35 +5,141 @@ on:
|
|||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
notify:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- name: Download source tarball
|
- name: Notify about DEB build
|
||||||
run: |
|
|
||||||
curl -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz --output cryptomator-${{ github.event.release.tag_name }}.tar.gz
|
|
||||||
- name: Sign source tarball with key 615D449FE6E6A235
|
|
||||||
run: |
|
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
|
||||||
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
|
|
||||||
env:
|
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
|
||||||
- name: Publish asc on GitHub Releases
|
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
|
||||||
with:
|
|
||||||
fail_on_unmatched_files: true
|
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
|
||||||
files: |
|
|
||||||
cryptomator-*.tar.gz.asc
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
SLACK_ICON: false
|
SLACK_ICON: ''
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
|
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_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/debian.yml|build deb Package>."
|
||||||
SLACK_FOOTER: false
|
SLACK_FOOTER: ''
|
||||||
MSG_MINIMAL: true
|
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: ''
|
||||||
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
|
SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
|
||||||
|
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/winget.yml|release to winget>."
|
||||||
|
SLACK_FOOTER: ''
|
||||||
|
MSG_MINIMAL: true
|
||||||
|
|
||||||
|
trigger-website-update:
|
||||||
|
needs: [check-release]
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
if: needs.check-release.outputs.release-kind == 'stable'
|
||||||
|
steps:
|
||||||
|
- name: Start website update workflow
|
||||||
|
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||||
|
with:
|
||||||
|
event-type: desktop-release
|
||||||
|
token: ${{ secrets.CRYPTOBOT_WORKFLOW_DISPATCH_TOKEN }}
|
||||||
|
repository: cryptomator/cryptomator.github.io
|
||||||
|
client-payload: '{ "version": "${{ github.event.release.tag_name }}", "release": ${{ toJson(github.event.release.assets) }} }'
|
||||||
|
|
||||||
|
trigger-docs-update:
|
||||||
|
needs: [check-release, get-asset-urls]
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
if: needs.get-asset-urls.outputs.is-windows-release == 'true' && needs.check-release.outputs.release-kind == 'stable'
|
||||||
|
steps:
|
||||||
|
- name: Start docs update workflow
|
||||||
|
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||||
|
with:
|
||||||
|
event-type: desktop-release
|
||||||
|
token: ${{ secrets.CRYPTOBOT_WORKFLOW_DISPATCH_TOKEN }}
|
||||||
|
repository: cryptomator/docs
|
||||||
|
client-payload: '{ "version": "${{ github.event.release.tag_name }}", "release": ${{ toJson(github.event.release.assets) }} }'
|
||||||
|
|
||||||
|
|||||||
@@ -43,13 +43,14 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file
|
- name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file
|
||||||
|
if: ${{ ! (contains(github.event.head_commit.message, '[skip metadata check]') || contains(github.event.head_commit.message, '[metadata check skip]')) }}
|
||||||
run: |
|
run: |
|
||||||
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\">" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
|
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\">" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
|
||||||
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
|
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Cache NVD DB
|
- name: Cache NVD DB
|
||||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
with:
|
with:
|
||||||
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
||||||
key: dependency-check-${{ github.run_id }}
|
key: dependency-check-${{ github.run_id }}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||||
with:
|
with:
|
||||||
days-before-stale: 365
|
days-before-stale: 365
|
||||||
days-before-close: 90
|
days-before-close: 90
|
||||||
|
|||||||
+109
-119
@@ -1,13 +1,48 @@
|
|||||||
name: Build Windows Installer
|
name: Build Windows Installer
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
schedule:
|
||||||
types: [published]
|
- 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:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
semVerNum:
|
||||||
description: 'Version'
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
required: false
|
required: false
|
||||||
|
revisionNum:
|
||||||
|
description: 'The revision number'
|
||||||
|
required: false
|
||||||
|
semVerSuffix:
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: false
|
||||||
|
default: '-SNAPSHOT'
|
||||||
sign:
|
sign:
|
||||||
description: 'Sign binaries'
|
description: 'Sign binaries'
|
||||||
required: false
|
required: false
|
||||||
@@ -22,6 +57,9 @@ on:
|
|||||||
|
|
||||||
|
|
||||||
env:
|
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: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_windows-x64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AMD64_HASH: '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3'
|
OPENJFX_JMODS_AMD64_HASH: '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3'
|
||||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
||||||
@@ -34,22 +72,18 @@ defaults:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
|
||||||
uses: ./.github/workflows/get-version.yml
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version }}
|
|
||||||
|
|
||||||
build-msi:
|
build-msi:
|
||||||
name: Build .msi Installer
|
name: Build .msi Installer
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [ get-version ]
|
outputs:
|
||||||
|
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- arch: x64
|
- arch: x64
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
java-dist: 'zulu' #cannot use temurin, see https://github.com/cryptomator/cryptomator/issues/3824#issuecomment-2829827427
|
java-dist: 'zulu' #cannot use temurin, see https://github.com/cryptomator/cryptomator/issues/3824#issuecomment-2829827427
|
||||||
java-version: '25.0.1+8'
|
java-version: '26.0.1+8'
|
||||||
java-package: 'jdk'
|
java-package: 'jdk'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
@@ -72,7 +106,7 @@ jobs:
|
|||||||
if: matrix.arch == 'x64'
|
if: matrix.arch == 'x64'
|
||||||
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
|
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
|
||||||
run: |
|
run: |
|
||||||
curl --output openjfx-jmods.zip -L "${{ env.OPENJFX_JMODS_AMD64 }}"
|
curl --silent --fail-with-body --proto "=https" -L "${{ env.OPENJFX_JMODS_AMD64 }}" --output openjfx-jmods.zip
|
||||||
if(!(Get-FileHash -Path openjfx-jmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) {
|
if(!(Get-FileHash -Path openjfx-jmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) {
|
||||||
throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}.";
|
throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}.";
|
||||||
}
|
}
|
||||||
@@ -94,7 +128,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Set version
|
- 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
|
- name: Run maven
|
||||||
run: mvn -B clean package -Pwin -DskipTests
|
run: mvn -B clean package -Pwin -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
@@ -134,13 +168,13 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
--app-version "${VERSION_NUM}.${REVISION_NUM}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
|
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
--java-options "-Xmx256m"
|
--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 "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
|
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
|
||||||
@@ -151,12 +185,13 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.loopbackAlias=\"cryptomator-vault\""
|
--java-options "-Dcryptomator.loopbackAlias=\"cryptomator-vault\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--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.autoStartShellLinkName=\"Cryptomator\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
|
--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.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json\""
|
||||||
--java-options "-Dcryptomator.disableUpdateCheck=false"
|
--java-options "-Dcryptomator.disableUpdateCheck=false"
|
||||||
--java-options "-XX:ErrorFile=C:/cryptomator/cryptomator_crash.log"
|
--java-options "-XX:ErrorFile=C:/cryptomator/cryptomator_crash.log"
|
||||||
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||||
--resource-dir dist/win/resources
|
--resource-dir dist/win/resources
|
||||||
--icon dist/win/resources/Cryptomator.ico
|
--icon dist/win/resources/Cryptomator.ico
|
||||||
--add-launcher "Cryptomator (Debug)=dist/win/debug-launcher.properties"
|
--add-launcher "Cryptomator (Debug)=dist/win/debug-launcher.properties"
|
||||||
@@ -185,14 +220,12 @@ jobs:
|
|||||||
}
|
}
|
||||||
$jar.Dispose()
|
$jar.Dispose()
|
||||||
}
|
}
|
||||||
- name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
|
- name: Get msi helper dll for code signing
|
||||||
shell: pwsh
|
|
||||||
run: |
|
run: |
|
||||||
New-Item -Path appdir/jpackage-jmod -ItemType Directory
|
${JAVA_HOME}/bin/jpackage --type msi --win-upgrade-uuid bda45523-42b1-4cae-9354-a45475ed4775 --app-image appdir/Cryptomator --dest /tmp/ --name Test --vendor Test --copyright "None" --app-version "1.0" --temp msi-helper
|
||||||
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
|
find ./msi-helper/ -type f -name msica.dll -exec mv {} ./appdir \;
|
||||||
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
|
|
||||||
- name: Sign DLLs with Azure Trusted Signing
|
- 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
|
uses: ./.github/actions/win-sign-action
|
||||||
with:
|
with:
|
||||||
base-dir: ${{ github.workspace }}\appdir
|
base-dir: ${{ github.workspace }}\appdir
|
||||||
@@ -201,17 +234,6 @@ jobs:
|
|||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
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
|
- name: Replace DLLs inside jars with signed ones
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
@@ -237,6 +259,16 @@ jobs:
|
|||||||
"-Dlicense.failOnMissing=true"
|
"-Dlicense.failOnMissing=true"
|
||||||
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
|
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
- name: Create file association file from template
|
||||||
|
working-directory: dist/win
|
||||||
|
run: |
|
||||||
|
$Env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT = "${Env:JP_WIXWIZARD_RESOURCES}".Replace('\', '\\');
|
||||||
|
Get-Content .\resources\FAvaultFile.template.properties `
|
||||||
|
| ForEach-Object { $ExecutionContext.InvokeCommand.ExpandString($_) } `
|
||||||
|
| Out-File -FilePath .\resources\FAvaultFile.properties
|
||||||
|
env:
|
||||||
|
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources/ # requires abs path, used in resources/main.wxs
|
||||||
|
shell: pwsh
|
||||||
- name: Create MSI
|
- name: Create MSI
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jpackage
|
${JAVA_HOME}/bin/jpackage
|
||||||
@@ -247,21 +279,21 @@ jobs:
|
|||||||
--dest installer
|
--dest installer
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
--app-version "${VERSION_NUM}.${REVISION_NUM}"
|
||||||
--win-menu
|
--win-menu
|
||||||
--win-dir-chooser
|
--win-dir-chooser
|
||||||
--win-shortcut-prompt
|
--win-shortcut-prompt
|
||||||
--win-update-url "https:\\cryptomator.org\downloads"
|
--win-update-url "https://cryptomator.org/downloads"
|
||||||
--win-menu-group Cryptomator
|
--win-menu-group Cryptomator
|
||||||
--resource-dir dist/win/resources
|
--resource-dir dist/win/resources
|
||||||
--license-file dist/win/resources/license.rtf
|
--license-file dist/win/resources/license.rtf
|
||||||
--file-associations dist/win/resources/FAvaultFile.properties
|
--file-associations dist/win/resources/FAvaultFile.properties
|
||||||
env:
|
env:
|
||||||
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
|
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources/ # requires abs path, used in resources/main.wxs
|
||||||
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
|
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir\
|
||||||
- name: Sign MSI with Azure Trusted Signing
|
- 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
|
uses: ./.github/actions/win-sign-action
|
||||||
with:
|
with:
|
||||||
base-dir: ${{ github.workspace }}\installer
|
base-dir: ${{ github.workspace }}\installer
|
||||||
@@ -270,8 +302,12 @@ jobs:
|
|||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
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
|
- 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
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
run: |
|
run: |
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
@@ -280,7 +316,7 @@ jobs:
|
|||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: msi-${{ matrix.arch }}
|
name: msi-${{ matrix.arch }}
|
||||||
path: |
|
path: |
|
||||||
@@ -291,7 +327,9 @@ jobs:
|
|||||||
build-exe:
|
build-exe:
|
||||||
name: Build .exe installer
|
name: Build .exe installer
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [ get-version, build-msi ]
|
needs: [ build-msi ]
|
||||||
|
outputs:
|
||||||
|
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
@@ -311,7 +349,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WIX_VERSION: ${{ env.WIX_VERSION }}
|
WIX_VERSION: ${{ env.WIX_VERSION }}
|
||||||
- name: Download .msi
|
- name: Download .msi
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: msi-${{ matrix.arch }}
|
name: msi-${{ matrix.arch }}
|
||||||
path: dist/win/bundle/resources
|
path: dist/win/bundle/resources
|
||||||
@@ -338,38 +376,39 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Download WinFsp
|
- name: Download WinFsp
|
||||||
run: |
|
run: |
|
||||||
curl --output $env:WINFSP_PATH -L ${{ env.WINFSP_MSI }}
|
curl --silent --fail-with-body --proto "=https" -L "$env:WINFSP_MSI" --output $env:WINFSP_PATH
|
||||||
$computedHash = (Get-FileHash -Path $env:WINFSP_PATH -Algorithm SHA256).Hash.ToLower()
|
$computedHash = (Get-FileHash -Path "$env:WINFSP_PATH" -Algorithm SHA256).Hash.ToLower()
|
||||||
if ($computedHash -ne "${{ env.WINFSP_MSI_HASH }}") {
|
if ($computedHash -ne "$env:WINFSP_MSI_HASH") {
|
||||||
throw "Checksum mismatch for $env:WINFSP_PATH (expected ${{ env.WINFSP_MSI_HASH }}, got $computedHash)."
|
throw "Checksum mismatch for ${env:WINFSP_PATH} (expected ${env:WINFSP_MSI_HASH}, got $computedHash)."
|
||||||
}
|
}
|
||||||
env:
|
env:
|
||||||
WINFSP_PATH: 'dist/win/bundle/resources/winfsp.msi'
|
WINFSP_PATH: 'dist/win/bundle/resources/winfsp.msi'
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Download Legacy-WinFsp uninstaller
|
- name: Download Legacy-WinFsp uninstaller
|
||||||
run: |
|
run: |
|
||||||
curl --output dist/win/bundle/resources/winfsp-uninstaller.exe -L ${{ env.WINFSP_UNINSTALLER }}
|
curl --silent --fail-with-body --proto "=https" -L ${{ env.WINFSP_UNINSTALLER }} --output dist/win/bundle/resources/winfsp-uninstaller.exe
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Create Wix Burn bundle
|
- name: Create Wix Burn bundle
|
||||||
working-directory: dist/win
|
working-directory: dist/win
|
||||||
run: >
|
run: >
|
||||||
wix build
|
wix build
|
||||||
-define BundleName="Cryptomator"
|
-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 BundleVendor="Skymatic GmbH"
|
||||||
-define BundleCopyright="(C) 2016 - 2025 Skymatic GmbH"
|
-define BundleCopyright="(C) 2016 - 2026 Skymatic GmbH"
|
||||||
-define AboutUrl="https://cryptomator.org"
|
-define AboutUrl="https://cryptomator.org"
|
||||||
-define HelpUrl="https://cryptomator.org/contact"
|
-define HelpUrl="https://cryptomator.org/contact"
|
||||||
-define UpdateUrl="https://cryptomator.org/downloads/"
|
-define UpdateUrl="https://cryptomator.org/downloads/"
|
||||||
-ext "WixToolset.Util.wixext"
|
-ext "WixToolset.Util.wixext"
|
||||||
-ext "WixToolset.BootstrapperApplications.wixext"
|
-ext "WixToolset.BootstrapperApplications.wixext"
|
||||||
./bundle/bundleWithWinfsp.wxs
|
./bundle/bundleWithWinfsp.wxs
|
||||||
-out "../../installer/unsigned/Cryptomator-Installer.exe"
|
-out "../../installer/Cryptomator-Installer.exe"
|
||||||
- name: Detach burn engine in preparation to sign
|
- name: Detach burn engine in preparation to sign
|
||||||
|
if: inputs.sign || github.event_name == 'schedule'
|
||||||
run: >
|
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
|
- 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
|
uses: ./.github/actions/win-sign-action
|
||||||
with:
|
with:
|
||||||
base-dir: ${{ github.workspace }}\tmp
|
base-dir: ${{ github.workspace }}\tmp
|
||||||
@@ -379,21 +418,14 @@ jobs:
|
|||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
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
|
- name: Reattach signed burn engine to installer
|
||||||
run: >
|
if: inputs.sign || github.event_name == 'schedule'
|
||||||
wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
|
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
|
- 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
|
uses: ./.github/actions/win-sign-action
|
||||||
with:
|
with:
|
||||||
base-dir: ${{ github.workspace }}\installer
|
base-dir: ${{ github.workspace }}\installer
|
||||||
@@ -403,18 +435,12 @@ jobs:
|
|||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||||
- name: Sign installer with Actalis CodeSigner
|
- id: sha256sum
|
||||||
if: inputs.sign || github.event_name == 'release'
|
run: |
|
||||||
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
read -ra CMD_OUTPUT < <(sha256sum installer/Cryptomator-*.exe)
|
||||||
with:
|
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||||
base-dir: 'installer'
|
|
||||||
file-extensions: 'exe'
|
|
||||||
sign-description: 'Cryptomator Bundle Installer'
|
|
||||||
sign-url: 'https://cryptomator.org'
|
|
||||||
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
|
|
||||||
password: ${{ secrets.WIN_CODESIGN_PW }}
|
|
||||||
- name: Add possible alpha/beta tags to installer name
|
- 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
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
run: |
|
run: |
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
@@ -423,7 +449,7 @@ jobs:
|
|||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: exe-${{ matrix.executable-suffix }}
|
name: exe-${{ matrix.executable-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -433,58 +459,22 @@ jobs:
|
|||||||
|
|
||||||
publish:
|
publish:
|
||||||
name: Publish installers to the github release
|
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
|
runs-on: ubuntu-latest
|
||||||
needs: [ build-msi, build-exe ]
|
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:
|
steps:
|
||||||
- name: Download installers
|
- name: Download installers
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
- name: Publish installers on GitHub Releases
|
- name: Publish installers on GitHub Releases
|
||||||
id: publish
|
id: publish
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||||
with:
|
with:
|
||||||
|
draft: true
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
# do not change ordering of filelist, required for correct job output
|
|
||||||
files: |
|
files: |
|
||||||
*x64.msi
|
*x64.msi
|
||||||
*x64.exe
|
*x64.exe
|
||||||
*.asc
|
*.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
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
- name: Submit package
|
- name: Submit package
|
||||||
uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # no_specific_version
|
uses: vedantmgoyal2009/winget-releaser@7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 # no_specific_version
|
||||||
with:
|
with:
|
||||||
identifier: Cryptomator.Cryptomator
|
identifier: Cryptomator.Cryptomator
|
||||||
version: ${{ inputs.tag }}
|
version: ${{ inputs.tag }}
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator/mnt" -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="@{userhome}/.config/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator/mnt" -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">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{userhome}/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dfuse.experimental="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="@{userhome}/.config/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/.config/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/.config/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/.local/share/Cryptomator-Dev/logs" -Dcryptomator.pluginDir="@{userhome}/.local/share/Cryptomator-Dev/plugins" -Dcryptomator.mountPointsDir="@{userhome}/.local/share/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dcryptomator.hub.enableTrustOnFirstUse=true -Dfuse.experimental="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">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator" -Dcryptomator.pluginDir="@{appdata}/Cryptomator/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.integrationsWin.windowsHelloKeychainPaths="@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -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="@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator" -Dcryptomator.pluginDir="@{appdata}/Cryptomator/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.integrationsWin.windowsHelloKeychainPaths="@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -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">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator-Dev" -Dcryptomator.pluginDir="@{appdata}/Cryptomator-Dev/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.integrationsWin.windowsHelloKeychainPaths="@{appdata}/Cryptomator-Dev/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/windowsHelloKeychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator-Dev" -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="@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator-Dev" -Dcryptomator.pluginDir="@{appdata}/Cryptomator-Dev/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.integrationsWin.windowsHelloKeychainPaths="@{appdata}/Cryptomator-Dev/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/windowsHelloKeychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator-Dev" -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">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
</envs>
|
</envs>
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -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="@{userhome}/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -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">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
</envs>
|
</envs>
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator-Dev" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Library/Application Support/Cryptomator-Dev/mnt" -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="@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator-Dev" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Library/Application Support/Cryptomator-Dev/mnt" -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">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+49
-9
@@ -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.
|
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).
|
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
|
### Added
|
||||||
* Self-Update Mechanism ([#3948](https://github.com/cryptomator/cryptomator/pull/3948))
|
* 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))
|
* 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))
|
* 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))
|
* 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
|
||||||
* Fixed password reset/show recovery possible for vaults without masterkey file ([#4120](https://github.com/cryptomator/cryptomator/pull/4120))
|
* 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 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
|
### Changed
|
||||||
* Disable user defined app start config on Windows ([#4132](https://github.com/cryptomator/cryptomator/issues/4132))
|
* 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))
|
* Disable plugin loading by default ([#4136](https://github.com/cryptomator/cryptomator/4136))
|
||||||
* Use JDK 25 ([#4031](https://github.com/cryptomator/cryptomator/pull/4031))
|
* 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 translations
|
||||||
* Updated dependencies
|
* Updated dependencies
|
||||||
* `ch.qos.logback:*` from 1.5.19 to 1.5.31
|
* `ch.qos.logback:*` from 1.5.19 to 1.5.32
|
||||||
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.0
|
* `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.0
|
* `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.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.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: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-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-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-mac` from 1.4.1 to 1.5.0-beta3
|
||||||
* `org.cryptomator:integrations-win` from 1.5.1 to 1.6.0
|
* `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` from 3.0.0 to 3.0.1
|
||||||
|
* `org.cryptomator:webdav-nio-adapter-servlet` to 1.2.12
|
||||||
|
|
||||||
|
|||||||
Vendored
+3
-2
@@ -82,7 +82,7 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--vendor "Skymatic GmbH" \
|
--vendor "Skymatic GmbH" \
|
||||||
--java-options "--enable-preview" \
|
--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 "--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 "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--app-version "${VERSION}.${REVISION_NO}" \
|
--app-version "${VERSION}.${REVISION_NO}" \
|
||||||
@@ -99,6 +99,7 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
||||||
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
|
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
|
||||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
|
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
|
||||||
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
|
||||||
--resource-dir ../resources
|
--resource-dir ../resources
|
||||||
|
|
||||||
# transform AppDir
|
# 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
|
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||||
|
|
||||||
# load AppImageTool
|
# 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
|
chmod +x /tmp/appimagetool.AppImage
|
||||||
|
|
||||||
# create AppImage
|
# create AppImage
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
<url type="faq">https://community.cryptomator.org/c/kb/faq</url>
|
<url type="faq">https://community.cryptomator.org/c/kb/faq</url>
|
||||||
<url type="help">https://docs.cryptomator.org/</url>
|
<url type="help">https://docs.cryptomator.org/</url>
|
||||||
<url type="translate">https://translate.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">
|
<developer id="de.skymatic">
|
||||||
<name>Skymatic GmbH</name>
|
<name>Skymatic GmbH</name>
|
||||||
@@ -83,6 +84,15 @@
|
|||||||
</content_rating>
|
</content_rating>
|
||||||
|
|
||||||
<releases>
|
<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">
|
<release date="2025-11-12" version="1.18.0">
|
||||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.18.0</url>
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.18.0</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
Vendored
+2
-1
@@ -46,7 +46,7 @@ override_dh_auto_build:
|
|||||||
--vendor "Skymatic GmbH" \
|
--vendor "Skymatic GmbH" \
|
||||||
--java-options "--enable-preview" \
|
--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 "--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 "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
@@ -64,6 +64,7 @@ override_dh_auto_build:
|
|||||||
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
|
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
|
||||||
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
|
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
|
||||||
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
|
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
|
||||||
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
|
||||||
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
||||||
--resource-dir resources \
|
--resource-dir resources \
|
||||||
--verbose
|
--verbose
|
||||||
|
|||||||
@@ -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 "$@"
|
||||||
@@ -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'}
|
||||||
Vendored
+119
@@ -0,0 +1,119 @@
|
|||||||
|
# Maintainer: Aaron Graves <linux@ajgraves.com>
|
||||||
|
# Contributor: Julian Raufelder <arch@raufelder.com>
|
||||||
|
# Contributor: Morten Linderud <morten@linderud.pw>
|
||||||
|
# Contributor: Sebastian Stenzel <sebastian.stenzel@gmail.com>
|
||||||
|
# Contributor: Armin Schrenk <armin.schrenk@skymatic.de>
|
||||||
|
|
||||||
|
pkgname=cryptomator
|
||||||
|
pkgver=$PKG_VERSION
|
||||||
|
pkgrel=$PKG_RELEASE
|
||||||
|
pkgdesc="Multiplatform transparent client-side encryption of your files in the cloud."
|
||||||
|
arch=('any')
|
||||||
|
url="https://cryptomator.org/"
|
||||||
|
license=('GPL3')
|
||||||
|
depends=('fuse3' 'alsa-lib' 'hicolor-icon-theme' 'libxtst' 'libnet' 'libxrender')
|
||||||
|
makedepends=('maven' 'unzip')
|
||||||
|
optdepends=('keepassxc-cryptomator: Use KeePassXC to store vault passwords' 'ttf-hanazono: Install this font when using Japanese system language')
|
||||||
|
_jdkver=25.0.2+10
|
||||||
|
_jfxver=25.0.2
|
||||||
|
_src_app_dir=cryptomator-${pkgver//_/-}
|
||||||
|
source=($SOURCES);
|
||||||
|
source_x86_64=("jdk-${_jdkver}.tar.gz::https://github.com/adoptium/temurin${_jdkver:0:2}-binaries/releases/download/jdk-${_jdkver//\+/%2B}/OpenJDK${_jdkver:0:2}U-jdk_x64_linux_hotspot_${_jdkver//\+/_}.tar.gz"
|
||||||
|
"openjfx-${_jfxver}.zip::https://download2.gluonhq.com/openjfx/${_jfxver}/openjfx-${_jfxver}_linux-x64_bin-jmods.zip")
|
||||||
|
source_aarch64=("jdk-${_jdkver}.tar.gz::https://github.com/adoptium/temurin${_jdkver:0:2}-binaries/releases/download/jdk-${_jdkver//\+/%2B}/OpenJDK${_jdkver:0:2}U-jdk_aarch64_linux_hotspot_${_jdkver//\+/_}.tar.gz"
|
||||||
|
"openjfx-${_jfxver}.zip::https://download2.gluonhq.com/openjfx/${_jfxver}/openjfx-${_jfxver}_linux-aarch64_bin-jmods.zip")
|
||||||
|
noextract=("jdk-${_jdkver}.tar.gz" "openjfx-${_jfxver}.zip")
|
||||||
|
sha256sums=($SOURCES_SHA)
|
||||||
|
sha256sums_x86_64=('987387933b64b9833846dee373b640440d3e1fd48a04804ec01a6dbf718e8ab8'
|
||||||
|
'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b')
|
||||||
|
sha256sums_aarch64=('a9d73e711d967dc44896d4f430f73a68fd33590dabc29a7f2fb9f593425b854c'
|
||||||
|
'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646')
|
||||||
|
options=('!strip')
|
||||||
|
|
||||||
|
validpgpkeys=('58117AFA1F85B3EEC154677D615D449FE6E6A235')
|
||||||
|
|
||||||
|
build() {
|
||||||
|
export JAVA_HOME="${srcdir}/jdk-${_jdkver}"
|
||||||
|
JMODS_PATH="${srcdir}/openjfx-${_jfxver}-jmods"
|
||||||
|
#JEP 493
|
||||||
|
if ! $(${JAVA_HOME}/bin/jlink --help | grep -q "Linking from run-time image enabled"); then
|
||||||
|
JMODS_PATH="${JMODS_PATH}:${JAVA_HOME}/jmods:"
|
||||||
|
fi
|
||||||
|
|
||||||
|
tar xfz "jdk-${_jdkver}.tar.gz"
|
||||||
|
|
||||||
|
mkdir "openjfx-${_jfxver}-jmods"
|
||||||
|
unzip -j "openjfx-${_jfxver}.zip" \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d "openjfx-${_jfxver}-jmods"
|
||||||
|
|
||||||
|
cd "${srcdir}/${_src_app_dir}"
|
||||||
|
|
||||||
|
mvn -B clean package -DskipTests -Plinux
|
||||||
|
|
||||||
|
cp LICENSE.txt target
|
||||||
|
cp target/cryptomator-*.jar target/mods
|
||||||
|
|
||||||
|
cd target
|
||||||
|
|
||||||
|
"$JAVA_HOME/bin/jlink" \
|
||||||
|
--output runtime \
|
||||||
|
--module-path "$JMODS_PATH" \
|
||||||
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
|
||||||
|
--strip-native-commands \
|
||||||
|
--no-header-files \
|
||||||
|
--no-man-pages \
|
||||||
|
--strip-debug \
|
||||||
|
--compress=zip-0
|
||||||
|
|
||||||
|
##Note: jpackage does not allow -beta suffixes, have to strip those
|
||||||
|
"$JAVA_HOME/bin/jpackage" \
|
||||||
|
--type app-image \
|
||||||
|
--runtime-image runtime \
|
||||||
|
--input libs \
|
||||||
|
--module-path mods \
|
||||||
|
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \
|
||||||
|
--dest . \
|
||||||
|
--name cryptomator \
|
||||||
|
--vendor "Skymatic GmbH" \
|
||||||
|
--copyright "(C) 2016 - 2026 Skymatic GmbH" \
|
||||||
|
--java-options "--enable-preview" \
|
||||||
|
--java-options '--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator' \
|
||||||
|
--java-options "-Xss5m" \
|
||||||
|
--java-options "-Xmx256m" \
|
||||||
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
|
--java-options "-Djava.net.useSystemProxies=true" \
|
||||||
|
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\"" \
|
||||||
|
--java-options "-Dcryptomator.appVersion=\"${pkgver//_/-}\"" \
|
||||||
|
--java-options "-Dcryptomator.buildNumber=\"aur-${pkgrel}\"" \
|
||||||
|
--java-options "-Dcryptomator.disableUpdateCheck=true" \
|
||||||
|
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
|
||||||
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/.config/Cryptomator/ipc.socket\"" \
|
||||||
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/.local/share/Cryptomator/logs\"" \
|
||||||
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\"" \
|
||||||
|
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
|
||||||
|
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/.local/share/Cryptomator/plugins\"" \
|
||||||
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/.config/Cryptomator/key.p12\"" \
|
||||||
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\"" \
|
||||||
|
--java-options "-Dcryptomator.showTrayIcon=true" \
|
||||||
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
|
||||||
|
--app-version "${pkgver//_*/}" \
|
||||||
|
--verbose
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/application-vnd.cryptomator.vault.xml" "${pkgdir}/usr/share/mime/packages/cryptomator-vault.xml"
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.desktop" "${pkgdir}/usr/share/applications/org.cryptomator.Cryptomator.desktop"
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator256.png" "${pkgdir}/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png"
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator512.png" "${pkgdir}/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png"
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg"
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.tray.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray.svg"
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.tray-unlocked.svg"
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.tray.svg" "${pkgdir}/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg"
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg" "${pkgdir}/usr/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg"
|
||||||
|
|
||||||
|
mkdir -p "${pkgdir}/opt/cryptomator/"
|
||||||
|
cp -R "${srcdir}/${_src_app_dir}/target/cryptomator" "${pkgdir}/opt/"
|
||||||
|
install -Dm644 "${srcdir}/${_src_app_dir}/target/LICENSE.txt" -t "${pkgdir}/usr/share/licenses/${pkgname}"
|
||||||
|
|
||||||
|
mkdir -p "${pkgdir}/usr/bin"
|
||||||
|
ln -s "/opt/cryptomator/bin/cryptomator" "${pkgdir}/usr/bin/cryptomator"
|
||||||
|
}
|
||||||
@@ -1,2 +1 @@
|
|||||||
embedded.provisionprofile
|
embedded.provisionprofile
|
||||||
xcuserdata/
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// CryptomatorDockTilePlugin.swift
|
|
||||||
// Integrations
|
|
||||||
//
|
|
||||||
// Created by Tobias Hagemann on 22.09.25.
|
|
||||||
// Copyright © 2025 Cryptomator. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
|
|
||||||
class CryptomatorDockTilePlugin: NSObject, NSDockTilePlugIn {
|
|
||||||
func setDockTile(_ dockTile: NSDockTile?) {
|
|
||||||
guard let dockTile = dockTile, let image = Bundle(for: Self.self).image(forResource: "Cryptomator") else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dockTile.contentView = NSImageView(image: image)
|
|
||||||
dockTile.display()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,314 +0,0 @@
|
|||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 77;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */; };
|
|
||||||
74E08DED2E858532007E665C /* Cryptomator.icns in Resources */ = {isa = PBXBuildFile; fileRef = 74E08DEC2E858532007E665C /* Cryptomator.icns */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
74E08DD92E858467007E665C /* Cryptomator.docktileplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Cryptomator.docktileplugin; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptomatorDockTilePlugin.swift; sourceTree = "<group>"; };
|
|
||||||
74E08DEC2E858532007E665C /* Cryptomator.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Cryptomator.icns; path = ../resources/Cryptomator.icns; sourceTree = SOURCE_ROOT; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
74E08DD62E858467007E665C /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
74E08DD02E858467007E665C = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
74E08DE02E85847E007E665C /* CryptomatorDockTilePlugin.swift */,
|
|
||||||
74E08DEC2E858532007E665C /* Cryptomator.icns */,
|
|
||||||
74E08DDA2E858467007E665C /* Products */,
|
|
||||||
);
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
74E08DDA2E858467007E665C /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
74E08DD92E858467007E665C /* Cryptomator.docktileplugin */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
74E08DD82E858467007E665C /* DockTilePlugin */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = 74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */;
|
|
||||||
buildPhases = (
|
|
||||||
74E08DD52E858467007E665C /* Sources */,
|
|
||||||
74E08DD62E858467007E665C /* Frameworks */,
|
|
||||||
74E08DD72E858467007E665C /* Resources */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = DockTilePlugin;
|
|
||||||
packageProductDependencies = (
|
|
||||||
);
|
|
||||||
productName = DockTilePlugin;
|
|
||||||
productReference = 74E08DD92E858467007E665C /* Cryptomator.docktileplugin */;
|
|
||||||
productType = "com.apple.product-type.bundle";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
74E08DD12E858467007E665C /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
attributes = {
|
|
||||||
BuildIndependentTargetsInParallel = 1;
|
|
||||||
LastUpgradeCheck = 2600;
|
|
||||||
ORGANIZATIONNAME = Cryptomator;
|
|
||||||
TargetAttributes = {
|
|
||||||
74E08DD82E858467007E665C = {
|
|
||||||
CreatedOnToolsVersion = 26.0.1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
buildConfigurationList = 74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */;
|
|
||||||
developmentRegion = en;
|
|
||||||
hasScannedForEncodings = 0;
|
|
||||||
knownRegions = (
|
|
||||||
en,
|
|
||||||
Base,
|
|
||||||
);
|
|
||||||
mainGroup = 74E08DD02E858467007E665C;
|
|
||||||
minimizedProjectReferenceProxies = 1;
|
|
||||||
preferredProjectObjectVersion = 77;
|
|
||||||
productRefGroup = 74E08DDA2E858467007E665C /* Products */;
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
74E08DD82E858467007E665C /* DockTilePlugin */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
|
||||||
74E08DD72E858467007E665C /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
74E08DED2E858532007E665C /* Cryptomator.icns in Resources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXResourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
74E08DD52E858467007E665C /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
74E08DE12E8584DE007E665C /* CryptomatorDockTilePlugin.swift in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
74E08DDB2E858467007E665C /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
|
||||||
DEVELOPMENT_TEAM = YZQJQUHA3L;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
ENABLE_TESTABILITY = YES;
|
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
||||||
"DEBUG=1",
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.5;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
74E08DDC2E858467007E665C /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
||||||
DEVELOPMENT_TEAM = YZQJQUHA3L;
|
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.5;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
74E08DDE2E858467007E665C /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
DEVELOPMENT_TEAM = "";
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved.";
|
|
||||||
INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin;
|
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
|
||||||
MARKETING_VERSION = 1.0;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin;
|
|
||||||
PRODUCT_NAME = Cryptomator;
|
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
SKIP_INSTALL = YES;
|
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
||||||
WRAPPER_EXTENSION = docktileplugin;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
74E08DDF2E858467007E665C /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
|
||||||
DEVELOPMENT_TEAM = "";
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Cryptomator. All rights reserved.";
|
|
||||||
INFOPLIST_KEY_NSPrincipalClass = CryptomatorDockTilePlugin;
|
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
|
|
||||||
MARKETING_VERSION = 1.0;
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.cryptomator.DockTilePlugin;
|
|
||||||
PRODUCT_NAME = Cryptomator;
|
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
SKIP_INSTALL = YES;
|
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
|
||||||
WRAPPER_EXTENSION = docktileplugin;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
74E08DD42E858467007E665C /* Build configuration list for PBXProject "DockTilePlugin" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
74E08DDB2E858467007E665C /* Debug */,
|
|
||||||
74E08DDC2E858467007E665C /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
74E08DDD2E858467007E665C /* Build configuration list for PBXNativeTarget "DockTilePlugin" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
74E08DDE2E858467007E665C /* Debug */,
|
|
||||||
74E08DDF2E858467007E665C /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = 74E08DD12E858467007E665C /* Project object */;
|
|
||||||
}
|
|
||||||
Generated
Vendored
-7
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "self:">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
||||||
Vendored
-67
@@ -1,67 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "2600"
|
|
||||||
version = "1.7">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES"
|
|
||||||
buildArchitectures = "Automatic">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "74E08DD82E858467007E665C"
|
|
||||||
BuildableName = "Cryptomator.docktileplugin"
|
|
||||||
BlueprintName = "DockTilePlugin"
|
|
||||||
ReferencedContainer = "container:DockTilePlugin.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
shouldAutocreateTestPlan = "YES">
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "74E08DD82E858467007E665C"
|
|
||||||
BuildableName = "Cryptomator.docktileplugin"
|
|
||||||
BlueprintName = "DockTilePlugin"
|
|
||||||
ReferencedContainer = "container:DockTilePlugin.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
Vendored
+3
-14
@@ -24,7 +24,7 @@ rm -rf runtime dmg *.app *.dmg
|
|||||||
# set variables
|
# set variables
|
||||||
APP_NAME="Cryptomator"
|
APP_NAME="Cryptomator"
|
||||||
VENDOR="Skymatic GmbH"
|
VENDOR="Skymatic GmbH"
|
||||||
COPYRIGHT_YEARS="2016 - 2025"
|
COPYRIGHT_YEARS="2016 - 2026"
|
||||||
PACKAGE_IDENTIFIER="org.cryptomator"
|
PACKAGE_IDENTIFIER="org.cryptomator"
|
||||||
MAIN_JAR_GLOB="cryptomator-*.jar"
|
MAIN_JAR_GLOB="cryptomator-*.jar"
|
||||||
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||||
@@ -125,28 +125,17 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--java-options "-Dcryptomator.showTrayIcon=true" \
|
--java-options "-Dcryptomator.showTrayIcon=true" \
|
||||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
|
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
|
||||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
|
||||||
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
|
||||||
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
|
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
|
||||||
--resource-dir ../resources
|
--resource-dir ../resources
|
||||||
|
|
||||||
# transform app dir
|
# transform app dir
|
||||||
cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/
|
cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/
|
||||||
|
cp ../resources/Assets.car ${APP_NAME}.app/Contents/Resources/
|
||||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
||||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
||||||
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
|
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
|
||||||
|
|
||||||
# build and install dock tile plugin
|
|
||||||
echo "Building and installing Cryptomator.docktileplugin..."
|
|
||||||
DERIVED_DATA_PATH=../DockTilePlugin/build
|
|
||||||
xcodebuild -project ../DockTilePlugin/DockTilePlugin.xcodeproj \
|
|
||||||
-scheme DockTilePlugin \
|
|
||||||
-configuration Release \
|
|
||||||
-derivedDataPath ${DERIVED_DATA_PATH} \
|
|
||||||
-quiet \
|
|
||||||
clean build
|
|
||||||
mkdir -p ${APP_NAME}.app/Contents/PlugIns
|
|
||||||
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin ${APP_NAME}.app/Contents/PlugIns/
|
|
||||||
rm -rf ${DERIVED_DATA_PATH}
|
|
||||||
|
|
||||||
# generate license
|
# generate license
|
||||||
mvn -B -f../../../pom.xml license:add-third-party \
|
mvn -B -f../../../pom.xml license:add-third-party \
|
||||||
-Dlicense.thirdPartyFilename=license.rtf \
|
-Dlicense.thirdPartyFilename=license.rtf \
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
Vendored
+2
-3
@@ -12,6 +12,8 @@
|
|||||||
<string>Cryptomator</string>
|
<string>Cryptomator</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>Cryptomator.icns</string>
|
<string>Cryptomator.icns</string>
|
||||||
|
<key>CFBundleIconName</key>
|
||||||
|
<string>Cryptomator</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>org.cryptomator</string>
|
<string>org.cryptomator</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
@@ -117,8 +119,5 @@
|
|||||||
<!-- allow utilization of integrated GPU, see https://developer.apple.com/library/mac/qa/qa1734/_index.html -->
|
<!-- allow utilization of integrated GPU, see https://developer.apple.com/library/mac/qa/qa1734/_index.html -->
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||||
<true/>
|
<true/>
|
||||||
<!-- register dock tile plugin -->
|
|
||||||
<key>NSDockTilePlugIn</key>
|
|
||||||
<string>Cryptomator.docktileplugin</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
Vendored
+89
-64
@@ -16,6 +16,19 @@ Param(
|
|||||||
# Function Definitions Section
|
# Function Definitions Section
|
||||||
# ============================
|
# ============================
|
||||||
|
|
||||||
|
function Invoke-CommandWithExitCheck {
|
||||||
|
param (
|
||||||
|
[string]$Command,
|
||||||
|
[string[]]$Arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
& $Command @Arguments
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Command '$Command' failed with exit code $LASTEXITCODE"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Main {
|
function Main {
|
||||||
|
|
||||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
@@ -65,7 +78,8 @@ Write-Host "`$Env:JAVA_HOME=$Env:JAVA_HOME"
|
|||||||
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
|
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
|
||||||
|
|
||||||
# compile
|
# compile
|
||||||
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
|
Invoke-CommandWithExitCheck -Command `
|
||||||
|
"mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "clean", "package", "-DskipTests", "-Pwin")
|
||||||
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
|
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
|
||||||
|
|
||||||
# add runtime
|
# add runtime
|
||||||
@@ -129,16 +143,18 @@ if ((& "$Env:JAVA_HOME\bin\jlink" --help | Select-String -Pattern "Linking from
|
|||||||
}
|
}
|
||||||
|
|
||||||
### create runtime
|
### create runtime
|
||||||
& "$Env:JAVA_HOME\bin\jlink" `
|
Invoke-CommandWithExitCheck -Command `
|
||||||
--verbose `
|
"$Env:JAVA_HOME\bin\jlink" -Arguments @(
|
||||||
--output runtime `
|
"--verbose",
|
||||||
--module-path $jmodPaths `
|
"--output", "runtime",
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
|
"--module-path", $jmodPaths,
|
||||||
--strip-native-commands `
|
"--add-modules", "java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml",
|
||||||
--no-header-files `
|
"--strip-native-commands",
|
||||||
--no-man-pages `
|
"--no-header-files",
|
||||||
--strip-debug `
|
"--no-man-pages",
|
||||||
--compress "zip-0" #do not compress and use msi compression
|
"--strip-debug",
|
||||||
|
"--compress", "zip-0" #do not compress and use msi compression
|
||||||
|
)
|
||||||
|
|
||||||
$appPath = ".\$AppName"
|
$appPath = ".\$AppName"
|
||||||
if ($clean -and (Test-Path -Path $appPath)) {
|
if ($clean -and (Test-Path -Path $appPath)) {
|
||||||
@@ -167,6 +183,7 @@ $javaOptions = @(
|
|||||||
"--java-options", "-Dcryptomator.showTrayIcon=true"
|
"--java-options", "-Dcryptomator.showTrayIcon=true"
|
||||||
"--java-options", "-Dcryptomator.buildNumber=`"msi-$revisionNo`""
|
"--java-options", "-Dcryptomator.buildNumber=`"msi-$revisionNo`""
|
||||||
"--java-options", "-Dcryptomator.disableUpdateCheck=false"
|
"--java-options", "-Dcryptomator.disableUpdateCheck=false"
|
||||||
|
"--java-options", "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -194,14 +211,15 @@ if ($LASTEXITCODE -ne 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#Create RTF license for msi
|
#Create RTF license for msi
|
||||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
Invoke-CommandWithExitCheck -Command `
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
"mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "license:add-third-party", `
|
||||||
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
|
"-Dlicense.thirdPartyFilename=license.rtf", `
|
||||||
"-Dlicense.outputDirectory=$buildDir\resources\" `
|
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl", `
|
||||||
"-Dlicense.includedScopes=compile" `
|
"-Dlicense.outputDirectory=$buildDir\resources\", `
|
||||||
"-Dlicense.excludedGroups=^org\.cryptomator" `
|
"-Dlicense.includedScopes=compile", `
|
||||||
"-Dlicense.failOnMissing=true" `
|
"-Dlicense.excludedGroups=^org\.cryptomator", `
|
||||||
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
"-Dlicense.failOnMissing=true", `
|
||||||
|
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges")
|
||||||
|
|
||||||
# patch app dir
|
# patch app dir
|
||||||
Copy-Item "contrib\*" -Destination "$AppName"
|
Copy-Item "contrib\*" -Destination "$AppName"
|
||||||
@@ -209,42 +227,46 @@ attrib -r "$AppName\$AppName.exe"
|
|||||||
attrib -r "$AppName\${AppName} (Debug).exe"
|
attrib -r "$AppName\${AppName} (Debug).exe"
|
||||||
|
|
||||||
# create .msi
|
# create .msi
|
||||||
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
|
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources\"
|
||||||
$Env:JP_WIXHELPER_DIR = "."
|
$Env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT = "${Env:JP_WIXWIZARD_RESOURCES}".Replace('\', '\\');
|
||||||
& "$Env:JAVA_HOME\bin\jpackage" `
|
$Env:JP_WIXHELPER_DIR = ""
|
||||||
--verbose `
|
|
||||||
--type msi `
|
|
||||||
--win-upgrade-uuid $UpgradeUUID `
|
|
||||||
--app-image $AppName `
|
|
||||||
--dest installer `
|
|
||||||
--name $AppName `
|
|
||||||
--vendor $Vendor `
|
|
||||||
--copyright $copyright `
|
|
||||||
--app-version "$semVerNo.$revisionNo" `
|
|
||||||
--win-menu `
|
|
||||||
--win-dir-chooser `
|
|
||||||
--win-shortcut-prompt `
|
|
||||||
--win-menu-group $AppName `
|
|
||||||
--resource-dir resources `
|
|
||||||
--license-file resources/license.rtf `
|
|
||||||
--win-update-url $UpdateUrl `
|
|
||||||
--about-url $AboutUrl `
|
|
||||||
--file-associations resources/FAvaultFile.properties
|
|
||||||
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
Get-Content .\resources\FAvaultFile.template.properties ` # Similar to envsubst
|
||||||
Write-Error "jpackage MSI failed with exit code $LASTEXITCODE"
|
| ForEach-Object { $ExecutionContext.InvokeCommand.ExpandString($_) } `
|
||||||
return 1;
|
| Out-File -FilePath .\resources\FAvaultFile.properties
|
||||||
}
|
|
||||||
|
Invoke-CommandWithExitCheck -Command `
|
||||||
|
"$Env:JAVA_HOME\bin\jpackage" -Arguments @(
|
||||||
|
"--verbose",
|
||||||
|
"--type", "msi",
|
||||||
|
"--win-upgrade-uuid", $UpgradeUUID,
|
||||||
|
"--app-image", $AppName,
|
||||||
|
"--dest", "installer",
|
||||||
|
"--name", $AppName,
|
||||||
|
"--vendor", $Vendor,
|
||||||
|
"--copyright", $copyright,
|
||||||
|
"--app-version", "$semVerNo.$revisionNo",
|
||||||
|
"--win-menu",
|
||||||
|
"--win-dir-chooser",
|
||||||
|
"--win-shortcut-prompt",
|
||||||
|
"--win-menu-group", $AppName,
|
||||||
|
"--resource-dir", "resources",
|
||||||
|
"--license-file", "resources/license.rtf",
|
||||||
|
"--win-update-url", $UpdateUrl,
|
||||||
|
"--about-url", $AboutUrl,
|
||||||
|
"--file-associations", "resources/FAvaultFile.properties"
|
||||||
|
)
|
||||||
|
|
||||||
#Create RTF license for bundle
|
#Create RTF license for bundle
|
||||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
Invoke-CommandWithExitCheck -Command `
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
"mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "license:add-third-party", `
|
||||||
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
|
"-Dlicense.thirdPartyFilename=license.rtf", `
|
||||||
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
|
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl", `
|
||||||
"-Dlicense.includedScopes=compile" `
|
"-Dlicense.outputDirectory=$buildDir\bundle\resources\", `
|
||||||
"-Dlicense.excludedGroups=^org\.cryptomator" `
|
"-Dlicense.includedScopes=compile", `
|
||||||
"-Dlicense.failOnMissing=true" `
|
"-Dlicense.excludedGroups=^org\.cryptomator", `
|
||||||
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
"-Dlicense.failOnMissing=true", `
|
||||||
|
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges")
|
||||||
|
|
||||||
# download Winfsp
|
# download Winfsp
|
||||||
$winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
$winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
||||||
@@ -270,18 +292,21 @@ Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninsta
|
|||||||
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi" -Force
|
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi" -Force
|
||||||
|
|
||||||
# create bundle including winfsp
|
# create bundle including winfsp
|
||||||
& wix build `
|
Invoke-CommandWithExitCheck -Command `
|
||||||
-define BundleName="$AppName" `
|
"wix" -Arguments @(
|
||||||
-define BundleVersion="$semVerNo.$revisionNo" `
|
"build",
|
||||||
-define BundleVendor="$Vendor" `
|
"-define", "BundleName=$AppName",
|
||||||
-define BundleCopyright="$copyright" `
|
"-define", "BundleVersion=$semVerNo.$revisionNo",
|
||||||
-define AboutUrl="$AboutUrl" `
|
"-define", "BundleVendor=$Vendor",
|
||||||
-define HelpUrl="$HelpUrl" `
|
"-define", "BundleCopyright=$copyright",
|
||||||
-define UpdateUrl="$UpdateUrl" `
|
"-define", "AboutUrl=$AboutUrl",
|
||||||
-ext "WixToolset.Util.wixext" `
|
"-define", "HelpUrl=$HelpUrl",
|
||||||
-ext "WixToolset.BootstrapperApplications.wixext" `
|
"-define", "UpdateUrl=$UpdateUrl",
|
||||||
.\bundle\bundleWithWinfsp.wxs `
|
"-ext", "WixToolset.Util.wixext",
|
||||||
-out "installer\$AppName-Installer.exe"
|
"-ext", "WixToolset.BootstrapperApplications.wixext",
|
||||||
|
".\bundle\bundleWithWinfsp.wxs",
|
||||||
|
"-out", ".\installer\$AppName-Installer.exe"
|
||||||
|
)
|
||||||
|
|
||||||
Write-Host "Created EXE installer .\installer\$AppName-Installer.exe"
|
Write-Host "Created EXE installer .\installer\$AppName-Installer.exe"
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
mime-type=application/vnd.cryptomator.vault
|
mime-type=application/vnd.cryptomator.vault
|
||||||
extension=cryptomator
|
extension=cryptomator
|
||||||
description=Cryptomator Vault File
|
description=Cryptomator Vault File
|
||||||
icon=resources/Cryptomator-Vault.ico
|
icon=C:\\Users\\Arbeit\\Skymatic\\cryptomator-jdk26-jpackage\\dist\\win\\resources\\Cryptomator-Vault.ico
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
mime-type=application/vnd.cryptomator.vault
|
||||||
|
extension=cryptomator
|
||||||
|
description=Cryptomator Vault File
|
||||||
|
icon=${env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT}Cryptomator-Vault.ico
|
||||||
Vendored
+4
-4
@@ -68,7 +68,7 @@
|
|||||||
<?endif?>
|
<?endif?>
|
||||||
|
|
||||||
<!-- TODO: how does this work again? -->
|
<!-- TODO: how does this work again? -->
|
||||||
<ns0:Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)\wixhelper.dll" />
|
<ns0:Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)msica.dll"></ns0:Binary>
|
||||||
<ns0:CustomAction Id="JpFindRelatedProducts" BinaryRef="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
<ns0:CustomAction Id="JpFindRelatedProducts" BinaryRef="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
||||||
|
|
||||||
<?ifndef SkipCryptomatorLegacyCheck ?>
|
<?ifndef SkipCryptomatorLegacyCheck ?>
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
</ns0:CreateFolder>
|
</ns0:CreateFolder>
|
||||||
</ns0:Component>
|
</ns0:Component>
|
||||||
<ns0:Component Id="AdminConfigFile" NeverOverwrite="yes" Permanent="yes">
|
<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="SYSTEM" GenericAll="yes"/>
|
||||||
<util:PermissionEx User="Administrators" GenericAll="yes"/>
|
<util:PermissionEx User="Administrators" GenericAll="yes"/>
|
||||||
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
|
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
<ns0:Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
<ns0:Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
||||||
</ns0:InstallUISequence>
|
</ns0:InstallUISequence>
|
||||||
|
|
||||||
<ns0:WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" />
|
<ns0:WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)banner.bmp" />
|
||||||
<ns0:WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
|
<ns0:WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)background.bmp" />
|
||||||
</ns0:Package>
|
</ns0:Package>
|
||||||
</ns0:Wix>
|
</ns0:Wix>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptomator</artifactId>
|
<artifactId>cryptomator</artifactId>
|
||||||
<version>1.19.0</version>
|
<version>1.20.0-SNAPSHOT</version>
|
||||||
<name>Cryptomator Desktop App</name>
|
<name>Cryptomator Desktop App</name>
|
||||||
|
|
||||||
<organization>
|
<organization>
|
||||||
@@ -33,45 +33,46 @@
|
|||||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
||||||
|
|
||||||
<!-- cryptomator dependencies -->
|
<!-- 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.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.win.version>1.6.0</cryptomator.integrations.win.version>
|
||||||
<cryptomator.integrations.mac.version>1.5.0-beta3</cryptomator.integrations.mac.version>
|
<cryptomator.integrations.mac.version>1.5.0</cryptomator.integrations.mac.version>
|
||||||
<cryptomator.integrations.linux.version>1.7.0-beta4</cryptomator.integrations.linux.version>
|
<cryptomator.integrations.linux.version>1.7.0</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.fuse.version>6.0.0</cryptomator.fuse.version>
|
<cryptomator.fuse.version>6.0.1</cryptomator.fuse.version>
|
||||||
<cryptomator.webdav.version>3.0.1</cryptomator.webdav.version>
|
<cryptomator.webdav.version>3.0.1</cryptomator.webdav.version>
|
||||||
|
<cryptomator.webdav-servlet.version>1.2.12</cryptomator.webdav-servlet.version>
|
||||||
|
|
||||||
<!-- 3rd party dependencies -->
|
<!-- 3rd party dependencies -->
|
||||||
<caffeine.version>3.2.3</caffeine.version>
|
<caffeine.version>3.2.3</caffeine.version>
|
||||||
<commons-lang3.version>3.20.0</commons-lang3.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>
|
<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>
|
<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>
|
<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>
|
<slf4j.version>2.0.17</slf4j.version>
|
||||||
<tinyoauth2.version>0.8.1</tinyoauth2.version>
|
<tinyoauth2.version>0.8.1</tinyoauth2.version>
|
||||||
<zxcvbn.version>1.9.0</zxcvbn.version>
|
<zxcvbn.version>1.9.0</zxcvbn.version>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<junit.jupiter.version>5.13.4</junit.jupiter.version>
|
<junit.jupiter.version>6.0.3</junit.jupiter.version>
|
||||||
<mockito.version>5.20.0</mockito.version>
|
<mockito.version>5.22.0</mockito.version>
|
||||||
<hamcrest.version>3.0</hamcrest.version>
|
<hamcrest.version>3.0</hamcrest.version>
|
||||||
|
|
||||||
<!-- build-time dependencies -->
|
<!-- build-time dependencies -->
|
||||||
<jetbrains.annotations.version>26.0.2-1</jetbrains.annotations.version>
|
<jetbrains.annotations.version>26.1.0</jetbrains.annotations.version>
|
||||||
<dependency-check.version>12.1.5</dependency-check.version>
|
<dependency-check.version>12.2.0</dependency-check.version>
|
||||||
<jacoco.version>0.8.14</jacoco.version>
|
<jacoco.version>0.8.14</jacoco.version>
|
||||||
<license-generator.version>2.7.0</license-generator.version>
|
<license-generator.version>2.7.1</license-generator.version>
|
||||||
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
|
<junit-tree-reporter.version>1.5.1</junit-tree-reporter.version>
|
||||||
<mvn-compiler.version>3.14.1</mvn-compiler.version>
|
<mvn-compiler.version>3.15.0</mvn-compiler.version>
|
||||||
<mvn-resources.version>3.3.1</mvn-resources.version>
|
<mvn-resources.version>3.5.0</mvn-resources.version>
|
||||||
<mvn-dependency.version>3.8.1</mvn-dependency.version>
|
<mvn-dependency.version>3.10.0</mvn-dependency.version>
|
||||||
<mvn-surefire.version>3.5.4</mvn-surefire.version>
|
<mvn-surefire.version>3.5.3</mvn-surefire.version>
|
||||||
<mvn-jar.version>3.4.2</mvn-jar.version>
|
<mvn-jar.version>3.5.0</mvn-jar.version>
|
||||||
|
|
||||||
<!-- Property used by surefire to determine jacoco engine -->
|
<!-- Property used by surefire to determine jacoco engine -->
|
||||||
<surefire.jacoco.args></surefire.jacoco.args>
|
<surefire.jacoco.args></surefire.jacoco.args>
|
||||||
@@ -93,6 +94,11 @@
|
|||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Cryptomator Libs -->
|
<!-- Cryptomator Libs -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>webdav-nio-adapter-servlet</artifactId>
|
||||||
|
<version>${cryptomator.webdav-servlet.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptolib</artifactId>
|
<artifactId>cryptolib</artifactId>
|
||||||
@@ -521,11 +527,57 @@
|
|||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
<profile>
|
<profile>
|
||||||
<id>linux</id>
|
<id>linux-aarch64</id>
|
||||||
<activation>
|
<activation>
|
||||||
<os>
|
<os>
|
||||||
<family>unix</family>
|
<family>unix</family>
|
||||||
<name>Linux</name>
|
<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>
|
</os>
|
||||||
<property>
|
<property>
|
||||||
<name>idea.version</name>
|
<name>idea.version</name>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
|
|||||||
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
||||||
|
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||||
@@ -20,6 +21,7 @@ import org.cryptomator.networking.SSLContextProvider;
|
|||||||
import org.cryptomator.networking.SSLContextWithMacKeychain;
|
import org.cryptomator.networking.SSLContextWithMacKeychain;
|
||||||
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
|
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
|
||||||
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
||||||
|
import org.cryptomator.ui.fxapp.JfxRevealPathService;
|
||||||
import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider;
|
import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider;
|
||||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||||
|
|
||||||
@@ -64,6 +66,7 @@ open module org.cryptomator.desktop {
|
|||||||
uses org.cryptomator.event.NotificationHandler;
|
uses org.cryptomator.event.NotificationHandler;
|
||||||
|
|
||||||
provides UiAppearanceProvider with JfxUiAppearanceProvider;
|
provides UiAppearanceProvider with JfxUiAppearanceProvider;
|
||||||
|
provides RevealPathService with JfxRevealPathService;
|
||||||
provides TrayMenuController with AwtTrayMenuController;
|
provides TrayMenuController with AwtTrayMenuController;
|
||||||
provides Configurator with LogbackConfiguratorFactory;
|
provides Configurator with LogbackConfiguratorFactory;
|
||||||
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
|
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import javax.inject.Named;
|
|||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.SynchronousQueue;
|
import java.util.concurrent.SynchronousQueue;
|
||||||
@@ -76,8 +74,8 @@ public abstract class CommonsModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static Optional<RevealPathService> provideRevealPathService() {
|
static RevealPathService provideRevealPathService() {
|
||||||
return RevealPathService.get().findFirst();
|
return RevealPathService.get().findFirst().orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,13 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.Spliterator;
|
import java.util.Spliterator;
|
||||||
import java.util.Spliterators;
|
import java.util.Spliterators;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@@ -20,20 +23,22 @@ public class Environment {
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
|
||||||
private static final int DEFAULT_MIN_PW_LENGTH = 8;
|
private static final int DEFAULT_MIN_PW_LENGTH = 8;
|
||||||
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
public static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
||||||
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
public static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
||||||
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
public static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
||||||
private static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
|
public static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
|
||||||
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
public static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
||||||
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
public static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
||||||
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
public static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
||||||
private static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
|
public static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
|
||||||
private static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
|
public static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
|
||||||
private static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
|
public static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
|
||||||
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
|
public static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
|
||||||
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
|
public static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
|
||||||
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
|
public static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
|
||||||
private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
|
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() {}
|
private Environment() {}
|
||||||
|
|
||||||
@@ -57,6 +62,8 @@ public class Environment {
|
|||||||
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
|
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
|
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
|
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
|
||||||
|
logCryptomatorSystemProperty(HUB_ALLOWED_HOSTS_PROP_NAME);
|
||||||
|
logCryptomatorSystemProperty(HUB_TOFU_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Environment getInstance() {
|
public static Environment getInstance() {
|
||||||
@@ -145,6 +152,18 @@ public class Environment {
|
|||||||
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
|
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) {
|
private Optional<Path> getPath(String propertyName) {
|
||||||
String value = System.getProperty(propertyName);
|
String value = System.getProperty(propertyName);
|
||||||
return Optional.ofNullable(value).map(Paths::get);
|
return Optional.ofNullable(value).map(Paths::get);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.cryptomator.common;
|
package org.cryptomator.common;
|
||||||
|
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
import org.jetbrains.annotations.VisibleForTesting;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
@@ -13,10 +13,12 @@ public class SubstitutingProperties extends PropertiesDecorator {
|
|||||||
private static final Pattern TEMPLATE = Pattern.compile("@\\{(\\w+)}");
|
private static final Pattern TEMPLATE = Pattern.compile("@\\{(\\w+)}");
|
||||||
|
|
||||||
private final Map<String, String> env;
|
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);
|
super(props);
|
||||||
this.env = systemEnvironment;
|
this.env = systemEnvironment;
|
||||||
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -44,7 +46,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
|
|||||||
case "localappdata" -> resolveFrom("LOCALAPPDATA", Source.ENV);
|
case "localappdata" -> resolveFrom("LOCALAPPDATA", Source.ENV);
|
||||||
case "userhome" -> resolveFrom("user.home", Source.PROPS);
|
case "userhome" -> resolveFrom("user.home", Source.PROPS);
|
||||||
default -> {
|
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();
|
yield match.group();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -56,7 +58,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
|
|||||||
case PROPS -> delegate.getProperty(key);
|
case PROPS -> delegate.getProperty(key);
|
||||||
};
|
};
|
||||||
if (val == null) {
|
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 "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
return Matcher.quoteReplacement(val);
|
return Matcher.quoteReplacement(val);
|
||||||
|
|||||||
@@ -24,9 +24,12 @@ import javafx.beans.property.SimpleStringProperty;
|
|||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.collections.ObservableSet;
|
||||||
import javafx.geometry.NodeOrientation;
|
import javafx.geometry.NodeOrientation;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class Settings {
|
public class Settings {
|
||||||
|
|
||||||
@@ -78,6 +81,7 @@ public class Settings {
|
|||||||
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
|
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
|
||||||
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
|
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
|
||||||
public final StringProperty lastUpdateAttemptedByVersion;
|
public final StringProperty lastUpdateAttemptedByVersion;
|
||||||
|
public final ObservableSet<String> trustedHosts;
|
||||||
|
|
||||||
public static Settings create(SettingsProvider provider, Environment env) {
|
public static Settings create(SettingsProvider provider, Environment env) {
|
||||||
var defaults = new SettingsJson();
|
var defaults = new SettingsJson();
|
||||||
@@ -118,6 +122,7 @@ public class Settings {
|
|||||||
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
|
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
|
||||||
this.previouslyUsedVaultDirectory = new SimpleObjectProperty<>(this, "previouslyUsedVaultDirectory", json.previouslyUsedVaultDirectory);
|
this.previouslyUsedVaultDirectory = new SimpleObjectProperty<>(this, "previouslyUsedVaultDirectory", json.previouslyUsedVaultDirectory);
|
||||||
this.lastUpdateAttemptedByVersion = new SimpleStringProperty(this, "lastUpdateAttemptedByVersion", json.lastUpdateAttemptedByVersion);
|
this.lastUpdateAttemptedByVersion = new SimpleStringProperty(this, "lastUpdateAttemptedByVersion", json.lastUpdateAttemptedByVersion);
|
||||||
|
this.trustedHosts = FXCollections.observableSet(json.trustedHosts);
|
||||||
|
|
||||||
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
|
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
|
||||||
|
|
||||||
@@ -149,6 +154,7 @@ public class Settings {
|
|||||||
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
|
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
|
||||||
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
|
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
|
||||||
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
|
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
|
||||||
|
trustedHosts.addListener(this::somethingChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@@ -207,6 +213,7 @@ public class Settings {
|
|||||||
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
|
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
|
||||||
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
|
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
|
||||||
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
|
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
|
||||||
|
json.trustedHosts = Set.copyOf(trustedHosts);
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,23 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
|||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
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.nio.file.Path;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
class SettingsJson {
|
class SettingsJson {
|
||||||
|
|
||||||
@JsonProperty("directories")
|
@JsonProperty("directories")
|
||||||
List<VaultSettingsJson> directories = List.of();
|
@JsonSetter(nulls = Nulls.AS_EMPTY)
|
||||||
|
List<VaultSettingsJson> directories = new ArrayList<>();
|
||||||
|
|
||||||
@JsonProperty("writtenByVersion")
|
@JsonProperty("writtenByVersion")
|
||||||
String writtenByVersion;
|
String writtenByVersion;
|
||||||
@@ -99,4 +105,8 @@ class SettingsJson {
|
|||||||
|
|
||||||
@JsonProperty("lastUpdateAttemptedByVersion")
|
@JsonProperty("lastUpdateAttemptedByVersion")
|
||||||
String lastUpdateAttemptedByVersion;
|
String lastUpdateAttemptedByVersion;
|
||||||
|
|
||||||
|
@JsonProperty("trustedHosts")
|
||||||
|
@JsonSetter(nulls = Nulls.AS_EMPTY)
|
||||||
|
Set<String> trustedHosts = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import java.util.Set;
|
|||||||
* <li>cryptomator.p12Path</li>
|
* <li>cryptomator.p12Path</li>
|
||||||
* <li>cryptomator.mountPointsDir</li>
|
* <li>cryptomator.mountPointsDir</li>
|
||||||
* <li>cryptomator.disableUpdateCheck</li>
|
* <li>cryptomator.disableUpdateCheck</li>
|
||||||
|
* <li>cryptomator.hub.allowedHosts</li>
|
||||||
|
* <li>cryptomator.hub.enableTrustOnFirstUse</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @see Properties
|
* @see Properties
|
||||||
@@ -42,7 +44,9 @@ class AdminPropertiesFactory {
|
|||||||
"cryptomator.pluginDir", //
|
"cryptomator.pluginDir", //
|
||||||
"cryptomator.p12Path", //
|
"cryptomator.p12Path", //
|
||||||
"cryptomator.mountPointsDir", //
|
"cryptomator.mountPointsDir", //
|
||||||
"cryptomator.disableUpdateCheck");
|
"cryptomator.disableUpdateCheck", //
|
||||||
|
"cryptomator.hub.allowedHosts", //
|
||||||
|
"cryptomator.hub.enableTrustOnFirstUse");
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class Cryptomator {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
var adminProps = AdminPropertiesFactory.create();
|
var adminProps = AdminPropertiesFactory.create();
|
||||||
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv());
|
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv(), EventualLogger.INSTANCE);
|
||||||
System.setProperties(lazyProcessedProps);
|
System.setProperties(lazyProcessedProps);
|
||||||
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
||||||
LOG = LoggerFactory.getLogger(Cryptomator.class);
|
LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||||
@@ -65,7 +65,6 @@ public class Cryptomator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
EventualLogger.INSTANCE.drainTo(LOG);
|
|
||||||
var printVersion = Optional.ofNullable(args) //
|
var printVersion = Optional.ofNullable(args) //
|
||||||
.stream() //Streams either one element (the args-array) or zero elements
|
.stream() //Streams either one element (the args-array) or zero elements
|
||||||
.flatMap(Arrays::stream) //
|
.flatMap(Arrays::stream) //
|
||||||
@@ -91,10 +90,11 @@ public class Cryptomator {
|
|||||||
* @return Nonzero exit code in case of an error.
|
* @return Nonzero exit code in case of an error.
|
||||||
*/
|
*/
|
||||||
private int run(String[] args) {
|
private int run(String[] args) {
|
||||||
|
debugMode.initialize();
|
||||||
|
EventualLogger.INSTANCE.drainTo(LOG);
|
||||||
env.log();
|
env.log();
|
||||||
LOG.debug("Dagger graph initialized after {}ms", System.currentTimeMillis() - STARTUP_TIME);
|
LOG.debug("Dagger graph initialized after {}ms", System.currentTimeMillis() - STARTUP_TIME);
|
||||||
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
||||||
debugMode.initialize();
|
|
||||||
supportedLanguages.applyPreferred();
|
supportedLanguages.applyPreferred();
|
||||||
changeDefaultSSLContext();
|
changeDefaultSSLContext();
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
|||||||
public SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException {
|
public SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException {
|
||||||
try {
|
try {
|
||||||
KeyStore truststore = getTruststore();
|
KeyStore truststore = getTruststore();
|
||||||
truststore.load(null, null);
|
ensureLoaded(truststore);
|
||||||
|
|
||||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
tmf.init(truststore);
|
tmf.init(truststore);
|
||||||
@@ -30,4 +30,13 @@ abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
|||||||
throw new SSLContextBuildException(e);
|
throw new SSLContextBuildException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ensureLoaded(KeyStore truststore) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
|
||||||
|
try {
|
||||||
|
truststore.aliases();
|
||||||
|
} catch (KeyStoreException e) {
|
||||||
|
// Not initialized yet (e.g. custom KeyStore SPI); initialize without replacing preloaded stores.
|
||||||
|
truststore.load(null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,73 @@
|
|||||||
package org.cryptomator.networking;
|
package org.cryptomator.networking;
|
||||||
|
|
||||||
|
import org.cryptomator.common.Nullable;
|
||||||
import org.cryptomator.integrations.common.OperatingSystem;
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
import org.jetbrains.annotations.VisibleForTesting;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SSLContextProvider for Windows using the Windows certificate store as trust store
|
* SSLContextProvider for Windows using the Windows certificate store as trust store and the bundled JDK cacerts as fallback
|
||||||
* <p>
|
* <p>
|
||||||
* In order to work, the jdk.crypto.mscapi jmod is needed
|
* In order to work, the jdk.crypto.mscapi jmod is needed
|
||||||
*/
|
*/
|
||||||
@OperatingSystem(OperatingSystem.Value.WINDOWS)
|
@OperatingSystem(OperatingSystem.Value.WINDOWS)
|
||||||
public class SSLContextWithWindowsCertStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
public class SSLContextWithWindowsCertStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(SSLContextWithWindowsCertStore.class);
|
||||||
|
private static final String DEFAULT_TRUSTSTORE_PASSWORD = "changeit"; //default JDK cacerts password
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
KeyStore getTruststore() throws KeyStoreException {
|
KeyStore getTruststore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
|
||||||
return KeyStore.getInstance("WINDOWS-ROOT");
|
var windowsKeyStore = KeyStore.getInstance("WINDOWS-ROOT");
|
||||||
|
var jdkKeyStore = getShippedCaCertsStore();
|
||||||
|
if (jdkKeyStore == null) {
|
||||||
|
return windowsKeyStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureLoaded(windowsKeyStore);
|
||||||
|
ensureLoaded(jdkKeyStore);
|
||||||
|
try {
|
||||||
|
CombinedKeyStoreSpi spi = CombinedKeyStoreSpi.create(windowsKeyStore, jdkKeyStore);
|
||||||
|
Provider dummyProvider = new Provider("CombinedKeyStoreProvider", "1.0", "Provides a combined, read-only KeyStore") {};
|
||||||
|
return new KeyStore(spi, dummyProvider, "CombinedKeyStoreProvider") {};
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new KeyStoreException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
KeyStore getShippedCaCertsStore() {
|
||||||
|
return getCaCertsStoreByProperties(System.getProperties());
|
||||||
|
}
|
||||||
|
|
||||||
|
//for testability
|
||||||
|
@VisibleForTesting
|
||||||
|
@Nullable
|
||||||
|
KeyStore getCaCertsStoreByProperties(Properties props) {
|
||||||
|
var javaHome = Path.of(props.getProperty("java.home"));
|
||||||
|
var trustStorePassword = props.getProperty("javax.net.ssl.trustStorePassword", DEFAULT_TRUSTSTORE_PASSWORD).toCharArray();
|
||||||
|
for (var candidate : List.of(javaHome.resolve("lib/security/cacerts"), javaHome.resolve("conf/security/cacerts"))) {
|
||||||
|
try {
|
||||||
|
if (Files.isRegularFile(candidate)) {
|
||||||
|
return KeyStore.getInstance(candidate.toFile(), trustStorePassword);
|
||||||
|
}
|
||||||
|
} catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException e) {
|
||||||
|
LOG.info("Unable to load fallback cacerts {} file. Skipping fallback.", candidate, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public enum FxmlFile {
|
|||||||
HEALTH_START("/fxml/health_start.fxml"), //
|
HEALTH_START("/fxml/health_start.fxml"), //
|
||||||
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
||||||
HUB_NO_KEYCHAIN("/fxml/hub_no_keychain.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_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
|
||||||
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
|
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
|
||||||
HUB_RECEIVE_KEY("/fxml/hub_receive_key.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_FAILED("/fxml/hub_register_failed.fxml"), //
|
||||||
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
||||||
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_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"), //
|
HUB_REQUIRE_ACCOUNT_INIT("/fxml/hub_require_account_init.fxml"), //
|
||||||
LOCK_FORCED("/fxml/lock_forced.fxml"), //
|
LOCK_FORCED("/fxml/lock_forced.fxml"), //
|
||||||
LOCK_FAILED("/fxml/lock_failed.fxml"), //
|
LOCK_FAILED("/fxml/lock_failed.fxml"), //
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
package org.cryptomator.ui.common;
|
package org.cryptomator.ui.common;
|
||||||
|
|
||||||
import dagger.Lazy;
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
import org.cryptomator.integrations.mount.Mountpoint;
|
import org.cryptomator.integrations.mount.Mountpoint;
|
||||||
import org.cryptomator.integrations.mount.UnmountFailedException;
|
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.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
|
||||||
import javafx.application.HostServices;
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -28,12 +27,12 @@ public class VaultService {
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(VaultService.class);
|
private static final Logger LOG = LoggerFactory.getLogger(VaultService.class);
|
||||||
|
|
||||||
private final Lazy<Application> application;
|
private final RevealPathService revealPathService;
|
||||||
private final ExecutorService executorService;
|
private final ExecutorService executorService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public VaultService(Lazy<Application> application, ExecutorService executorService) {
|
public VaultService(RevealPathService revealPathService, ExecutorService executorService) {
|
||||||
this.application = application;
|
this.revealPathService = revealPathService;
|
||||||
this.executorService = executorService;
|
this.executorService = executorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,9 +46,9 @@ public class VaultService {
|
|||||||
* @param vault The vault to reveal
|
* @param vault The vault to reveal
|
||||||
*/
|
*/
|
||||||
public Task<Vault> createRevealTask(Vault vault) {
|
public Task<Vault> createRevealTask(Vault vault) {
|
||||||
Task<Vault> task = new RevealVaultTask(vault, application.get().getHostServices());
|
Task<Vault> task = new RevealVaultTask(vault, revealPathService);
|
||||||
task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayName()));
|
task.setOnSucceeded(_ -> LOG.info("Revealed {}", vault.getDisplayName()));
|
||||||
task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), evt.getSource().getException()));
|
task.setOnFailed(evt -> LOG.warn("Failed to reveal {}", vault.getDisplayName(), evt.getSource().getException()));
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,19 +109,18 @@ public class VaultService {
|
|||||||
private static class RevealVaultTask extends Task<Vault> {
|
private static class RevealVaultTask extends Task<Vault> {
|
||||||
|
|
||||||
private final Vault 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.vault = vault;
|
||||||
this.hostServices = hostServices;
|
this.rs = revealPathService;
|
||||||
setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), getException()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Vault call() {
|
protected Vault call() throws RevealFailedException {
|
||||||
switch (vault.getMountPoint()) {
|
switch (vault.getMountPoint()) {
|
||||||
case null -> LOG.warn("Not currently mounted");
|
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?
|
case Mountpoint.WithUri m -> LOG.info("Vault mounted at {}", m.uri()); // TODO show in UI?
|
||||||
}
|
}
|
||||||
return vault;
|
return vault;
|
||||||
|
|||||||
@@ -58,8 +58,6 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Vault vault;
|
private final Vault vault;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final List<Path> initialList;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<CipherAndCleartext, String> ciphertextColumn;
|
public TableColumn<CipherAndCleartext, String> ciphertextColumn;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -68,12 +66,11 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
public TableView<CipherAndCleartext> cipherToCleartextTable;
|
public TableView<CipherAndCleartext> cipherToCleartextTable;
|
||||||
|
|
||||||
@Inject
|
@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.window = window;
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList());
|
this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
this.initialList = pathsToDecrypt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -97,8 +94,7 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
});
|
});
|
||||||
cipherToCleartextTable.setOnDragDropped(event -> {
|
cipherToCleartextTable.setOnDragDropped(event -> {
|
||||||
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
|
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
|
||||||
checkAndDecrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
|
decrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
|
||||||
cipherToCleartextTable.setItems(mapping);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cipherToCleartextTable.setOnDragExited(_ -> cipherToCleartextTable.setItems(mapping));
|
cipherToCleartextTable.setOnDragExited(_ -> cipherToCleartextTable.setItems(mapping));
|
||||||
@@ -124,9 +120,7 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!initialList.isEmpty()) {
|
window.setOnHidden(_ -> mapping.clear());
|
||||||
checkAndDecrypt(initialList);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copySingleCelltoClipboard() {
|
private void copySingleCelltoClipboard() {
|
||||||
@@ -149,10 +143,18 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
fileChooser.setInitialDirectory(vault.getPath().toFile());
|
fileChooser.setInitialDirectory(vault.getPath().toFile());
|
||||||
var ciphertextNodes = fileChooser.showOpenMultipleDialog(window);
|
var ciphertextNodes = fileChooser.showOpenMultipleDialog(window);
|
||||||
if (ciphertextNodes != null) {
|
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) {
|
private void checkAndDecrypt(List<Path> pathsToDecrypt) {
|
||||||
mapping.clear();
|
mapping.clear();
|
||||||
//Assumption: All files are in the same directory
|
//Assumption: All files are in the same directory
|
||||||
|
|||||||
@@ -28,23 +28,28 @@ public interface DecryptNameComponent {
|
|||||||
@FxmlScene(FxmlFile.DECRYPTNAMES)
|
@FxmlScene(FxmlFile.DECRYPTNAMES)
|
||||||
Lazy<Scene> decryptNamesView();
|
Lazy<Scene> decryptNamesView();
|
||||||
|
|
||||||
|
DecryptFileNamesViewController controller();
|
||||||
|
|
||||||
@DecryptNameWindow
|
@DecryptNameWindow
|
||||||
Vault vault();
|
Vault vault();
|
||||||
|
|
||||||
default void showDecryptFileNameWindow() {
|
default void showDecryptFileNameWindow(List<Path> pathsToDecrypt) {
|
||||||
Stage s = window();
|
Stage s = window();
|
||||||
s.setScene(decryptNamesView().get());
|
s.setScene(decryptNamesView().get());
|
||||||
s.sizeToScene();
|
s.sizeToScene();
|
||||||
if (vault().isUnlocked()) {
|
if (vault().isUnlocked()) {
|
||||||
|
controller().decrypt(pathsToDecrypt);
|
||||||
s.show();
|
s.show();
|
||||||
|
s.requestFocus();
|
||||||
} else {
|
} else {
|
||||||
LOG.error("Aborted showing DecryptFileName window: vault state is not {}, but {}.", VaultState.Value.UNLOCKED, vault().getState());
|
LOG.error("Aborted showing DecryptFileName window: vault state is not {}, but {}.", VaultState.Value.UNLOCKED, vault().getState());
|
||||||
|
s.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subcomponent.Factory
|
@Subcomponent.Factory
|
||||||
interface 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,17 @@ package org.cryptomator.ui.eventview;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Constants;
|
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.Nullable;
|
||||||
import org.cryptomator.common.ObservableUtil;
|
import org.cryptomator.common.ObservableUtil;
|
||||||
import org.cryptomator.cryptofs.CryptoPath;
|
|
||||||
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
|
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
|
||||||
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
|
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
|
||||||
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
||||||
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
||||||
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
|
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.RevealFailedException;
|
||||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
@@ -46,7 +45,6 @@ import java.time.ZoneId;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.FormatStyle;
|
import java.time.format.FormatStyle;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.function.Function;
|
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 static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
|
||||||
|
|
||||||
private final FileSystemEventAggregator fileSystemEventAggregator;
|
private final FileSystemEventAggregator fileSystemEventAggregator;
|
||||||
@Nullable
|
|
||||||
private final RevealPathService revealService;
|
private final RevealPathService revealService;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final ObjectProperty<Map.Entry<FSEventBucket, FSEventBucketContent>> eventEntry;
|
private final ObjectProperty<Map.Entry<FSEventBucket, FSEventBucketContent>> eventEntry;
|
||||||
@@ -82,15 +79,17 @@ public class EventListCellController implements FxController {
|
|||||||
Button eventActionsButton;
|
Button eventActionsButton;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator, Optional<RevealPathService> revealService, ResourceBundle resourceBundle) {
|
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator,
|
||||||
|
RevealPathService revealService,
|
||||||
|
ResourceBundle resourceBundle) {
|
||||||
this.fileSystemEventAggregator = fileSystemEventAggregator;
|
this.fileSystemEventAggregator = fileSystemEventAggregator;
|
||||||
this.revealService = revealService.orElseGet(() -> null);
|
this.revealService = revealService;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.eventEntry = new SimpleObjectProperty<>(null);
|
this.eventEntry = new SimpleObjectProperty<>(null);
|
||||||
this.eventMessage = new SimpleStringProperty();
|
this.eventMessage = new SimpleStringProperty();
|
||||||
this.eventDescription = new SimpleStringProperty();
|
this.eventDescription = new SimpleStringProperty();
|
||||||
this.eventIcon = new SimpleObjectProperty<>();
|
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.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.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()), "");
|
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"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.inUse.message"));
|
||||||
var indexFileName = fiiue.cleartextPath().lastIndexOf("/");
|
var indexFileName = fiiue.cleartextPath().lastIndexOf("/");
|
||||||
eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1));
|
eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1));
|
||||||
if (revealService != null) {
|
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
|
||||||
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
|
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
|
||||||
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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
|
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
|
||||||
var user = userAndDevice[0];
|
var user = userAndDevice[0];
|
||||||
@@ -156,11 +150,7 @@ public class EventListCellController implements FxController {
|
|||||||
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
|
||||||
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
|
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
|
||||||
if (revealService != null) {
|
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
||||||
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
|
||||||
} else {
|
|
||||||
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
|
|
||||||
}
|
|
||||||
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
|
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,46 +158,29 @@ public class EventListCellController implements FxController {
|
|||||||
eventIcon.setValue(FontAwesome5Icon.CHECK);
|
eventIcon.setValue(FontAwesome5Icon.CHECK);
|
||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
|
||||||
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
|
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
|
||||||
if (revealService != null) {
|
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
||||||
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
|
||||||
} else {
|
|
||||||
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) {
|
private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) {
|
||||||
eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT);
|
eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT);
|
||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
|
||||||
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
|
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
|
||||||
if (revealService != null) {
|
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
||||||
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) {
|
private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) {
|
||||||
eventIcon.setValue(FontAwesome5Icon.BAN);
|
eventIcon.setValue(FontAwesome5Icon.BAN);
|
||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
|
||||||
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
|
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
|
||||||
if (revealService != null) {
|
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
||||||
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
|
||||||
} else {
|
|
||||||
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) {
|
private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) {
|
||||||
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
|
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
|
||||||
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
|
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
|
||||||
if (revealService != null) {
|
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
||||||
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
|
||||||
} else {
|
|
||||||
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLocalizedAction(String localizationKey, Runnable action) {
|
private void addLocalizedAction(String localizationKey, Runnable action) {
|
||||||
@@ -270,7 +243,7 @@ public class EventListCellController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var mountPoint = v.getMountPoint().uri().getPath();
|
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
|
mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors
|
||||||
}
|
}
|
||||||
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute
|
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute
|
||||||
|
|||||||
@@ -10,16 +10,20 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import javafx.application.Application;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
public class FxApplication {
|
public class FxApplication {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
||||||
|
|
||||||
|
static final AtomicReference<Application> INSTANCE = new AtomicReference<>();
|
||||||
|
|
||||||
private final long startupTime;
|
private final long startupTime;
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
@@ -33,7 +37,8 @@ public class FxApplication {
|
|||||||
private final FxNotificationManager notificationManager;
|
private final FxNotificationManager notificationManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
FxApplication(@Named("startupTime") long startupTime, //
|
FxApplication(Application fxApp,
|
||||||
|
@Named("startupTime") long startupTime, //
|
||||||
Environment environment, //
|
Environment environment, //
|
||||||
Settings settings, //
|
Settings settings, //
|
||||||
AppLaunchEventHandler launchEventHandler, //
|
AppLaunchEventHandler launchEventHandler, //
|
||||||
@@ -55,6 +60,8 @@ public class FxApplication {
|
|||||||
this.autoUnlocker = autoUnlocker;
|
this.autoUnlocker = autoUnlocker;
|
||||||
this.fxFSEventList = fxFSEventList;
|
this.fxFSEventList = fxFSEventList;
|
||||||
this.notificationManager = notificationManager;
|
this.notificationManager = notificationManager;
|
||||||
|
|
||||||
|
INSTANCE.set(fxApp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
|||||||
@@ -64,17 +64,6 @@ abstract class FxApplicationModule {
|
|||||||
return builder.build();
|
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
|
@Provides
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
@@ -88,10 +77,4 @@ abstract class FxApplicationModule {
|
|||||||
return factory.create();
|
return factory.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@FxApplicationScoped
|
|
||||||
static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) {
|
|
||||||
return factory.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -39,6 +39,7 @@ import java.util.Optional;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionStage;
|
import java.util.concurrent.CompletionStage;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
public class FxApplicationWindows {
|
public class FxApplicationWindows {
|
||||||
@@ -47,15 +48,15 @@ public class FxApplicationWindows {
|
|||||||
|
|
||||||
private final Stage primaryStage;
|
private final Stage primaryStage;
|
||||||
private final Optional<TrayIntegrationProvider> trayIntegration;
|
private final Optional<TrayIntegrationProvider> trayIntegration;
|
||||||
private final Lazy<MainWindowComponent> mainWindow;
|
private final CachedLazy<MainWindowComponent> mainWindow;
|
||||||
private final Lazy<PreferencesComponent> preferencesWindow;
|
private final CachedLazy<PreferencesComponent> preferencesWindow;
|
||||||
private final QuitComponent.Builder quitWindowBuilder;
|
private final QuitComponent.Builder quitWindowBuilder;
|
||||||
private final UnlockComponent.Factory unlockWorkflowFactory;
|
private final UnlockComponent.Factory unlockWorkflowFactory;
|
||||||
private final UpdateReminderComponent.Factory updateReminderWindowFactory;
|
private final UpdateReminderComponent.Factory updateReminderWindowFactory;
|
||||||
private final LockComponent.Factory lockWorkflowFactory;
|
private final LockComponent.Factory lockWorkflowFactory;
|
||||||
private final ErrorComponent.Factory errorWindowFactory;
|
private final ErrorComponent.Factory errorWindowFactory;
|
||||||
private final Lazy<EventViewComponent> eventViewWindow;
|
private final CachedLazy<EventViewComponent> eventViewWindow;
|
||||||
private final Lazy<NotificationComponent> notificationWindow;
|
private final CachedLazy<NotificationComponent> notificationWindow;
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||||
private final ShareVaultComponent.Factory shareVaultWindow;
|
private final ShareVaultComponent.Factory shareVaultWindow;
|
||||||
@@ -65,8 +66,8 @@ public class FxApplicationWindows {
|
|||||||
@Inject
|
@Inject
|
||||||
public FxApplicationWindows(@PrimaryStage Stage primaryStage, //
|
public FxApplicationWindows(@PrimaryStage Stage primaryStage, //
|
||||||
Optional<TrayIntegrationProvider> trayIntegration, //
|
Optional<TrayIntegrationProvider> trayIntegration, //
|
||||||
Lazy<MainWindowComponent> mainWindow, //
|
MainWindowComponent.Builder mainWindowBuilder, //
|
||||||
Lazy<PreferencesComponent> preferencesWindow, //
|
PreferencesComponent.Builder preferencesWindowBuilder, //
|
||||||
QuitComponent.Builder quitWindowBuilder, //
|
QuitComponent.Builder quitWindowBuilder, //
|
||||||
UnlockComponent.Factory unlockWorkflowFactory, //
|
UnlockComponent.Factory unlockWorkflowFactory, //
|
||||||
UpdateReminderComponent.Factory updateReminderWindowFactory, //
|
UpdateReminderComponent.Factory updateReminderWindowFactory, //
|
||||||
@@ -74,21 +75,21 @@ public class FxApplicationWindows {
|
|||||||
ErrorComponent.Factory errorWindowFactory, //
|
ErrorComponent.Factory errorWindowFactory, //
|
||||||
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
||||||
ShareVaultComponent.Factory shareVaultWindow, //
|
ShareVaultComponent.Factory shareVaultWindow, //
|
||||||
Lazy<EventViewComponent> eventViewWindow, //
|
EventViewComponent.Factory eventViewWindowFactory, //
|
||||||
Lazy<NotificationComponent> notificationWindow,
|
NotificationComponent.Factory notificationWindowFactory, //
|
||||||
ExecutorService executor, //
|
ExecutorService executor, //
|
||||||
Dialogs dialogs) {
|
Dialogs dialogs) {
|
||||||
this.primaryStage = primaryStage;
|
this.primaryStage = primaryStage;
|
||||||
this.trayIntegration = trayIntegration;
|
this.trayIntegration = trayIntegration;
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = new CachedLazy<>(mainWindowBuilder::build);
|
||||||
this.preferencesWindow = preferencesWindow;
|
this.preferencesWindow = new CachedLazy<>(preferencesWindowBuilder::build);
|
||||||
this.quitWindowBuilder = quitWindowBuilder;
|
this.quitWindowBuilder = quitWindowBuilder;
|
||||||
this.unlockWorkflowFactory = unlockWorkflowFactory;
|
this.unlockWorkflowFactory = unlockWorkflowFactory;
|
||||||
this.updateReminderWindowFactory = updateReminderWindowFactory;
|
this.updateReminderWindowFactory = updateReminderWindowFactory;
|
||||||
this.lockWorkflowFactory = lockWorkflowFactory;
|
this.lockWorkflowFactory = lockWorkflowFactory;
|
||||||
this.errorWindowFactory = errorWindowFactory;
|
this.errorWindowFactory = errorWindowFactory;
|
||||||
this.eventViewWindow = eventViewWindow;
|
this.eventViewWindow = new CachedLazy<>(eventViewWindowFactory::create);
|
||||||
this.notificationWindow = notificationWindow;
|
this.notificationWindow = new CachedLazy<>(notificationWindowFactory::create);
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||||
this.shareVaultWindow = shareVaultWindow;
|
this.shareVaultWindow = shareVaultWindow;
|
||||||
@@ -218,4 +219,29 @@ public class FxApplicationWindows {
|
|||||||
LOG.error("Failed to display stage", error);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,12 @@ import com.google.common.base.Throwables;
|
|||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.cryptofs.VaultConfig;
|
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 javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
@@ -25,6 +28,7 @@ import java.util.stream.Collectors;
|
|||||||
@HealthCheckScoped
|
@HealthCheckScoped
|
||||||
public class ReportWriter {
|
public class ReportWriter {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ReportWriter.class);
|
||||||
private static final String REPORT_HEADER = """
|
private static final String REPORT_HEADER = """
|
||||||
*******************************************
|
*******************************************
|
||||||
* Cryptomator Vault Health Report *
|
* Cryptomator Vault Health Report *
|
||||||
@@ -43,14 +47,14 @@ public class ReportWriter {
|
|||||||
|
|
||||||
private final Vault vault;
|
private final Vault vault;
|
||||||
private final VaultConfig vaultConfig;
|
private final VaultConfig vaultConfig;
|
||||||
private final Application application;
|
private final RevealPathService revealPathService;
|
||||||
private final Path exportDestination;
|
private final Path exportDestination;
|
||||||
|
|
||||||
@Inject
|
@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.vault = vault;
|
||||||
this.vaultConfig = Objects.requireNonNull(vaultConfigRef.get());
|
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");
|
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() {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.cryptomator.ui.keyloading.hub;
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
import com.nimbusds.jose.JWEObject;
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
@@ -12,8 +11,6 @@ import javax.inject.Inject;
|
|||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.binding.StringBinding;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.concurrent.WorkerStateEvent;
|
import javafx.concurrent.WorkerStateEvent;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -98,6 +98,13 @@ public abstract class HubKeyLoadingModule {
|
|||||||
return fxmlLoaders.createScene(FxmlFile.HUB_NO_KEYCHAIN);
|
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
|
@Provides
|
||||||
@FxmlScene(FxmlFile.HUB_AUTH_FLOW)
|
@FxmlScene(FxmlFile.HUB_AUTH_FLOW)
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
@@ -168,6 +175,13 @@ public abstract class HubKeyLoadingModule {
|
|||||||
return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
|
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
|
@Provides
|
||||||
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT)
|
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT)
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
@@ -180,6 +194,11 @@ public abstract class HubKeyLoadingModule {
|
|||||||
@FxControllerKey(NoKeychainController.class)
|
@FxControllerKey(NoKeychainController.class)
|
||||||
abstract FxController bindNoKeychainController(NoKeychainController controller);
|
abstract FxController bindNoKeychainController(NoKeychainController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(CheckHostTrustController.class)
|
||||||
|
abstract FxController bindCheckHostAuthenticityController(CheckHostTrustController controller);
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(AuthFlowController.class)
|
@FxControllerKey(AuthFlowController.class)
|
||||||
@@ -225,6 +244,11 @@ public abstract class HubKeyLoadingModule {
|
|||||||
@FxControllerKey(UnauthorizedDeviceController.class)
|
@FxControllerKey(UnauthorizedDeviceController.class)
|
||||||
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
|
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(UntrustedHostController.class)
|
||||||
|
abstract FxController bindUnauthorizedHostController(UntrustedHostController controller);
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(RequireAccountInitController.class)
|
@FxControllerKey(RequireAccountInitController.class)
|
||||||
|
|||||||
@@ -36,19 +36,19 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
|
|||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final KeychainManager keychainManager;
|
private final KeychainManager keychainManager;
|
||||||
private final AtomicReference<String> fsOwnerId;
|
private final AtomicReference<String> fsOwnerId;
|
||||||
private final Lazy<Scene> authFlowScene;
|
private final Lazy<Scene> checkHostTrustScene;
|
||||||
private final Lazy<Scene> noKeychainScene;
|
private final Lazy<Scene> noKeychainScene;
|
||||||
private final CompletableFuture<ReceivedKey> result;
|
private final CompletableFuture<ReceivedKey> result;
|
||||||
private final DeviceKey deviceKey;
|
private final DeviceKey deviceKey;
|
||||||
|
|
||||||
@Inject
|
@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.window = window;
|
||||||
this.keychainManager = keychainManager;
|
this.keychainManager = keychainManager;
|
||||||
this.fsOwnerId = fsOwnerId;
|
this.fsOwnerId = fsOwnerId;
|
||||||
window.setTitle(windowTitle);
|
window.setTitle(windowTitle);
|
||||||
window.setOnCloseRequest(_ -> result.cancel(true));
|
window.setOnCloseRequest(_ -> result.cancel(true));
|
||||||
this.authFlowScene = authFlowScene;
|
this.checkHostTrustScene = checkHostTrustScene;
|
||||||
this.noKeychainScene = noKeychainScene;
|
this.noKeychainScene = noKeychainScene;
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.deviceKey = deviceKey;
|
this.deviceKey = deviceKey;
|
||||||
@@ -62,7 +62,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
|
|||||||
throw new NoKeychainAccessProviderException();
|
throw new NoKeychainAccessProviderException();
|
||||||
}
|
}
|
||||||
var keypair = deviceKey.get();
|
var keypair = deviceKey.get();
|
||||||
showWindow(authFlowScene);
|
showWindow(checkHostTrustScene);
|
||||||
var jwe = result.get();
|
var jwe = result.get();
|
||||||
return jwe.decryptMasterkey(keypair.getPrivate());
|
return jwe.decryptMasterkey(keypair.getPrivate());
|
||||||
} catch (NoKeychainAccessProviderException e) {
|
} catch (NoKeychainAccessProviderException e) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
-2
@@ -1,6 +1,7 @@
|
|||||||
package org.cryptomator.ui.keyloading.masterkeyfile;
|
package org.cryptomator.ui.keyloading.masterkeyfile;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.cryptomator.common.Constants;
|
||||||
import org.cryptomator.common.Passphrase;
|
import org.cryptomator.common.Passphrase;
|
||||||
import org.cryptomator.common.keychain.KeychainManager;
|
import org.cryptomator.common.keychain.KeychainManager;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
@@ -63,16 +64,21 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
|
|||||||
public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException {
|
public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException {
|
||||||
window.setTitle(resourceBundle.getString("unlock.title").formatted(vault.getDisplayName()));
|
window.setTitle(resourceBundle.getString("unlock.title").formatted(vault.getDisplayName()));
|
||||||
Preconditions.checkArgument(SCHEME.equalsIgnoreCase(keyId.getScheme()), "Only supports keys with scheme " + SCHEME);
|
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 {
|
try {
|
||||||
Path filePath = vault.getPath().resolve(keyId.getSchemeSpecificPart());
|
// determine masterkey file path:
|
||||||
|
Path filePath = vault.getPath().resolve(Constants.MASTERKEY_FILENAME);
|
||||||
if (!Files.exists(filePath)) {
|
if (!Files.exists(filePath)) {
|
||||||
filePath = askUserForMasterkeyFilePath();
|
filePath = askUserForMasterkeyFilePath();
|
||||||
}
|
}
|
||||||
|
// unlock:
|
||||||
if (passphrase == null) {
|
if (passphrase == null) {
|
||||||
askForPassphrase();
|
askForPassphrase();
|
||||||
}
|
}
|
||||||
var masterkey = masterkeyFileAccess.load(filePath, passphrase);
|
var masterkey = masterkeyFileAccess.load(filePath, passphrase);
|
||||||
//backup
|
// backup on successful unlock:
|
||||||
if (filePath.startsWith(vault.getPath())) {
|
if (filePath.startsWith(vault.getPath())) {
|
||||||
try {
|
try {
|
||||||
BackupHelper.attemptBackup(filePath);
|
BackupHelper.attemptBackup(filePath);
|
||||||
|
|||||||
@@ -2,14 +2,17 @@ package org.cryptomator.ui.mainwindow;
|
|||||||
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultState;
|
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.Animations;
|
||||||
import org.cryptomator.ui.common.AutoAnimator;
|
import org.cryptomator.ui.common.AutoAnimator;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||||
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
import javafx.beans.binding.BooleanBinding;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
@@ -19,10 +22,12 @@ import javafx.fxml.FXML;
|
|||||||
@MainWindowScoped
|
@MainWindowScoped
|
||||||
public class VaultDetailController implements FxController {
|
public class VaultDetailController implements FxController {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(VaultDetailController.class);
|
||||||
|
|
||||||
private final ReadOnlyObjectProperty<Vault> vault;
|
private final ReadOnlyObjectProperty<Vault> vault;
|
||||||
private final Application application;
|
|
||||||
private final ObservableValue<FontAwesome5Icon> glyph;
|
private final ObservableValue<FontAwesome5Icon> glyph;
|
||||||
private final BooleanBinding anyVaultSelected;
|
private final BooleanBinding anyVaultSelected;
|
||||||
|
private final RevealPathService revealPathService;
|
||||||
|
|
||||||
private AutoAnimator spinAnimation;
|
private AutoAnimator spinAnimation;
|
||||||
|
|
||||||
@@ -31,11 +36,11 @@ public class VaultDetailController implements FxController {
|
|||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
VaultDetailController(ObjectProperty<Vault> vault, Application application) {
|
VaultDetailController(ObjectProperty<Vault> vault, RevealPathService revealPathService) {
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.application = application;
|
|
||||||
this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState);
|
this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState);
|
||||||
this.anyVaultSelected = vault.isNotNull();
|
this.anyVaultSelected = vault.isNotNull();
|
||||||
|
this.revealPathService = revealPathService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
@@ -61,7 +66,11 @@ public class VaultDetailController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void revealStorageLocation() {
|
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 */
|
/* Observable Properties */
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package org.cryptomator.ui.mainwindow;
|
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.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 com.tobiasdiez.easybind.EasyBind;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Nullable;
|
import org.cryptomator.common.Nullable;
|
||||||
@@ -21,7 +20,6 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
@@ -31,7 +29,6 @@ import javafx.fxml.FXML;
|
|||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.input.Clipboard;
|
import javafx.scene.input.Clipboard;
|
||||||
import javafx.scene.input.ClipboardContent;
|
import javafx.scene.input.ClipboardContent;
|
||||||
import javafx.scene.input.DataFormat;
|
|
||||||
import javafx.scene.input.DragEvent;
|
import javafx.scene.input.DragEvent;
|
||||||
import javafx.scene.input.TransferMode;
|
import javafx.scene.input.TransferMode;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
@@ -40,12 +37,8 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@@ -60,10 +53,11 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
private final VaultService vaultService;
|
private final VaultService vaultService;
|
||||||
private final WrongFileAlertComponent.Builder wrongFileAlert;
|
private final WrongFileAlertComponent.Builder wrongFileAlert;
|
||||||
private final Stage mainWindow;
|
private final Stage mainWindow;
|
||||||
private final Optional<RevealPathService> revealPathService;
|
private final RevealPathService revealPathService;
|
||||||
private final DecryptNameComponent.Factory decryptNameWindowFactory;
|
private final DecryptNameComponent.Factory decryptNameWindowFactory;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
|
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
|
||||||
|
private final LoadingCache<Vault, DecryptNameComponent> decryptNameWindows;
|
||||||
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
|
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
|
||||||
private final ObservableValue<Boolean> accessibleViaPath;
|
private final ObservableValue<Boolean> accessibleViaPath;
|
||||||
private final ObservableValue<Boolean> accessibleViaUri;
|
private final ObservableValue<Boolean> accessibleViaUri;
|
||||||
@@ -84,7 +78,7 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
VaultStatisticsComponent.Builder vaultStatsBuilder, //
|
VaultStatisticsComponent.Builder vaultStatsBuilder, //
|
||||||
WrongFileAlertComponent.Builder wrongFileAlert, //
|
WrongFileAlertComponent.Builder wrongFileAlert, //
|
||||||
@MainWindow Stage mainWindow, //
|
@MainWindow Stage mainWindow, //
|
||||||
Optional<RevealPathService> revealPathService, //
|
RevealPathService revealPathService, //
|
||||||
DecryptNameComponent.Factory decryptNameWindowFactory, //
|
DecryptNameComponent.Factory decryptNameWindowFactory, //
|
||||||
ResourceBundle resourceBundle) {
|
ResourceBundle resourceBundle) {
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
@@ -95,7 +89,8 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
this.revealPathService = revealPathService;
|
this.revealPathService = revealPathService;
|
||||||
this.decryptNameWindowFactory = decryptNameWindowFactory;
|
this.decryptNameWindowFactory = decryptNameWindowFactory;
|
||||||
this.resourceBundle = resourceBundle;
|
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;
|
this.vaultStatsBuilder = vaultStatsBuilder;
|
||||||
var mp = vault.flatMap(Vault::mountPointProperty);
|
var mp = vault.flatMap(Vault::mountPointProperty);
|
||||||
this.accessibleViaPath = mp.map(m -> m instanceof Mountpoint.WithPath).orElse(false);
|
this.accessibleViaPath = mp.map(m -> m instanceof Mountpoint.WithPath).orElse(false);
|
||||||
@@ -111,7 +106,7 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
revealEncryptedDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverLocateEncrypted));
|
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));
|
revealEncryptedDropZone.setOnDragExited(_ -> draggingOverLocateEncrypted.setValue(false));
|
||||||
|
|
||||||
decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName));
|
decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName));
|
||||||
@@ -156,7 +151,7 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
if (cleartextFile != null) {
|
if (cleartextFile != null) {
|
||||||
var ciphertextPath = getCiphertextPath(cleartextFile.toPath());
|
var ciphertextPath = getCiphertextPath(cleartextFile.toPath());
|
||||||
if (ciphertextPath != null) {
|
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) {
|
private void showDecryptNameWindow(List<Path> pathsToDecrypt) {
|
||||||
decryptNameWindowFactory.create(vault.get(), mainWindow, pathsToDecrypt).showDecryptFileNameWindow();
|
decryptNameWindows.get(vault.get()).showDecryptFileNameWindow(pathsToDecrypt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean startsWithVaultAccessPoint(Path path) {
|
private boolean startsWithVaultAccessPoint(Path path) {
|
||||||
@@ -188,38 +183,26 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void revealOrCopyPaths(List<Path> paths) {
|
private void revealPaths(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) {
|
|
||||||
paths.forEach(path -> {
|
paths.forEach(path -> {
|
||||||
try {
|
try {
|
||||||
LOG.debug("Revealing {}", path);
|
LOG.debug("Revealing {}", path);
|
||||||
service.reveal(path);
|
revealPathService.reveal(path);
|
||||||
} catch (RevealFailedException e) {
|
} catch (RevealFailedException e) {
|
||||||
|
//TODO: show popup in ui
|
||||||
LOG.error("Revealing ciphertext file failed.", e);
|
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) {
|
private VaultStatisticsComponent buildVaultStats(Vault vault) {
|
||||||
return vaultStatsBuilder.vault(vault).build();
|
return vaultStatsBuilder.vault(vault).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DecryptNameComponent buildDecryptNameWindow(Vault vault) {
|
||||||
|
return decryptNameWindowFactory.create(vault, mainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void revealAccessLocation() {
|
public void revealAccessLocation() {
|
||||||
vaultService.reveal(vault.get());
|
vaultService.reveal(vault.get());
|
||||||
@@ -239,7 +222,7 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void showVaultStatistics() {
|
public void showVaultStatistics() {
|
||||||
vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
|
vaultStats.get(vault.get()).showVaultStatisticsWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
|
|||||||
@@ -10,16 +10,18 @@ import org.cryptomator.integrations.common.NamedServiceProvider;
|
|||||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
import org.cryptomator.integrations.quickaccess.QuickAccessService;
|
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.common.FxController;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.ChoiceBox;
|
import javafx.scene.control.ChoiceBox;
|
||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
@@ -41,7 +43,7 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final Optional<AutoStartProvider> autoStartProvider;
|
private final Optional<AutoStartProvider> autoStartProvider;
|
||||||
private final List<QuickAccessService> quickAccessServices;
|
private final List<QuickAccessService> quickAccessServices;
|
||||||
private final Application application;
|
private final RevealPathService revealPathService;
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
private final List<KeychainAccessProvider> keychainAccessProviders;
|
private final List<KeychainAccessProvider> keychainAccessProviders;
|
||||||
private final KeychainManager keychain;
|
private final KeychainManager keychain;
|
||||||
@@ -55,14 +57,21 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
public CheckBox autoCloseVaultsCheckbox;
|
public CheckBox autoCloseVaultsCheckbox;
|
||||||
public CheckBox debugModeCheckbox;
|
public CheckBox debugModeCheckbox;
|
||||||
public CheckBox autoStartCheckbox;
|
public CheckBox autoStartCheckbox;
|
||||||
|
public Button resetTrustedHostsButton;
|
||||||
public ToggleGroup nodeOrientation;
|
public ToggleGroup nodeOrientation;
|
||||||
|
|
||||||
private CompletionStage<Void> keychainMigrations = CompletableFuture.completedFuture(null);
|
private CompletionStage<Void> keychainMigrations = CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, //
|
GeneralPreferencesController(@PreferencesWindow Stage window, //
|
||||||
List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, //
|
Settings settings, //
|
||||||
Environment environment, FxApplicationWindows appWindows, ExecutorService backgroundExecutor) {
|
Optional<AutoStartProvider> autoStartProvider, //
|
||||||
|
List<KeychainAccessProvider> keychainAccessProviders, //
|
||||||
|
KeychainManager keychain, //
|
||||||
|
RevealPathService revealPathService, //
|
||||||
|
Environment environment, //
|
||||||
|
FxApplicationWindows appWindows, //
|
||||||
|
ExecutorService backgroundExecutor) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.autoStartProvider = autoStartProvider;
|
this.autoStartProvider = autoStartProvider;
|
||||||
@@ -70,7 +79,7 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
this.keychain = keychain;
|
this.keychain = keychain;
|
||||||
this.backgroundExecutor = backgroundExecutor;
|
this.backgroundExecutor = backgroundExecutor;
|
||||||
this.quickAccessServices = QuickAccessService.get().toList();
|
this.quickAccessServices = QuickAccessService.get().toList();
|
||||||
this.application = application;
|
this.revealPathService = revealPathService;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
}
|
}
|
||||||
@@ -98,6 +107,9 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
quickAccessServiceChoiceBox.setConverter(new NamedServiceConverter<>());
|
quickAccessServiceChoiceBox.setConverter(new NamedServiceConverter<>());
|
||||||
Bindings.bindBidirectional(settings.quickAccessService, quickAccessServiceChoiceBox.valueProperty(), quickAccessSettingsConverter);
|
Bindings.bindBidirectional(settings.quickAccessService, quickAccessServiceChoiceBox.valueProperty(), quickAccessSettingsConverter);
|
||||||
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
|
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) {
|
private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) {
|
||||||
@@ -124,6 +136,10 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
return autoStartProvider.isPresent();
|
return autoStartProvider.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHubTrustOnFirstUseEnabled() {
|
||||||
|
return environment.hubTrustOnFirstUse();
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void toggleAutoStart() {
|
public void toggleAutoStart() {
|
||||||
autoStartProvider.ifPresent(autoStart -> {
|
autoStartProvider.ifPresent(autoStart -> {
|
||||||
@@ -146,9 +162,18 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
return !quickAccessServices.isEmpty();
|
return !quickAccessServices.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void resetTrustedHosts() {
|
||||||
|
settings.trustedHosts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void showLogfileDirectory() {
|
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 */
|
/* Helper classes */
|
||||||
@@ -196,4 +221,5 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,19 @@ package org.cryptomator.ui.preferences;
|
|||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
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.integrations.update.UpdateStep;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.VaultService;
|
import org.cryptomator.ui.common.VaultService;
|
||||||
import org.cryptomator.updater.UpdateChecker;
|
|
||||||
import org.cryptomator.updater.FallbackUpdateInfo;
|
import org.cryptomator.updater.FallbackUpdateInfo;
|
||||||
|
import org.cryptomator.updater.UpdateChecker;
|
||||||
import org.cryptomator.updater.UpdateService;
|
import org.cryptomator.updater.UpdateService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.animation.PauseTransition;
|
import javafx.animation.PauseTransition;
|
||||||
import javafx.application.Application;
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.BooleanBinding;
|
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 Logger LOG = LoggerFactory.getLogger(UpdatesPreferencesController.class);
|
||||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
|
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 Environment environment;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
@@ -72,8 +73,8 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
public CheckBox checkForUpdatesCheckbox;
|
public CheckBox checkForUpdatesCheckbox;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
|
UpdatesPreferencesController(RevealPathService revealPathService, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
|
||||||
this.application = application;
|
this.revealPathService = revealPathService;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
@@ -106,9 +107,14 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
updateService.setOnFailed(this::updateFailed);
|
updateService.setOnFailed(this::updateFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void showLogfileDirectory() {
|
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
|
@FXML
|
||||||
@@ -254,7 +260,7 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
|
|
||||||
public boolean isProhibitUpdateWhileUnlocked() {
|
public boolean isProhibitUpdateWhileUnlocked() {
|
||||||
// If the result of the last update check was from the fallback mechanism, we don't need to show the warning
|
// 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() {
|
public BooleanBinding prohibitUpdateWhileUnlockedProperty() {
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ import java.net.URISyntaxException;
|
|||||||
public class ShareVaultController implements FxController {
|
public class ShareVaultController implements FxController {
|
||||||
|
|
||||||
private static final String SCHEME_PREFIX = "hub+";
|
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 static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/security/best-practices/#sharing-of-vaults";
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<BorderPane xmlns="http://javafx.com/javafx"
|
<BorderPane xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
fx:controller="org.cryptomator.ui.notification.NotificationController"
|
fx:controller="org.cryptomator.ui.notification.NotificationController"
|
||||||
prefHeight="200.0" prefWidth="400.0" maxHeight="200.0" maxWidth="400.0"
|
prefHeight="224.0" prefWidth="400.0" maxHeight="224.0" maxWidth="400.0"
|
||||||
styleClass="notification-window"
|
styleClass="notification-window"
|
||||||
accessibleRole="DIALOG">
|
accessibleRole="DIALOG">
|
||||||
<padding>
|
<padding>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
<Label text="${controller.message}" styleClass="label-large" wrapText="true"/>
|
<Label text="${controller.message}" styleClass="label-large" wrapText="true"/>
|
||||||
<Label text="${controller.fileName}" styleClass="label" textOverrun="CENTER_ELLIPSIS" visible="${!controller.fileName.empty}" managed="${!controller.fileName.empty}"/>
|
<Label text="${controller.fileName}" styleClass="label" textOverrun="CENTER_ELLIPSIS" visible="${!controller.fileName.empty}" managed="${!controller.fileName.empty}"/>
|
||||||
<Region minHeight="6"/>
|
<Region minHeight="6"/>
|
||||||
<ScrollPane minViewportWidth="370" minViewportHeight="50">
|
<ScrollPane minViewportWidth="370" minViewportHeight="70">
|
||||||
<Label text="${controller.description}" styleClass="label" wrapText="true" maxWidth="370"/>
|
<Label text="${controller.description}" styleClass="label" wrapText="true" maxWidth="370"/>
|
||||||
</ScrollPane>
|
</ScrollPane>
|
||||||
<Region VBox.vgrow="ALWAYS"/>
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
</ImageView>
|
</ImageView>
|
||||||
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
|
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
|
||||||
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
|
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
|
||||||
<Label text="© 2016 – 2025 Skymatic GmbH"/>
|
<Label text="© 2016 – 2026 Skymatic GmbH"/>
|
||||||
</VBox>
|
</VBox>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.control.CheckBox?>
|
<?import javafx.scene.control.CheckBox?>
|
||||||
<?import javafx.scene.control.ChoiceBox?>
|
<?import javafx.scene.control.ChoiceBox?>
|
||||||
<?import javafx.scene.control.Hyperlink?>
|
<?import javafx.scene.control.Hyperlink?>
|
||||||
@@ -34,6 +35,8 @@
|
|||||||
<CheckBox fx:id="useQuickAccessCheckbox" text="%preferences.general.quickAccessService"/>
|
<CheckBox fx:id="useQuickAccessCheckbox" text="%preferences.general.quickAccessService"/>
|
||||||
<ChoiceBox fx:id="quickAccessServiceChoiceBox" accessibleText="%preferences.general.quickAccessService"/>
|
<ChoiceBox fx:id="quickAccessServiceChoiceBox" accessibleText="%preferences.general.quickAccessService"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
|
<Button fx:id="resetTrustedHostsButton" text="%preferences.general.resetTrustedHosts" visible="${controller.hubTrustOnFirstUseEnabled}" managed="${controller.hubTrustOnFirstUseEnabled}" onAction="#resetTrustedHosts"/>
|
||||||
<Region VBox.vgrow="ALWAYS"/>
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
|
||||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
|
|||||||
@@ -9,90 +9,94 @@
|
|||||||
<?import javafx.scene.control.Hyperlink?>
|
<?import javafx.scene.control.Hyperlink?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.RadioButton?>
|
<?import javafx.scene.control.RadioButton?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
<?import javafx.scene.control.ToggleGroup?>
|
<?import javafx.scene.control.ToggleGroup?>
|
||||||
<?import javafx.scene.control.Tooltip?>
|
<?import javafx.scene.control.Tooltip?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<ScrollPane xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
|
fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
|
||||||
spacing="6">
|
fitToWidth="true"
|
||||||
<fx:define>
|
hbarPolicy="NEVER">
|
||||||
<ToggleGroup fx:id="mountPointToggleGroup"/>
|
<VBox spacing="6">
|
||||||
</fx:define>
|
<fx:define>
|
||||||
<padding>
|
<ToggleGroup fx:id="mountPointToggleGroup"/>
|
||||||
<Insets topRightBottomLeft="12"/>
|
</fx:define>
|
||||||
</padding>
|
<padding>
|
||||||
<children>
|
<Insets topRightBottomLeft="12"/>
|
||||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
</padding>
|
||||||
<Label text="%vaultOptions.mount.volume.type" labelFor="$vaultVolumeTypeChoiceBox"/>
|
<children>
|
||||||
<ChoiceBox fx:id="vaultVolumeTypeChoiceBox"/>
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}" accessibleText="%vaultOptions.mount.info">
|
<Label text="%vaultOptions.mount.volume.type" labelFor="$vaultVolumeTypeChoiceBox"/>
|
||||||
<graphic>
|
<ChoiceBox fx:id="vaultVolumeTypeChoiceBox"/>
|
||||||
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
|
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}" accessibleText="%vaultOptions.mount.info">
|
||||||
</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}">
|
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="FOLDER_OPEN" glyphSize="15"/>
|
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
|
||||||
</graphic>
|
</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>
|
</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>
|
||||||
|
|||||||
@@ -162,6 +162,12 @@ unlock.error.title=Unlock "%s" failed
|
|||||||
hub.noKeychain.message=Unable to access device key
|
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.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
|
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
|
### Waiting
|
||||||
hub.auth.message=Waiting for authentication…
|
hub.auth.message=Waiting for authentication…
|
||||||
hub.auth.description=You should automatically be redirected to the login page.
|
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
|
### Unauthorized
|
||||||
hub.unauthorized.message=Access denied
|
hub.unauthorized.message=Access denied
|
||||||
hub.unauthorized.description=You are not authorized to open this vault. Contact the vault's owner to request access.
|
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
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Action required
|
hub.requireAccountInit.message=Action required
|
||||||
hub.requireAccountInit.description.0=To proceed, please complete the steps required in your
|
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.autoStart=Launch Cryptomator on system start
|
||||||
preferences.general.keychainBackend=Store passwords with
|
preferences.general.keychainBackend=Store passwords with
|
||||||
preferences.general.quickAccessService=Add unlocked vaults to the quick access area
|
preferences.general.quickAccessService=Add unlocked vaults to the quick access area
|
||||||
|
preferences.general.resetTrustedHosts=Reset trusted hosts
|
||||||
## Interface
|
## Interface
|
||||||
preferences.interface=Interface
|
preferences.interface=Interface
|
||||||
preferences.interface.theme=Look & Feel
|
preferences.interface.theme=Look & Feel
|
||||||
@@ -696,33 +706,25 @@ eventView.cell.actionsButton.tooltip=Event actions
|
|||||||
eventView.entry.vaultLocked.description=Unlock "%s" for details
|
eventView.entry.vaultLocked.description=Unlock "%s" for details
|
||||||
eventView.entry.conflictResolved.message=Resolved conflict
|
eventView.entry.conflictResolved.message=Resolved conflict
|
||||||
eventView.entry.conflictResolved.showDecrypted=Show decrypted file
|
eventView.entry.conflictResolved.showDecrypted=Show decrypted file
|
||||||
eventView.entry.conflictResolved.copyDecrypted=Copy decrypted path
|
|
||||||
eventView.entry.conflict.message=Conflict resolution failed
|
eventView.entry.conflict.message=Conflict resolution failed
|
||||||
eventView.entry.conflict.showDecrypted=Show decrypted, original file
|
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.showEncrypted=Show conflicting, encrypted file
|
||||||
eventView.entry.conflict.copyEncrypted=Copy conflicting, encrypted path
|
|
||||||
eventView.entry.decryptionFailed.message=Decryption failed
|
eventView.entry.decryptionFailed.message=Decryption failed
|
||||||
eventView.entry.decryptionFailed.showEncrypted=Show encrypted file
|
eventView.entry.decryptionFailed.showEncrypted=Show encrypted file
|
||||||
eventView.entry.decryptionFailed.copyEncrypted=Copy encrypted path
|
|
||||||
eventView.entry.brokenDirFile.message=Broken directory link
|
eventView.entry.brokenDirFile.message=Broken directory link
|
||||||
eventView.entry.brokenDirFile.showEncrypted=Show broken, encrypted 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.message=Broken filesystem node
|
||||||
eventView.entry.brokenFileNode.showEncrypted=Show broken, encrypted 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.brokenFileNode.copyDecrypted=Copy decrypted path
|
||||||
eventView.entry.inUse.message=Locked File
|
eventView.entry.inUse.message=File in use
|
||||||
eventView.entry.inUse.showDecrypted=Show decrypted file
|
eventView.entry.inUse.showDecrypted=Show decrypted file
|
||||||
eventView.entry.inUse.copyDecrypted=Copy decrypted path
|
|
||||||
eventView.entry.inUse.showEncrypted=Show encrypted file
|
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.copyUserAndDevice=Copy locking user and device name
|
||||||
eventView.entry.inUse.ignoreLock=Ignore Lock
|
eventView.entry.inUse.ignoreLock=Ignore use status
|
||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.message=File is locked by another device
|
notification.inUse.message=File is in use on another device
|
||||||
notification.inUse.description=The file is opened by %s on device %s. Ask the user to close the file and sync again. Otherwise, you can ignore the lock and open it anyway.
|
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 Lock
|
notification.inUse.action=Ignore Use Status
|
||||||
|
|||||||
@@ -630,26 +630,18 @@ eventView.clearListButton.tooltip=تفريغ القائمة
|
|||||||
eventView.entry.vaultLocked.description=فتح "%s" للحصول على التفاصيل
|
eventView.entry.vaultLocked.description=فتح "%s" للحصول على التفاصيل
|
||||||
eventView.entry.conflictResolved.message=تم حل التضارب
|
eventView.entry.conflictResolved.message=تم حل التضارب
|
||||||
eventView.entry.conflictResolved.showDecrypted=إظهار الملف غير المشفر
|
eventView.entry.conflictResolved.showDecrypted=إظهار الملف غير المشفر
|
||||||
eventView.entry.conflictResolved.copyDecrypted=نسخ المسار غير المشفر
|
|
||||||
eventView.entry.conflict.message=فشل حل التضارب
|
eventView.entry.conflict.message=فشل حل التضارب
|
||||||
eventView.entry.conflict.showDecrypted=إظهار الملَف غير المشفر الأصلي
|
eventView.entry.conflict.showDecrypted=إظهار الملَف غير المشفر الأصلي
|
||||||
eventView.entry.conflict.copyDecrypted=نسخ المسار غير المشفر والأصلي
|
|
||||||
eventView.entry.conflict.showEncrypted=إظهار ملف متضارب ومشفر
|
eventView.entry.conflict.showEncrypted=إظهار ملف متضارب ومشفر
|
||||||
eventView.entry.conflict.copyEncrypted=نسخ مسار التشفير المتعارض
|
|
||||||
eventView.entry.decryptionFailed.message=فشل فك التشفير
|
eventView.entry.decryptionFailed.message=فشل فك التشفير
|
||||||
eventView.entry.decryptionFailed.showEncrypted=عرض ملَف المشفر
|
eventView.entry.decryptionFailed.showEncrypted=عرض ملَف المشفر
|
||||||
eventView.entry.decryptionFailed.copyEncrypted=نسخ مسار المشفر
|
|
||||||
eventView.entry.brokenDirFile.message=رابط الدليل المكسور
|
eventView.entry.brokenDirFile.message=رابط الدليل المكسور
|
||||||
eventView.entry.brokenDirFile.showEncrypted=إظهار الرابط المكسور، المشفر
|
eventView.entry.brokenDirFile.showEncrypted=إظهار الرابط المكسور، المشفر
|
||||||
eventView.entry.brokenDirFile.copyEncrypted=نسخ مسار الرابط المكسور
|
|
||||||
eventView.entry.brokenFileNode.message=عقدة ملفات النظام التافلة
|
eventView.entry.brokenFileNode.message=عقدة ملفات النظام التافلة
|
||||||
eventView.entry.brokenFileNode.showEncrypted=عرض العقدة المشفّرة التافلة
|
eventView.entry.brokenFileNode.showEncrypted=عرض العقدة المشفّرة التافلة
|
||||||
eventView.entry.brokenFileNode.copyEncrypted=نسخ مسار العقدة المشفّرة التافلة
|
|
||||||
eventView.entry.brokenFileNode.copyDecrypted=نسخ المسار غير المشفر
|
eventView.entry.brokenFileNode.copyDecrypted=نسخ المسار غير المشفر
|
||||||
eventView.entry.inUse.showDecrypted=إظهار الملف غير المشفر
|
eventView.entry.inUse.showDecrypted=إظهار الملف غير المشفر
|
||||||
eventView.entry.inUse.copyDecrypted=نسخ المسار غير المشفر
|
|
||||||
eventView.entry.inUse.showEncrypted=عرض ملَف المشفر
|
eventView.entry.inUse.showEncrypted=عرض ملَف المشفر
|
||||||
eventView.entry.inUse.copyEncrypted=نسخ مسار المشفر
|
|
||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ generic.button.print=Отпечатване
|
|||||||
generic.button.remove=Премахване
|
generic.button.remove=Премахване
|
||||||
|
|
||||||
## Vault state
|
## Vault state
|
||||||
|
vault.state.locked=Заключено
|
||||||
|
vault.state.unlocked=Отключено
|
||||||
|
vault.state.missing=Липсващ
|
||||||
|
vault.state.migrationNeeded=Необходима е миграция
|
||||||
|
vault.state.processing=Обработване
|
||||||
|
vault.state.error=Грешка
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
error.message=Възникна грешка
|
error.message=Възникна грешка
|
||||||
@@ -98,6 +104,7 @@ addvault.new.readme.accessLocation.4=При желание можете да п
|
|||||||
## Existing
|
## Existing
|
||||||
addvaultwizard.existing.title=Добавяне на съществуващо хранилище
|
addvaultwizard.existing.title=Добавяне на съществуващо хранилище
|
||||||
addvaultwizard.existing.instruction=Изберете файла „vault.cryptomator“ от съществуващото хранилище, но ако има само файл „masterkey.cryptomator“, изберете него.
|
addvaultwizard.existing.instruction=Изберете файла „vault.cryptomator“ от съществуващото хранилище, но ако има само файл „masterkey.cryptomator“, изберете него.
|
||||||
|
addvaultwizard.existing.restore=Възстановяване…
|
||||||
addvaultwizard.existing.chooseBtn=Избиране…
|
addvaultwizard.existing.chooseBtn=Избиране…
|
||||||
addvaultwizard.existing.filePickerTitle=Избор на файл на хранилището
|
addvaultwizard.existing.filePickerTitle=Избор на файл на хранилището
|
||||||
addvaultwizard.existing.filePickerMimeDesc=Хранилище на Криптоматор
|
addvaultwizard.existing.filePickerMimeDesc=Хранилище на Криптоматор
|
||||||
@@ -145,6 +152,7 @@ unlock.error.customPath.description.hideawayNotDir=Временният, скр
|
|||||||
unlock.error.customPath.description.couldNotBeCleaned=Хранилището не може да бъде монтирано на „%s“. Опитайте отново или изберете друг път.
|
unlock.error.customPath.description.couldNotBeCleaned=Хранилището не може да бъде монтирано на „%s“. Опитайте отново или изберете друг път.
|
||||||
unlock.error.customPath.description.notEmptyDir=Потребителският път на монтиране „%s“ не е празна папка. Изберете празна папка и опитайте отново.
|
unlock.error.customPath.description.notEmptyDir=Потребителският път на монтиране „%s“ не е празна папка. Изберете празна папка и опитайте отново.
|
||||||
unlock.error.customPath.description.generic=Избрали сте потребителски път за монтиране на това хранилище, но при използването му възникна следната грешка: %2$s
|
unlock.error.customPath.description.generic=Избрали сте потребителски път за монтиране на това хранилище, но при използването му възникна следната грешка: %2$s
|
||||||
|
unlock.error.restartRequired.message=Хранилището не може да бъде отключено
|
||||||
unlock.error.title=Неуспешно отключване на „%s“
|
unlock.error.title=Неуспешно отключване на „%s“
|
||||||
## Hub
|
## Hub
|
||||||
hub.noKeychain.message=Няма достъп до ключа на устройството
|
hub.noKeychain.message=Няма достъп до ключа на устройството
|
||||||
@@ -369,6 +377,7 @@ main.vaultlist.contextMenu.unlock=Отключване…
|
|||||||
main.vaultlist.contextMenu.unlockNow=Отключване сега
|
main.vaultlist.contextMenu.unlockNow=Отключване сега
|
||||||
main.vaultlist.contextMenu.vaultoptions=Настройки на хранилището
|
main.vaultlist.contextMenu.vaultoptions=Настройки на хранилището
|
||||||
main.vaultlist.contextMenu.reveal=Разкриване на диска
|
main.vaultlist.contextMenu.reveal=Разкриване на диска
|
||||||
|
main.vaultlist.contextMenu.share=Споделяне…
|
||||||
main.vaultlist.addVaultButton.tooltip=Добавяне на хранилище
|
main.vaultlist.addVaultButton.tooltip=Добавяне на хранилище
|
||||||
##Notification
|
##Notification
|
||||||
## Vault Detail
|
## Vault Detail
|
||||||
|
|||||||
@@ -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.checkFailed=Kontrolproceduren blev afbrudt af en fejl.
|
||||||
health.check.detail.checkCancelled=Kontrolproceduren blev annulleret.
|
health.check.detail.checkCancelled=Kontrolproceduren blev annulleret.
|
||||||
health.check.detail.listFilters.label=Filter
|
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.detail.fixAllSpecificBtn=Løs alle af type
|
||||||
health.check.exportBtn=Eksportér rapport
|
health.check.exportBtn=Eksportér rapport
|
||||||
## Result view
|
## 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.thankYou=Tak fordi du støtter Cryptomators open source-udvikling!
|
||||||
preferences.contribute.donate=Donér
|
preferences.contribute.donate=Donér
|
||||||
preferences.contribute.sponsor=Sponsor
|
preferences.contribute.sponsor=Sponsor
|
||||||
|
preferences.contribute.removeCert.tooltip=Fjern certifikat
|
||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.title=Fjern certifikat
|
removeCert.title=Fjern certifikat
|
||||||
@@ -367,6 +370,7 @@ removeCert.description=Cryptomators kernefunktioner påvirkes ikke af dette. Hve
|
|||||||
|
|
||||||
## About
|
## About
|
||||||
preferences.about=Om
|
preferences.about=Om
|
||||||
|
preferences.about.thirdPartyLicenses=Tredjepartslicenser
|
||||||
|
|
||||||
# Vault Statistics
|
# Vault Statistics
|
||||||
stats.title=Statistik for %s
|
stats.title=Statistik for %s
|
||||||
@@ -406,6 +410,7 @@ stats.access.total=Samlede adgang: %d
|
|||||||
# Main Window
|
# Main Window
|
||||||
## Vault List
|
## Vault List
|
||||||
main.vaultlist=Bokse
|
main.vaultlist=Bokse
|
||||||
|
main.vaultlist.listEntry=Boks %s (%s)
|
||||||
main.vaultlist.emptyList.onboardingInstruction=Klik her for at tilføje en boks
|
main.vaultlist.emptyList.onboardingInstruction=Klik her for at tilføje en boks
|
||||||
main.vaultlist.contextMenu.remove=Fjern…
|
main.vaultlist.contextMenu.remove=Fjern…
|
||||||
main.vaultlist.contextMenu.lock=Lås
|
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.addVaultBtn.menuItemRecover=Genopret eksisterende boks…
|
||||||
main.vaultlist.addVaultButton.tooltip=Tilføj boks
|
main.vaultlist.addVaultButton.tooltip=Tilføj boks
|
||||||
main.vaultlist.showEventsButton.tooltip=Åbn begivenhedsvisning
|
main.vaultlist.showEventsButton.tooltip=Åbn begivenhedsvisning
|
||||||
|
main.vaultlist.showPreferencesButton.tooltip=Vis Indstillinger
|
||||||
##Notification
|
##Notification
|
||||||
main.notification.updateAvailable=Opdatering er tilgængelig.
|
main.notification.updateAvailable=Opdatering er tilgængelig.
|
||||||
main.notification.support=Støt Cryptomator.
|
main.notification.support=Støt Cryptomator.
|
||||||
|
main.notification.closeButton.tooltip=Luk infobjælke
|
||||||
## Vault Detail
|
## Vault Detail
|
||||||
### Welcome
|
### 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.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
|
### Locked
|
||||||
main.vaultDetail.lockedStatus=LÅST
|
main.vaultDetail.lockedStatus=LÅST
|
||||||
main.vaultDetail.unlockBtn=Lås op…
|
main.vaultDetail.unlockBtn=Lås op…
|
||||||
@@ -482,6 +490,7 @@ vaultOptions.general=Generelt
|
|||||||
vaultOptions.general.vaultName=Boks-navn
|
vaultOptions.general.vaultName=Boks-navn
|
||||||
vaultOptions.general.autoLock.lockAfterTimePart1=Lås efter inaktivitet i
|
vaultOptions.general.autoLock.lockAfterTimePart1=Lås efter inaktivitet i
|
||||||
vaultOptions.general.autoLock.lockAfterTimePart2=minutter
|
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.unlockAfterStartup=Lås boksen op når Cryptomator starter
|
||||||
vaultOptions.general.actionAfterUnlock=Efter oplåsning af boks
|
vaultOptions.general.actionAfterUnlock=Efter oplåsning af boks
|
||||||
vaultOptions.general.actionAfterUnlock.ignore=Gør intet
|
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.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.showRecoveryKeyBtn=Vis gendannelsesnøgle
|
||||||
vaultOptions.masterkey.recoverPasswordBtn=Nulstil adgangskode
|
vaultOptions.masterkey.recoverPasswordBtn=Nulstil adgangskode
|
||||||
|
vaultOptions.masterkey.missingMasterkeyFile=Disse tilvalg er kun tilgængelige hvis masterkeyfilen er til stede i boksmappen.
|
||||||
## Hub
|
## Hub
|
||||||
vaultOptions.hub=Gendannelse
|
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.
|
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.filePicker.extensionDescription=Cryptomator krypteret fil
|
||||||
decryptNames.copyTable.tooltip=Kopiér tabel
|
decryptNames.copyTable.tooltip=Kopiér tabel
|
||||||
decryptNames.clearTable.tooltip=Ryd tabel
|
decryptNames.clearTable.tooltip=Ryd tabel
|
||||||
|
decryptNames.column.encrypted=Krypteret
|
||||||
|
decryptNames.column.decrypted=Dekrypteret
|
||||||
decryptNames.copyHint=Kopiér celleindhold med %s
|
decryptNames.copyHint=Kopiér celleindhold med %s
|
||||||
decryptNames.dropZone.message=Slip filer eller klik for at vælge
|
decryptNames.dropZone.message=Slip filer eller klik for at vælge
|
||||||
decryptNames.dropZone.error.vaultInternalFiles=Boks interne filer med intet dekryptérbart navn valgt
|
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.title=Begivenheder
|
||||||
eventView.filter.allVaults=Alle
|
eventView.filter.allVaults=Alle
|
||||||
eventView.clearListButton.tooltip=Ryd liste
|
eventView.clearListButton.tooltip=Ryd liste
|
||||||
|
eventView.filterVaults=Filtrér efter boks
|
||||||
|
eventView.cell.actionsButton.tooltip=Begivenhedshandlinger
|
||||||
## event list entries
|
## event list entries
|
||||||
eventView.entry.vaultLocked.description=Lås "%s" op for detaljer
|
eventView.entry.vaultLocked.description=Lås "%s" op for detaljer
|
||||||
eventView.entry.conflictResolved.message=Løst konflikt
|
eventView.entry.conflictResolved.message=Løst konflikt
|
||||||
eventView.entry.conflictResolved.showDecrypted=Vis dekrypteret fil
|
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.message=Konfliktløsning mislykkedes
|
||||||
eventView.entry.conflict.showDecrypted=Vis dekrypteret, original fil
|
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.showEncrypted=Vis modstridende, krypteret fil
|
||||||
eventView.entry.conflict.copyEncrypted=Kopier modstridende, krypteret sti
|
|
||||||
eventView.entry.decryptionFailed.message=Dekryptering mislykkedes
|
eventView.entry.decryptionFailed.message=Dekryptering mislykkedes
|
||||||
eventView.entry.decryptionFailed.showEncrypted=Vis krypteret fil
|
eventView.entry.decryptionFailed.showEncrypted=Vis krypteret fil
|
||||||
eventView.entry.decryptionFailed.copyEncrypted=Kopiér krypteret sti
|
|
||||||
eventView.entry.brokenDirFile.message=Brudt mappelink
|
eventView.entry.brokenDirFile.message=Brudt mappelink
|
||||||
eventView.entry.brokenDirFile.showEncrypted=Vis brudt, krypteret link
|
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.message=Brudt filsystem-node
|
||||||
eventView.entry.brokenFileNode.showEncrypted=Vis brudt, krypteret 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.brokenFileNode.copyDecrypted=Kopiér dekrypteret sti
|
||||||
|
eventView.entry.inUse.message=Fil i brug
|
||||||
eventView.entry.inUse.showDecrypted=Vis dekrypteret fil
|
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.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
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.message=Filen er låst af en anden enhed
|
notification.inUse.message=Filen er i brug på 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.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 Lås
|
notification.inUse.action=Ignorér anvendelsesstatus
|
||||||
@@ -684,32 +684,21 @@ eventView.cell.actionsButton.tooltip=Ereignisaktionen
|
|||||||
eventView.entry.vaultLocked.description=Entsperre "%s" für Details
|
eventView.entry.vaultLocked.description=Entsperre "%s" für Details
|
||||||
eventView.entry.conflictResolved.message=Konflikt gelöst
|
eventView.entry.conflictResolved.message=Konflikt gelöst
|
||||||
eventView.entry.conflictResolved.showDecrypted=Entschlüsselte Datei anzeigen
|
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.message=Konfliktlösung fehlgeschlagen
|
||||||
eventView.entry.conflict.showDecrypted=Entschlüsselte, ursprüngliche Datei anzeigen
|
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.showEncrypted=Zeige verschlüsselte Konfliktdatei
|
||||||
eventView.entry.conflict.copyEncrypted=Verschlüsselten Konfliktpfad kopieren
|
|
||||||
eventView.entry.decryptionFailed.message=Entschlüsselung fehlgeschlagen
|
eventView.entry.decryptionFailed.message=Entschlüsselung fehlgeschlagen
|
||||||
eventView.entry.decryptionFailed.showEncrypted=Verschlüsselte Datei anzeigen
|
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.message=Ungültiger Verzeichnislink
|
||||||
eventView.entry.brokenDirFile.showEncrypted=Defekten, verschlüsselten Link anzeigen
|
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.message=Beschädigter Dateisystemknoten
|
||||||
eventView.entry.brokenFileNode.showEncrypted=Beschädigten, verschlüsselten Knoten anzeigen
|
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.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.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.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.copyUserAndDevice=Benutzer- und Gerätenamen der Sperre kopieren
|
||||||
eventView.entry.inUse.ignoreLock=Sperre ignorieren
|
|
||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.message=Die Datei ist über ein anderes Gerät gesperrt
|
notification.inUse.action=Verwendungsstatus ignorieren
|
||||||
notification.inUse.action=Sperre ignorieren
|
|
||||||
@@ -630,26 +630,18 @@ eventView.clearListButton.tooltip=Εκκαθάριση λίστας
|
|||||||
eventView.entry.vaultLocked.description=Ξεκλείδωμα "%s" για λεπτομέρειες
|
eventView.entry.vaultLocked.description=Ξεκλείδωμα "%s" για λεπτομέρειες
|
||||||
eventView.entry.conflictResolved.message=Επίλυση σύγκρουσης
|
eventView.entry.conflictResolved.message=Επίλυση σύγκρουσης
|
||||||
eventView.entry.conflictResolved.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
|
eventView.entry.conflictResolved.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
|
||||||
eventView.entry.conflictResolved.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
|
|
||||||
eventView.entry.conflict.message=Η επίλυση συγκρούσεων απέτυχε
|
eventView.entry.conflict.message=Η επίλυση συγκρούσεων απέτυχε
|
||||||
eventView.entry.conflict.showDecrypted=Εμφάνιση αποκρυπτογραφημένων, αρχικό αρχείο
|
eventView.entry.conflict.showDecrypted=Εμφάνιση αποκρυπτογραφημένων, αρχικό αρχείο
|
||||||
eventView.entry.conflict.copyDecrypted=Αντιγραφή αποκρυπτογραφημένων, αρχική διαδρομή
|
|
||||||
eventView.entry.conflict.showEncrypted=Εμφάνιση αντικρουόμενου, κρυπτογραφημένο αρχείο
|
eventView.entry.conflict.showEncrypted=Εμφάνιση αντικρουόμενου, κρυπτογραφημένο αρχείο
|
||||||
eventView.entry.conflict.copyEncrypted=Αντιγραφή σύγκρουσης, κρυπτογραφημένη διαδρομή
|
|
||||||
eventView.entry.decryptionFailed.message=Η αποκρυπτογράφηση απέτυχε
|
eventView.entry.decryptionFailed.message=Η αποκρυπτογράφηση απέτυχε
|
||||||
eventView.entry.decryptionFailed.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
|
eventView.entry.decryptionFailed.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
|
||||||
eventView.entry.decryptionFailed.copyEncrypted=Αντιγραφή κρυπτογραφημένης διαδρομής
|
|
||||||
eventView.entry.brokenDirFile.message=Μη λειτουργικός σύνδεσμος καταλόγου
|
eventView.entry.brokenDirFile.message=Μη λειτουργικός σύνδεσμος καταλόγου
|
||||||
eventView.entry.brokenDirFile.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου συνδέσμου
|
eventView.entry.brokenDirFile.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου συνδέσμου
|
||||||
eventView.entry.brokenDirFile.copyEncrypted=Αντιγραφή διαδρομής του μη λειτουργικού συνδέσμου
|
|
||||||
eventView.entry.brokenFileNode.message=Μη λειτουργικός κόμβος συστήματος αρχείων
|
eventView.entry.brokenFileNode.message=Μη λειτουργικός κόμβος συστήματος αρχείων
|
||||||
eventView.entry.brokenFileNode.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου κόμβου
|
eventView.entry.brokenFileNode.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου κόμβου
|
||||||
eventView.entry.brokenFileNode.copyEncrypted=Αντιγραφή διαδρομής του μη λειτουργικού, κρυπτογραφημένου κόμβου
|
|
||||||
eventView.entry.brokenFileNode.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
|
eventView.entry.brokenFileNode.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
|
||||||
eventView.entry.inUse.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
|
eventView.entry.inUse.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
|
||||||
eventView.entry.inUse.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
|
|
||||||
eventView.entry.inUse.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
|
eventView.entry.inUse.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
|
||||||
eventView.entry.inUse.copyEncrypted=Αντιγραφή κρυπτογραφημένης διαδρομής
|
|
||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
|
|||||||
@@ -684,27 +684,24 @@ eventView.cell.actionsButton.tooltip=Acciones del evento
|
|||||||
eventView.entry.vaultLocked.description=Desbloquear "%s" para más detalles
|
eventView.entry.vaultLocked.description=Desbloquear "%s" para más detalles
|
||||||
eventView.entry.conflictResolved.message=Conflicto resuelto
|
eventView.entry.conflictResolved.message=Conflicto resuelto
|
||||||
eventView.entry.conflictResolved.showDecrypted=Mostrar archivo descifrado
|
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.message=Resolución de conflictos fallida
|
||||||
eventView.entry.conflict.showDecrypted=Mostrar archivo descifrado, original
|
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.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.message=Desencriptación fallida
|
||||||
eventView.entry.decryptionFailed.showEncrypted=Mostrar archivo cifrado
|
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.message=Enlace de directorio roto
|
||||||
eventView.entry.brokenDirFile.showEncrypted=Mostrar enlace roto, cifrado
|
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.message=Nodo de sistema de archivos roto
|
||||||
eventView.entry.brokenFileNode.showEncrypted=Mostrar nodo roto, cifrado
|
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.brokenFileNode.copyDecrypted=Copiar ruta descifrada
|
||||||
|
eventView.entry.inUse.message=Fichero en uso
|
||||||
eventView.entry.inUse.showDecrypted=Mostrar archivo descifrado
|
eventView.entry.inUse.showDecrypted=Mostrar archivo descifrado
|
||||||
eventView.entry.inUse.copyDecrypted=Copiar ruta descifrada
|
|
||||||
eventView.entry.inUse.showEncrypted=Mostrar archivo cifrado
|
eventView.entry.inUse.showEncrypted=Mostrar archivo cifrado
|
||||||
eventView.entry.inUse.copyEncrypted=Copiar ruta cifrada
|
eventView.entry.inUse.ignoreLock=Ignorar el estado de uso
|
||||||
|
|
||||||
|
|
||||||
# Notifications
|
# 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
|
||||||
@@ -683,26 +683,18 @@ eventView.cell.actionsButton.tooltip=Toiminnot
|
|||||||
eventView.entry.vaultLocked.description=Avaa "%s" lisätietoja varten
|
eventView.entry.vaultLocked.description=Avaa "%s" lisätietoja varten
|
||||||
eventView.entry.conflictResolved.message=Ratkaistu ristiriita
|
eventView.entry.conflictResolved.message=Ratkaistu ristiriita
|
||||||
eventView.entry.conflictResolved.showDecrypted=Näytä purettu tiedosto
|
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.message=Ristiriidan ratkaisu epäonnistui
|
||||||
eventView.entry.conflict.showDecrypted=Näytä purettu, alkuperäinen tiedosto
|
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.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.message=Salauksen purku epäonnistui
|
||||||
eventView.entry.decryptionFailed.showEncrypted=Näytä salattu tiedosto
|
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.message=Virheellinen hakemiston linkki
|
||||||
eventView.entry.brokenDirFile.showEncrypted=Näytä virheellinen, salattu 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.message=Virheellinen tiedostojärjestelmän solmu
|
||||||
eventView.entry.brokenFileNode.showEncrypted=Näytä virheellinen, salattu 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.brokenFileNode.copyDecrypted=Kopioi purettu polku
|
||||||
eventView.entry.inUse.showDecrypted=Näytä purettu tiedosto
|
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.showEncrypted=Näytä salattu tiedosto
|
||||||
eventView.entry.inUse.copyEncrypted=Kopioi salattu polku
|
|
||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
|
|||||||
@@ -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.vaultLocked.description=Déverrouillez "%s" pour plus de détails
|
||||||
eventView.entry.conflictResolved.message=Conflit résolu
|
eventView.entry.conflictResolved.message=Conflit résolu
|
||||||
eventView.entry.conflictResolved.showDecrypted=Afficher le fichier déchiffré
|
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.message=La résolution des conflits a échoué
|
||||||
eventView.entry.conflict.showDecrypted=Afficher le fichier original déchiffré
|
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.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.message=Le déchiffrement a échoué
|
||||||
eventView.entry.decryptionFailed.showEncrypted=Afficher le fichier chiffré
|
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.message=Lien de répertoire cassé
|
||||||
eventView.entry.brokenDirFile.showEncrypted=Afficher le lien chiffré 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.message=Nœud de système de fichiers cassé
|
||||||
eventView.entry.brokenFileNode.showEncrypted=Afficher le nœud chiffré 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.brokenFileNode.copyDecrypted=Copier le chemin déchiffré
|
||||||
eventView.entry.inUse.message=Fichier verrouillé
|
eventView.entry.inUse.message=Fichier en cours d’utilisation
|
||||||
eventView.entry.inUse.showDecrypted=Afficher le fichier déchiffré
|
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.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.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 d’utilisation
|
||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.message=Le fichier est verrouillé par un autre appareil
|
notification.inUse.message=Le fichier est utilisé sur un autre appareil
|
||||||
notification.inUse.description=Le fichier est ouvert par %s sur l’appareil %s. Demandez à l’utilisateur de fermer le fichier et synchronisez à nouveau. Autrement vous pouvez ignorer le verrouillage et ouvrir quand même le fichier.
|
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 l’ouvrir maintenant, mais cela pourrait entraîner des conflits ou écraser des modifications récentes.
|
||||||
notification.inUse.action=Ignorer le verrou
|
notification.inUse.action=Ignorer le statut d’utilisation
|
||||||
@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Azioni dell'evento
|
|||||||
eventView.entry.vaultLocked.description=Sblocca "%s" per i dettagli
|
eventView.entry.vaultLocked.description=Sblocca "%s" per i dettagli
|
||||||
eventView.entry.conflictResolved.message=Conflitto risolto
|
eventView.entry.conflictResolved.message=Conflitto risolto
|
||||||
eventView.entry.conflictResolved.showDecrypted=Mostra file decifrato
|
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.message=Risoluzione dei conflitti fallita
|
||||||
eventView.entry.conflict.showDecrypted=Mostra il file originale decifrato
|
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.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.message=Decifratura fallita
|
||||||
eventView.entry.decryptionFailed.showEncrypted=Mostra file cifrato
|
eventView.entry.decryptionFailed.showEncrypted=Mostra file cifrato
|
||||||
eventView.entry.decryptionFailed.copyEncrypted=Copia percorso cifrato
|
|
||||||
eventView.entry.brokenDirFile.message=Collegamento directory errato
|
eventView.entry.brokenDirFile.message=Collegamento directory errato
|
||||||
eventView.entry.brokenDirFile.showEncrypted=Visualizza il link cifrato 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.message=Nodo file errato
|
||||||
eventView.entry.brokenFileNode.showEncrypted=Visualizza il nodo file cifrato 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.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.showDecrypted=Mostra file decifrato
|
||||||
eventView.entry.inUse.copyDecrypted=Copia percorso decriptato
|
|
||||||
eventView.entry.inUse.showEncrypted=Mostra file cifrato
|
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.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
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.message=Il file è bloccato da un altro dispositivo
|
notification.inUse.message=Il file è in uso su 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.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 blocco
|
notification.inUse.action=Ignora lo Stato di Utilizzo
|
||||||
@@ -626,16 +626,10 @@ decryptNames.dropZone.error.generic=ファイル名の復号化に失敗しま
|
|||||||
|
|
||||||
# Event View
|
# Event View
|
||||||
## event list entries
|
## event list entries
|
||||||
eventView.entry.inUse.message=ロックされたファイル
|
|
||||||
eventView.entry.inUse.showDecrypted=復号化されたファイルを表示
|
eventView.entry.inUse.showDecrypted=復号化されたファイルを表示
|
||||||
eventView.entry.inUse.copyDecrypted=復号化されたパスをコピー
|
|
||||||
eventView.entry.inUse.showEncrypted=暗号化されたファイルを表示
|
eventView.entry.inUse.showEncrypted=暗号化されたファイルを表示
|
||||||
eventView.entry.inUse.copyEncrypted=暗号化されたパスをコピー
|
|
||||||
eventView.entry.inUse.copyUserAndDevice=ロックしているユーザーとデバイス名をコピー
|
eventView.entry.inUse.copyUserAndDevice=ロックしているユーザーとデバイス名をコピー
|
||||||
eventView.entry.inUse.ignoreLock=ロックを無視する
|
|
||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.message=ファイルは別のデバイスによってロックされています
|
|
||||||
notification.inUse.action=ロックを無視する
|
|
||||||
@@ -42,7 +42,7 @@ defaults.vault.vaultName=Vault
|
|||||||
|
|
||||||
# Tray Menu
|
# Tray Menu
|
||||||
traymenu.showMainWindow=보기
|
traymenu.showMainWindow=보기
|
||||||
traymenu.showPreferencesWindow=환경설정
|
traymenu.showPreferencesWindow=환경 설정
|
||||||
traymenu.lockAllVaults=모두 잠그기
|
traymenu.lockAllVaults=모두 잠그기
|
||||||
traymenu.quitApplication=종료
|
traymenu.quitApplication=종료
|
||||||
traymenu.vault.unlock=잠금 해제
|
traymenu.vault.unlock=잠금 해제
|
||||||
@@ -114,7 +114,7 @@ addvaultwizard.success.nextStepsInstructions="%s" Vault가 추가되었습니다
|
|||||||
addvaultwizard.success.unlockNow=지금 잠금 해제
|
addvaultwizard.success.unlockNow=지금 잠금 해제
|
||||||
|
|
||||||
# Remove Vault
|
# Remove Vault
|
||||||
removeVault.title=Vault 제거
|
removeVault.title=Vault "%s" 제거
|
||||||
removeVault.message=Vault를 삭제하시겠습니까?
|
removeVault.message=Vault를 삭제하시겠습니까?
|
||||||
removeVault.description=이 행위는 Cryptomator에서만 이 Vault를 지웁니다. 나중에 다시 추가할 수 있습니다. 암호화된 파일은 하드디스크에서 삭제되지 않습니다.
|
removeVault.description=이 행위는 Cryptomator에서만 이 Vault를 지웁니다. 나중에 다시 추가할 수 있습니다. 암호화된 파일은 하드디스크에서 삭제되지 않습니다.
|
||||||
|
|
||||||
@@ -132,18 +132,18 @@ forgetPassword.confirmBtn=비밀번호 삭제
|
|||||||
# Unlock
|
# Unlock
|
||||||
unlock.title="%s" 잠금 해제
|
unlock.title="%s" 잠금 해제
|
||||||
unlock.passwordPrompt="%s"의 비밀번호를 입력하십시오.
|
unlock.passwordPrompt="%s"의 비밀번호를 입력하십시오.
|
||||||
unlock.savePassword=비밀번호 기억
|
unlock.savePassword=비밀번호 기억하기
|
||||||
unlock.unlockBtn=잠금 해제
|
unlock.unlockBtn=잠금 해제
|
||||||
## Select
|
## Select
|
||||||
unlock.chooseMasterkey.message=마스터키 파일을 찾을 수 없습니다
|
unlock.chooseMasterkey.message=마스터키 파일을 찾을 수 없습니다
|
||||||
unlock.chooseMasterkey.description=이 Vault의 Masterkey를 찾지 못했습니다. 마스터 키 위치를 수동으로 선택하여 주십시오.
|
unlock.chooseMasterkey.description=이 "%s" Vault의 마스터키를 찾지 못했습니다. 마스터키 위치를 수동으로 선택하여 주십시오.
|
||||||
unlock.chooseMasterkey.restoreInstead=대신 마스터키 파일 복구
|
unlock.chooseMasterkey.restoreInstead=대신 마스터키 파일 복구
|
||||||
unlock.chooseMasterkey.filePickerTitle=Masterkey 파일 선택
|
unlock.chooseMasterkey.filePickerTitle=Masterkey 파일 선택
|
||||||
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
|
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
|
||||||
## Success
|
## Success
|
||||||
unlock.success.message=잠금 해제 성공
|
unlock.success.message=잠금 해제 성공
|
||||||
unlock.success.description="%s"이(가) 성공적으로 잠금 해제되었습니다. 이제 이 Vault를 마운트 지점으로 접근할 수 있습니다.
|
unlock.success.description="%s"이(가) 성공적으로 잠금 해제되었습니다. 이제 이 Vault를 마운트 지점으로 접근할 수 있습니다.
|
||||||
unlock.success.rememberChoice=선택 기억하기, 다시 묻지 않음
|
unlock.success.rememberChoice=선택을 기억하고 다시 묻지 않음
|
||||||
unlock.success.revealBtn=드라이브 표시
|
unlock.success.revealBtn=드라이브 표시
|
||||||
## Failure
|
## Failure
|
||||||
unlock.error.customPath.message=Vault를 사용자 정의 경로에 마운트할 수 없습니다.
|
unlock.error.customPath.message=Vault를 사용자 정의 경로에 마운트할 수 없습니다.
|
||||||
@@ -166,7 +166,7 @@ hub.auth.message=인증 대기 중…
|
|||||||
hub.auth.description=자동으로 로그인 페이지로 리다이렉트 될 것입니다.
|
hub.auth.description=자동으로 로그인 페이지로 리다이렉트 될 것입니다.
|
||||||
hub.auth.loginLink=수동으로 열려면 클릭하십시오.
|
hub.auth.loginLink=수동으로 열려면 클릭하십시오.
|
||||||
### Receive Key
|
### Receive Key
|
||||||
hub.receive.message=응답 처리중…
|
hub.receive.message=응답 처리 중…
|
||||||
hub.receive.description=Hub로부터 응답을 처리하고 있습니다. 잠시만 기다려 주십시오.
|
hub.receive.description=Hub로부터 응답을 처리하고 있습니다. 잠시만 기다려 주십시오.
|
||||||
### Register Device
|
### Register Device
|
||||||
hub.register.message=새 기기
|
hub.register.message=새 기기
|
||||||
@@ -181,7 +181,7 @@ hub.register.legacy.description=이 기기로부터 첫번째 Hub 접근입니
|
|||||||
hub.registerSuccess.message=기기 등록됨
|
hub.registerSuccess.message=기기 등록됨
|
||||||
hub.registerSuccess.description=등록에 성공하였습니다. Vault를 잠금 해제할 수 있습니다.
|
hub.registerSuccess.description=등록에 성공하였습니다. Vault를 잠금 해제할 수 있습니다.
|
||||||
hub.registerSuccess.unlockBtn=잠금 해제
|
hub.registerSuccess.unlockBtn=잠금 해제
|
||||||
hub.registerSuccess.legacy.description=Vault에 접근하기 위해서는 이 기기를 Vault 소유주가 추가적으로 허가해야 합니다.
|
hub.registerSuccess.legacy.description=Vault에 접근하기 위해서는 이 기기를 Vault 소유자가 추가적으로 허가해야 합니다.
|
||||||
### Registration Failed
|
### Registration Failed
|
||||||
hub.registerFailed.message=기기 등록 실패
|
hub.registerFailed.message=기기 등록 실패
|
||||||
hub.registerFailed.description.generic=등록 중에 오류가 발생했습니다. 앱 로그에서 자세한 정보를 확인할 수 있습니다.
|
hub.registerFailed.description.generic=등록 중에 오류가 발생했습니다. 앱 로그에서 자세한 정보를 확인할 수 있습니다.
|
||||||
@@ -209,18 +209,18 @@ lock.forced.retryBtn=재시도
|
|||||||
lock.forced.forceBtn=강제 잠금
|
lock.forced.forceBtn=강제 잠금
|
||||||
## Failure
|
## Failure
|
||||||
lock.fail.message=Vault 잠금에 실패하였습니다.
|
lock.fail.message=Vault 잠금에 실패하였습니다.
|
||||||
lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료 하십시오.
|
lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료하십시오.
|
||||||
|
|
||||||
# Migration
|
# Migration
|
||||||
migration.title=Vault 업그레이드
|
migration.title=Vault 업그레이드
|
||||||
## Start
|
## Start
|
||||||
migration.start.header=Vault 업그레이드
|
migration.start.header=Vault 업그레이드
|
||||||
migration.start.text=Vault "%s"를 현재 버전의 Cryptomator에서 열기 위해서는 해당 vault를 새 버전으로 업그레이드해야 합니다. 업그레이드를 하기 전에 다음 사항들을 알고 있어야 합니다:
|
migration.start.text=Vault "%s"를 현재 버전의 Cryptomator에서 열기 위해서는 해당 Vault를 새 버전으로 업그레이드해야 합니다. 업그레이드를 하기 전에 다음 사항들을 알고 있어야 합니다:
|
||||||
migration.start.remarkUndone=이 업그레이드는 되돌릴 수 없습니다.
|
migration.start.remarkUndone=이 업그레이드는 되돌릴 수 없습니다.
|
||||||
migration.start.remarkVersions=과거 버전의 Cryptomator는 업그레이드된 Vault를 열 수 없습니다.
|
migration.start.remarkVersions=과거 버전의 Cryptomator는 업그레이드된 Vault를 열 수 없습니다.
|
||||||
migration.start.remarkCanRun=이 Vault를 열 때 사용하는 모든 기기가 현재 버전의 Cryptomator를 실행할 수 있는지 확인해야 합니다.
|
migration.start.remarkCanRun=이 Vault를 열 때 사용하는 모든 기기가 현재 버전의 Cryptomator를 실행할 수 있는지 확인해야 합니다.
|
||||||
migration.start.remarkSynced=업그레이드하기 전에 해당 Vault가 모든 기기에 정상적으로 동기화되어야 합니다.
|
migration.start.remarkSynced=업그레이드하기 전에 해당 Vault가 모든 기기에 정상적으로 동기화되어야 합니다.
|
||||||
migration.start.confirm=나는 위 정보를 읽고 정말 이해했습니다.
|
migration.start.confirm=위 내용을 충분히 숙지하였음을 확인합니다.
|
||||||
## Run
|
## Run
|
||||||
migration.run.enterPassword="%s"의 비밀번호를 입력하십시오.
|
migration.run.enterPassword="%s"의 비밀번호를 입력하십시오.
|
||||||
migration.run.startMigrationBtn=Vault 마이그레이션
|
migration.run.startMigrationBtn=Vault 마이그레이션
|
||||||
@@ -231,8 +231,8 @@ migration.success.unlockNow=지금 잠금 해제
|
|||||||
## Missing file system capabilities
|
## Missing file system capabilities
|
||||||
migration.error.missingFileSystemCapabilities.title=지원하지 않는 파일 시스템
|
migration.error.missingFileSystemCapabilities.title=지원하지 않는 파일 시스템
|
||||||
migration.error.missingFileSystemCapabilities.description=Vault가 부적절한 파일 시스템에 있기 때문에 마이그레이션이 시작되지 않았습니다.
|
migration.error.missingFileSystemCapabilities.description=Vault가 부적절한 파일 시스템에 있기 때문에 마이그레이션이 시작되지 않았습니다.
|
||||||
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=너무 긴 파일 이름을 파일 시스템에서 지원하지 않습니다.
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=파일 시스템이 긴 파일 이름을 지원하지 않습니다.
|
||||||
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=너무 긴 경로를 파일 시스템에서 지원하지 않습니다.
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=파일 시스템이 긴 경로를 지원하지 않습니다.
|
||||||
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=파일 시스템이 읽기를 허용하지 않습니다.
|
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=파일 시스템이 읽기를 허용하지 않습니다.
|
||||||
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=파일 시스템이 쓰기를 허용하지 않습니다.
|
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=파일 시스템이 쓰기를 허용하지 않습니다.
|
||||||
## Impossible
|
## Impossible
|
||||||
@@ -267,7 +267,7 @@ health.check.detail.checkSkipped=선택된 검사항목이 없습니다.
|
|||||||
health.check.detail.checkFinished=검사가 성공적으로 완료되었습니다.
|
health.check.detail.checkFinished=검사가 성공적으로 완료되었습니다.
|
||||||
health.check.detail.checkFinishedAndFound=검사가 완료되었습니다. 검사 결과를 확인해주세요.
|
health.check.detail.checkFinishedAndFound=검사가 완료되었습니다. 검사 결과를 확인해주세요.
|
||||||
health.check.detail.checkFailed=오류로 인해 검사가 종료되었습니다.
|
health.check.detail.checkFailed=오류로 인해 검사가 종료되었습니다.
|
||||||
health.check.detail.checkCancelled=검사가 취소되었습니다
|
health.check.detail.checkCancelled=검사가 취소되었습니다.
|
||||||
health.check.detail.listFilters.label=필터
|
health.check.detail.listFilters.label=필터
|
||||||
health.check.detail.filterSeverity=중요도로 정렬
|
health.check.detail.filterSeverity=중요도로 정렬
|
||||||
health.check.detail.filterFixState=해결 상태로 정렬
|
health.check.detail.filterFixState=해결 상태로 정렬
|
||||||
@@ -286,7 +286,7 @@ health.result.severityTip.crit=상태: 심각\nVault 구조가 손상되었습
|
|||||||
health.result.fixStateFilter.all=모든 문제 해결 상태
|
health.result.fixStateFilter.all=모든 문제 해결 상태
|
||||||
health.result.fixStateFilter.fixable=문제 해결 가능
|
health.result.fixStateFilter.fixable=문제 해결 가능
|
||||||
health.result.fixStateFilter.notFixable=문제 해결 불가
|
health.result.fixStateFilter.notFixable=문제 해결 불가
|
||||||
health.result.fixStateFilter.fixing=문제 해결중…
|
health.result.fixStateFilter.fixing=문제 해결 중…
|
||||||
health.result.fixStateFilter.fixed=문제 해결됨
|
health.result.fixStateFilter.fixed=문제 해결됨
|
||||||
health.result.fixStateFilter.fixFailed=문제 해결 실패
|
health.result.fixStateFilter.fixFailed=문제 해결 실패
|
||||||
## Fix Application
|
## Fix Application
|
||||||
@@ -295,7 +295,7 @@ health.fix.successTip=문제 해결이 성공적으로 완료되었습니다
|
|||||||
health.fix.failTip=문제 해결 실패, 상세 정보는 로그를 참조하십시오.
|
health.fix.failTip=문제 해결 실패, 상세 정보는 로그를 참조하십시오.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=환경설정
|
preferences.title=환경 설정
|
||||||
## General
|
## General
|
||||||
preferences.general=일반
|
preferences.general=일반
|
||||||
preferences.general.startHidden=Cryptomator를 시작할 때 창 숨김
|
preferences.general.startHidden=Cryptomator를 시작할 때 창 숨김
|
||||||
@@ -309,9 +309,9 @@ preferences.general.quickAccessService=열린 Vault를 빠른 접근 위치에
|
|||||||
preferences.interface=인터페이스
|
preferences.interface=인터페이스
|
||||||
preferences.interface.theme=테마
|
preferences.interface.theme=테마
|
||||||
preferences.interface.theme.automatic=자동
|
preferences.interface.theme.automatic=자동
|
||||||
preferences.interface.theme.dark=어둡게
|
preferences.interface.theme.dark=다크 모드
|
||||||
preferences.interface.theme.light=밝게
|
preferences.interface.theme.light=라이트 모드
|
||||||
preferences.interface.unlockThemes=다크모드 해제
|
preferences.interface.unlockThemes=다크 모드 사용 권한을 얻어보세요!
|
||||||
preferences.interface.language=언어 (재시작 필요)
|
preferences.interface.language=언어 (재시작 필요)
|
||||||
preferences.interface.language.auto=시스템 기본 설정
|
preferences.interface.language.auto=시스템 기본 설정
|
||||||
preferences.interface.interfaceOrientation=인터페이스 방향
|
preferences.interface.interfaceOrientation=인터페이스 방향
|
||||||
@@ -356,7 +356,7 @@ preferences.contribute=후원하기
|
|||||||
preferences.contribute.registeredFor=%s(으)로 후원자 인증 등록됨
|
preferences.contribute.registeredFor=%s(으)로 후원자 인증 등록됨
|
||||||
preferences.contribute.noCertificate=Cryptomator를 후원하시고 후원자 인증을 받으십시오. 라이선스 키와 비슷하지만 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-)
|
preferences.contribute.noCertificate=Cryptomator를 후원하시고 후원자 인증을 받으십시오. 라이선스 키와 비슷하지만 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-)
|
||||||
preferences.contribute.getCertificate=아직 후원자 인증이 없으신가요? 어떻게 얻는지 배울 수 있습니다.
|
preferences.contribute.getCertificate=아직 후원자 인증이 없으신가요? 어떻게 얻는지 배울 수 있습니다.
|
||||||
preferences.contribute.promptText=후원자 인증코드를 여기에 붙여넣기
|
preferences.contribute.promptText=후원자 인증 코드를 여기에 붙여넣기
|
||||||
preferences.contribute.thankYou=Cryptomator의 오픈 소스 개발을 지원해 주셔서 감사합니다!
|
preferences.contribute.thankYou=Cryptomator의 오픈 소스 개발을 지원해 주셔서 감사합니다!
|
||||||
preferences.contribute.donate=후원하기
|
preferences.contribute.donate=후원하기
|
||||||
preferences.contribute.sponsor=스폰서
|
preferences.contribute.sponsor=스폰서
|
||||||
@@ -364,7 +364,7 @@ preferences.contribute.removeCert.tooltip=인증서 제거
|
|||||||
|
|
||||||
### Remove License Key Dialog
|
### Remove License Key Dialog
|
||||||
removeCert.title=인증서 제거
|
removeCert.title=인증서 제거
|
||||||
removeCert.message=서포터 인증서를 제거하시겠습니까?
|
removeCert.message=후원자 인증서를 제거하시겠습니까?
|
||||||
removeCert.description=Cryptomator의 핵심 기능은 영향을 받지 않습니다. Vault에 대한 접근이 제한되거나 보안이 약화되지 않습니다.
|
removeCert.description=Cryptomator의 핵심 기능은 영향을 받지 않습니다. Vault에 대한 접근이 제한되거나 보안이 약화되지 않습니다.
|
||||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||||
|
|
||||||
@@ -392,7 +392,7 @@ stats.read.accessCount=총 읽기 횟수: %d
|
|||||||
stats.write.throughput.idle=쓰기: 대기 중
|
stats.write.throughput.idle=쓰기: 대기 중
|
||||||
stats.write.throughput.kibs=쓰기: %.2f KiB/s
|
stats.write.throughput.kibs=쓰기: %.2f KiB/s
|
||||||
stats.write.throughput.mibs=쓰기: %.2f MiB/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.kib=데이터 쓰기: %.1f KiB
|
||||||
stats.write.total.data.mib=데이터 쓰기: %.1f MiB
|
stats.write.total.data.mib=데이터 쓰기: %.1f MiB
|
||||||
stats.write.total.data.gib=데이터 쓰기: %.1f GiB
|
stats.write.total.data.gib=데이터 쓰기: %.1f GiB
|
||||||
@@ -427,11 +427,11 @@ main.vaultlist.showEventsButton.tooltip=이벤트 뷰어 열기
|
|||||||
main.vaultlist.showPreferencesButton.tooltip=환경 설정 표시
|
main.vaultlist.showPreferencesButton.tooltip=환경 설정 표시
|
||||||
##Notification
|
##Notification
|
||||||
main.notification.updateAvailable=업데이트가 있습니다.
|
main.notification.updateAvailable=업데이트가 있습니다.
|
||||||
main.notification.support=Cryptomator 지원하기.
|
main.notification.support=Cryptomator 지원해 주세요
|
||||||
main.notification.closeButton.tooltip=정보 표시줄 닫기
|
main.notification.closeButton.tooltip=정보 표시줄 닫기
|
||||||
## Vault Detail
|
## Vault Detail
|
||||||
### Welcome
|
### Welcome
|
||||||
main.vaultDetail.welcomeOnboarding=파일을 보호하기 위해 Cryptomator를 선택해주셔서 감사합니다. 만약 다른 도움이 필요하시면, 시작 안내서를 참조하시기 바랍니다.
|
main.vaultDetail.welcomeOnboarding=파일 보호를 위해 Cryptomator를 선택해 주셔서 감사합니다. 도움이 필요하시면 시작 가이드를 확인해 주십시오:
|
||||||
main.vaultDetail.storageLocation=Vault 저장 위치
|
main.vaultDetail.storageLocation=Vault 저장 위치
|
||||||
### Locked
|
### Locked
|
||||||
main.vaultDetail.lockedStatus=잠김
|
main.vaultDetail.lockedStatus=잠김
|
||||||
@@ -695,32 +695,25 @@ eventView.cell.actionsButton.tooltip=이벤트별 동작
|
|||||||
eventView.entry.vaultLocked.description="%s"를 잠금 해제하여 세부정보 보기
|
eventView.entry.vaultLocked.description="%s"를 잠금 해제하여 세부정보 보기
|
||||||
eventView.entry.conflictResolved.message=해결된 충돌
|
eventView.entry.conflictResolved.message=해결된 충돌
|
||||||
eventView.entry.conflictResolved.showDecrypted=복호화된 파일 보기
|
eventView.entry.conflictResolved.showDecrypted=복호화된 파일 보기
|
||||||
eventView.entry.conflictResolved.copyDecrypted=복호화된 경로 복사하기
|
|
||||||
eventView.entry.conflict.message=충돌 해결 실패
|
eventView.entry.conflict.message=충돌 해결 실패
|
||||||
eventView.entry.conflict.showDecrypted=복호화된 원본 파일 보기
|
eventView.entry.conflict.showDecrypted=복호화된 원본 파일 보기
|
||||||
eventView.entry.conflict.copyDecrypted=복호화된 원본 경로 복사하기
|
|
||||||
eventView.entry.conflict.showEncrypted=충돌하는 암호화된 파일 보기
|
eventView.entry.conflict.showEncrypted=충돌하는 암호화된 파일 보기
|
||||||
eventView.entry.conflict.copyEncrypted=충돌하는 암호화된 경로 복사하기
|
|
||||||
eventView.entry.decryptionFailed.message=복호화 실패
|
eventView.entry.decryptionFailed.message=복호화 실패
|
||||||
eventView.entry.decryptionFailed.showEncrypted=암호화된 파일 보기
|
eventView.entry.decryptionFailed.showEncrypted=암호화된 파일 보기
|
||||||
eventView.entry.decryptionFailed.copyEncrypted=암호화된 경로 복사하기
|
|
||||||
eventView.entry.brokenDirFile.message=망가진 디렉터리 링크
|
eventView.entry.brokenDirFile.message=망가진 디렉터리 링크
|
||||||
eventView.entry.brokenDirFile.showEncrypted=망가진 암호화된 링크 보기
|
eventView.entry.brokenDirFile.showEncrypted=망가진 암호화된 링크 보기
|
||||||
eventView.entry.brokenDirFile.copyEncrypted=망가진 링크의 경로 복사하리
|
eventView.entry.brokenFileNode.message=망가진 파일 시스템 노드
|
||||||
eventView.entry.brokenFileNode.message=망가진 파일시스템 노드
|
|
||||||
eventView.entry.brokenFileNode.showEncrypted=망가진 암호화된 노드 보기
|
eventView.entry.brokenFileNode.showEncrypted=망가진 암호화된 노드 보기
|
||||||
eventView.entry.brokenFileNode.copyEncrypted=망가진 암호화된 노드의 경로 복사하기
|
|
||||||
eventView.entry.brokenFileNode.copyDecrypted=복호화된 경로 복사하기
|
eventView.entry.brokenFileNode.copyDecrypted=복호화된 경로 복사하기
|
||||||
eventView.entry.inUse.message=잠긴 파일
|
eventView.entry.inUse.message=파일 사용 중
|
||||||
eventView.entry.inUse.showDecrypted=복호화된 파일 보기
|
eventView.entry.inUse.showDecrypted=복호화된 파일 보기
|
||||||
eventView.entry.inUse.copyDecrypted=복호화된 경로 복사하기
|
|
||||||
eventView.entry.inUse.showEncrypted=암호화된 파일 보기
|
eventView.entry.inUse.showEncrypted=암호화된 파일 보기
|
||||||
eventView.entry.inUse.copyEncrypted=암호화된 경로 복사하기
|
|
||||||
eventView.entry.inUse.copyUserAndDevice=파일 잠금 사용자 및 기기 이름 복사
|
eventView.entry.inUse.copyUserAndDevice=파일 잠금 사용자 및 기기 이름 복사
|
||||||
eventView.entry.inUse.ignoreLock=잠금 무시
|
eventView.entry.inUse.ignoreLock=사용 여부 상태 무시
|
||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.message=다른 기기에서 파일이 잠겼습니다.
|
notification.inUse.message=다른 기기에서 파일 사용 중
|
||||||
notification.inUse.action=잠금 무시
|
notification.inUse.description=해당 파일은 %s이(가) %s의 컴퓨터에서 열어 놓은 상태입니다. 파일을 닫고 동기화가 완료될 때까지 기다리도록 요청하세요. 상태를 무시하고 지금 파일을 열 수도 있지만, 이렇게 하면 충돌이 발생하거나 최신 변경 사항이 덮어쓰여질 수 있습니다.
|
||||||
|
notification.inUse.action=사용 여부 상태 무시
|
||||||
@@ -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.vaultLocked.description=Atslēgt "%s", lai redzētu informāciju
|
||||||
eventView.entry.conflictResolved.message=Atrisināta nesaderība
|
eventView.entry.conflictResolved.message=Atrisināta nesaderība
|
||||||
eventView.entry.conflictResolved.showDecrypted=Parādīt atšifrēto datni
|
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.message=Nesaderības atrisināšana neizdevās
|
||||||
eventView.entry.conflict.showDecrypted=Parādīt atšifrēto, sākotnējo datni
|
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.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.message=Atšifrēšana neizdevās
|
||||||
eventView.entry.decryptionFailed.showEncrypted=Parādīt šifrēto datni
|
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.message=Bojāta mapes saite
|
||||||
eventView.entry.brokenDirFile.showEncrypted=Parādīt bojāto, šifrēto saiti
|
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.message=Bojāts datņu sistēmas mezgls
|
||||||
eventView.entry.brokenFileNode.showEncrypted=Parādīt bojāto, šifrēto mezglu
|
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.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.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.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.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
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## 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
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user