mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-16 01:31:28 +00:00
Compare commits
240 Commits
1.19.0-bet
...
release/1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
459112f5b8 | ||
|
|
ea86124c7d | ||
|
|
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 |
37
.github/dependabot.yml
vendored
37
.github/dependabot.yml
vendored
@@ -3,10 +3,7 @@ updates:
|
||||
- package-ecosystem: "maven"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "06:00"
|
||||
timezone: "Etc/UTC"
|
||||
interval: "monthly"
|
||||
ignore:
|
||||
- dependency-name: "org.cryptomator:integrations-api"
|
||||
versions: ["2.0.0-alpha1"]
|
||||
@@ -14,37 +11,13 @@ updates:
|
||||
versions: ["2.0.1.MR"]
|
||||
- dependency-name: "org.openjfx:*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
# due to https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter/issues/68
|
||||
- dependency-name: "org.apache.maven.plugins:maven-surefire-plugin"
|
||||
versions: [ "3.5.4", "3.5.5" ]
|
||||
groups:
|
||||
java-test-dependencies:
|
||||
patterns:
|
||||
- "org.junit.jupiter:*"
|
||||
- "org.mockito:*"
|
||||
- "org.hamcrest:*"
|
||||
- "com.google.jimfs:jimfs"
|
||||
maven-build-plugins:
|
||||
patterns:
|
||||
- "org.apache.maven.plugins:*"
|
||||
- "org.jacoco:jacoco-maven-plugin"
|
||||
- "org.owasp:dependency-check-maven"
|
||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
||||
- "org.codehaus.mojo:license-maven-plugin"
|
||||
javafx:
|
||||
patterns:
|
||||
- "org.openjfx:*"
|
||||
java-production-dependencies:
|
||||
maven-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
exclude-patterns:
|
||||
- "org.openjfx:*"
|
||||
- "org.apache.maven.plugins:*"
|
||||
- "org.jacoco:jacoco-maven-plugin"
|
||||
- "org.owasp:dependency-check-maven"
|
||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
||||
- "org.codehaus.mojo:license-maven-plugin"
|
||||
- "org.junit.jupiter:*"
|
||||
- "org.mockito:*"
|
||||
- "org.hamcrest:*"
|
||||
- "com.google.jimfs:jimfs"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/" # even for `.github/workflows`
|
||||
|
||||
34
.github/release-body.md.template
vendored
Normal file
34
.github/release-body.md.template
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<!-- HEADER -->
|
||||
> [!WARN]
|
||||
> 🚧 DO NOT EDIT 🚧
|
||||
>
|
||||
> The [builds are still running](https://github.com/cryptomator/cryptomator/actions/workflows/create-release.yml).
|
||||
> This banner will be replaced after the builds are finished.
|
||||
<!-- /HEADER -->
|
||||
|
||||
<!--REPLACE with auto-generated release notes (see below)
|
||||
### What's New 🎉
|
||||
### Bugfixes 🐛
|
||||
### Other Changes 📎
|
||||
END REPLACE-->
|
||||
|
||||
For a comprehensive view of changes, read the [CHANGELOG](https://github.com/cryptomator/cryptomator/blob/$VERSION/CHANGELOG.md).
|
||||
|
||||
---
|
||||
|
||||
💾 SHA-256 checksums of release artifacts:
|
||||
```
|
||||
$TARBALL
|
||||
$EXE
|
||||
$MSI
|
||||
$DMG_x64
|
||||
$DMG_arm64
|
||||
$APPIMAGE_x86_64
|
||||
$APPIMAGE_aarch64
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> You can verify the GPG signature of all assets using our public key: [`5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
||||
|
||||
<!-- Auto-Generated Release Notes: -->
|
||||
|
||||
189
.github/workflows/RELEASE.md
vendored
Normal file
189
.github/workflows/RELEASE.md
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
# Cryptomator Release Workflow
|
||||
|
||||
This document describes the automated release pipeline defined in [`draft-release.yml`](draft-release.yml) and [`post-publish.yml`](post-publish.yml).
|
||||
|
||||
## Overview
|
||||
|
||||
The release process has two phases:
|
||||
|
||||
1. **Draft phase** (`draft-release.yml`) -- triggered by pushing a signed git tag. Compiles, tests, builds platform installers, and creates a **draft** GitHub Release.
|
||||
2. **Post-publish phase** (`post-publish.yml`) -- triggered when the draft release is manually **published**. Submits Windows installers for AV whitelisting, notifies the team for DEB build and latest-version update, and triggers downstream updates (website, docs, winget).
|
||||
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
htmlLabels: false
|
||||
---
|
||||
flowchart TD
|
||||
%% ── Trigger ──────────────────────────────────────────────
|
||||
push_tag([🏷 Signed tag pushed])
|
||||
|
||||
%% ── Draft phase ──────────────────────────────────────────
|
||||
push_tag --> get-version
|
||||
|
||||
subgraph draft["draft-release.yml"]
|
||||
get-version["get-version
|
||||
*parse semver from tag*"]
|
||||
|
||||
get-version --> create-release-draft
|
||||
create-release-draft["create-release-draft
|
||||
*compile & test (Linux)
|
||||
create draft release
|
||||
sign source tarball*"]
|
||||
|
||||
create-release-draft --> build-exe-and-msi
|
||||
create-release-draft --> build-dmg-arm64
|
||||
create-release-draft --> build-dmg-x64
|
||||
create-release-draft --> build-appimages
|
||||
|
||||
build-exe-and-msi["build-exe-and-msi
|
||||
*calls win-exe.yml
|
||||
MSI + EXE (x64)
|
||||
code-signed & GPG-signed*"]
|
||||
build-dmg-arm64["build-dmg-arm64
|
||||
*calls mac-dmg.yml
|
||||
DMG (arm64)
|
||||
notarized & GPG-signed*"]
|
||||
build-dmg-x64["build-dmg-x64
|
||||
*calls mac-dmg-x64.yml
|
||||
DMG (x64)
|
||||
notarized & GPG-signed*"]
|
||||
build-appimages["build-appimages
|
||||
*calls appimage.yml
|
||||
AppImage (x86_64 + aarch64)
|
||||
GPG-signed*"]
|
||||
|
||||
build-exe-and-msi --> update-sha256sums
|
||||
build-dmg-arm64 --> update-sha256sums
|
||||
build-dmg-x64 --> update-sha256sums
|
||||
build-appimages --> update-sha256sums
|
||||
|
||||
update-sha256sums["update-sha256sums
|
||||
*compute checksums
|
||||
update release body*"]
|
||||
end
|
||||
|
||||
update-sha256sums --> manual_review
|
||||
|
||||
%% ── Manual gate ──────────────────────────────────────────
|
||||
manual_review{{Manual review
|
||||
& publish}}
|
||||
|
||||
%% ── Post-publish phase ───────────────────────────────────
|
||||
manual_review --> published([📢 Release published])
|
||||
published --> post-publish
|
||||
|
||||
subgraph post-publish["post-publish.yml"]
|
||||
direction TB
|
||||
|
||||
check-release["check-release
|
||||
*classify release tag
|
||||
stable, alpha, beta, rc, unknown*"]
|
||||
notify["notify
|
||||
*Slack notifications
|
||||
deb build & version check*"]
|
||||
get-asset-urls["get-asset-urls
|
||||
*extract MSI & EXE
|
||||
download URLs*"]
|
||||
|
||||
check-release --> notify-winget
|
||||
check-release --> trigger-website
|
||||
check-release --> trigger-docs
|
||||
|
||||
get-asset-urls --> allowlist-msi
|
||||
allowlist-msi --> allowlist-exe
|
||||
|
||||
allowlist-msi["allowlist-msi-x64
|
||||
*av-whitelist.yml
|
||||
Kaspersky & Avast*"]
|
||||
allowlist-exe["allowlist-exe-x64
|
||||
*av-whitelist.yml
|
||||
Kaspersky & Avast*"]
|
||||
|
||||
notify-winget["notify-winget
|
||||
*Slack: ready for winget
|
||||
stable only*"]
|
||||
trigger-website["trigger-website-update
|
||||
*dispatch to
|
||||
cryptomator.github.io
|
||||
stable only*"]
|
||||
trigger-docs["trigger-docs-update
|
||||
*dispatch to
|
||||
cryptomator/docs
|
||||
stable only, Windows*"]
|
||||
end
|
||||
```
|
||||
|
||||
## Phase 1: Draft Release (`draft-release.yml`)
|
||||
|
||||
**Trigger:** push of any tag (`*`)
|
||||
|
||||
### Jobs
|
||||
|
||||
| Job | Runs on | Description |
|
||||
|-----|---------|-------------|
|
||||
| **get-version** | ubuntu | Parses the tag into semver components (`semVerNum`, `semVerSuffix`, `revNum`, `versionType`). The release is aborted if not an alpha, beta, rc or 'stable' release. |
|
||||
| **create-release-draft** | ubuntu | Checks out the repo, verifies the tag is **signed** and lives on a `main` or `release/*` branch. Runs `mvn verify` (with `xvfb-run`). Creates a GitHub Release **draft** using the [release body template](../release-body.md.template). Downloads and GPG-signs the source tarball. |
|
||||
| **build-exe-and-msi** | windows | Calls [`win-exe.yml`](win-exe.yml). Builds the MSI and EXE bundle installer for x64 Windows. Code-signed via Azure Trusted Signing, GPG-signed, and uploaded to the draft release. Outputs SHA-256 checksums. |
|
||||
| **build-dmg-arm64** | macos-15 | Calls [`mac-dmg.yml`](mac-dmg.yml). Builds the DMG for Apple Silicon. Code-signed, notarized with Apple, GPG-signed, and uploaded. Outputs SHA-256 checksum. |
|
||||
| **build-dmg-x64** | macos-15-large | Calls [`mac-dmg-x64.yml`](mac-dmg-x64.yml). Same as above but for Intel Macs. Uses macFUSE instead of FUSE-T. |
|
||||
| **build-appimages** | ubuntu | Calls [`appimage.yml`](appimage.yml). Builds AppImages for x86_64 and aarch64 (matrix). GPG-signed and uploaded with `.zsync` delta-update files. Outputs SHA-256 checksums. |
|
||||
| **update-sha256sums** | ubuntu | Runs after all builds complete. Computes the source tarball checksum, collects all artifact checksums, and updates the draft release body via `envsubst`. Replaces the "builds still running" banner with a success notice. |
|
||||
|
||||
### Release Artifacts
|
||||
|
||||
After the draft phase, the GitHub Release contains:
|
||||
|
||||
| Artifact | Platform |
|
||||
|----------|----------|
|
||||
| `cryptomator-<ver>.tar.gz.asc` | Source (GPG signature) |
|
||||
| `Cryptomator-<ver>-x64.msi` + `.asc` | Windows |
|
||||
| `Cryptomator-<ver>-x64.exe` + `.asc` | Windows |
|
||||
| `Cryptomator-<ver>-arm64.dmg` + `.asc` | macOS (Apple Silicon) |
|
||||
| `Cryptomator-<ver>-x64.dmg` + `.asc` | macOS (Intel) |
|
||||
| `cryptomator-<ver>-x86_64.AppImage` + `.zsync` + `.asc` | Linux (x86_64) |
|
||||
| `cryptomator-<ver>-aarch64.AppImage` + `.zsync` + `.asc` | Linux (aarch64) |
|
||||
|
||||
All artifacts are signed with GPG key [`615D449FE6E6A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
||||
|
||||
## Manual Review Gate
|
||||
|
||||
After the draft phase completes, a maintainer reviews the draft release on GitHub. This is the point to:
|
||||
|
||||
- Verify all artifacts are present and checksums look correct.
|
||||
- Edit the auto-generated release notes (What's New, Bugfixes, Other Changes).
|
||||
- **Publish** the release when ready, which triggers phase 2.
|
||||
|
||||
## Phase 2: Post-Publish (`post-publish.yml`)
|
||||
|
||||
**Trigger:** `release: [published]`
|
||||
|
||||
### Jobs
|
||||
|
||||
| Job | Condition | Description |
|
||||
|-----|-----------|-------------|
|
||||
| **notify** | always | Sends Slack notifications to `#cryptomator-desktop`: ready to build `.deb` package, and reminder to update `latest-version.json` on S3. |
|
||||
| **get-asset-urls** | always | Extracts MSI and EXE download URLs from the release assets. |
|
||||
| **check-release** | always | Classifies the published release tag as `stable`, `alpha`, `beta`, `rc`, or `unknown`. Stable-only follow-up jobs depend on this output. Unlike `get-version.yml` workflow, this job does not perform semver validation. |
|
||||
| **allowlist-msi-x64** | Windows release | Calls [`av-whitelist.yml`](av-whitelist.yml). Uploads the MSI to Kaspersky and Avast for whitelisting. |
|
||||
| **allowlist-exe-x64** | Windows release | Same as above for the EXE. Runs sequentially after MSI. |
|
||||
| **notify-winget** | stable + Windows | Sends a Slack notification that the release is ready for [winget submission](winget.yml). |
|
||||
| **trigger-website-update** | stable | Dispatches `desktop-release` event to `cryptomator/cryptomator.github.io`. |
|
||||
| **trigger-docs-update** | stable + Windows | Dispatches `desktop-release` event to `cryptomator/docs`. |
|
||||
|
||||
### Manual Follow-ups
|
||||
|
||||
These steps are triggered by team members after Slack notifications:
|
||||
|
||||
- **Debian package** -- Run the [`debian.yml`](debian.yml) workflow to build `.deb` and optionally upload to the PPA.
|
||||
- **winget** -- Run the [`winget.yml`](winget.yml) workflow to submit to the Windows Package Manager.
|
||||
- **latest-version.json** -- Update the version-check file on S3 (`static.cryptomator.org/desktop/latest-version.json`).
|
||||
|
||||
## Signing & Security
|
||||
|
||||
- **Git tag** must be SSH-signed and reside on `main` or `release/*`.
|
||||
- **Windows** installers are code-signed using Azure Trusted Signing.
|
||||
- **macOS** DMGs are code-signed with an Apple Developer certificate and notarized via `notarytool`.
|
||||
- **All artifacts** receive a detached GPG signature (`.asc`) using key `615D449FE6E6A235`.
|
||||
- **AV whitelisting** is submitted to Kaspersky and Avast after publish (Windows installers only).
|
||||
- The draft release is created using `CRYPTOBOT_RELEASE_TOKEN`, not `GITHUB_TOKEN`, to ensure proper permissions and trigger downstream workflows.
|
||||
165
.github/workflows/appimage.yml
vendored
165
.github/workflows/appimage.yml
vendored
@@ -1,13 +1,44 @@
|
||||
name: Build AppImage
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
- cron: '0 23 20 * *'
|
||||
workflow_call:
|
||||
inputs:
|
||||
semVerNum:
|
||||
type: string
|
||||
description: 'The Major.Minor.Patch part of the version'
|
||||
required: true
|
||||
revisionNum:
|
||||
type: string
|
||||
description: 'The revision number'
|
||||
required: true
|
||||
semVerSuffix:
|
||||
type: string
|
||||
description: 'The suffix of the version, including dash'
|
||||
required: true
|
||||
upload-to-draft:
|
||||
type: boolean
|
||||
default: true
|
||||
outputs:
|
||||
sha256-appimage-x64:
|
||||
description: "SHA256 sum of the x64 appimage"
|
||||
value: ${{ jobs.collect-sha256sums.outputs.x64-sha256sum}}
|
||||
sha256-appimage-aarch64:
|
||||
description: "SHA256 sum of the aarch64 appimage"
|
||||
value: ${{ jobs.collect-sha256sums.outputs.aarch64-sha256sum}}
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
semVerNum:
|
||||
description: 'The Major.Minor.Patch part of the version'
|
||||
required: false
|
||||
revisionNum:
|
||||
description: 'The revision number'
|
||||
required: false
|
||||
semVerSuffix:
|
||||
description: 'The suffix of the version, including dash'
|
||||
required: false
|
||||
default: '-SNAPSHOT'
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
@@ -20,29 +51,29 @@ on:
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
|
||||
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
|
||||
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
|
||||
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
uses: ./.github/workflows/get-version.yml
|
||||
with:
|
||||
version: ${{ inputs.version }} #okay if not defined
|
||||
|
||||
build:
|
||||
name: Build AppImage
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [get-version]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
appimage-suffix: x86_64
|
||||
arch: x86_64
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
|
||||
openjfx-sha: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
|
||||
appimagetool-sha: 'ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0'
|
||||
- os: ubuntu-24.04-arm
|
||||
appimage-suffix: aarch64
|
||||
arch: aarch64
|
||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
|
||||
openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
|
||||
appimagetool-sha: 'f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158'
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Setup Java
|
||||
@@ -55,7 +86,7 @@ jobs:
|
||||
- name: Download OpenJFX jmods
|
||||
id: download-jmods
|
||||
run: |
|
||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||
curl --silent --fail-with-body --proto "=https" -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||
echo "${{ matrix.openjfx-sha }} openjfx-jmods.zip" | shasum -a256 --check
|
||||
mkdir -p openjfx-jmods
|
||||
unzip -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||
@@ -73,7 +104,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
run : mvn versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Plinux -DskipTests
|
||||
- name: Patch target dir
|
||||
@@ -94,13 +125,15 @@ jobs:
|
||||
${JAVA_HOME}/bin/jlink
|
||||
--verbose
|
||||
--output runtime
|
||||
--module-path "${{ steps.jep-493-check.outputs.jmod_paths }}"
|
||||
--module-path "${JMOD_PATHS}"
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
|
||||
--strip-native-commands
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
--strip-debug
|
||||
--compress zip-0
|
||||
env:
|
||||
JMOD_PATHS: ${{ steps.jep-493-check.outputs.jmod_paths }}
|
||||
- name: Run jpackage
|
||||
run: >
|
||||
${JAVA_HOME}/bin/jpackage
|
||||
@@ -113,13 +146,13 @@ jobs:
|
||||
--dest appdir
|
||||
--name Cryptomator
|
||||
--vendor "Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||
--app-version "${VERSION_NUM}.${REVISION_NUM}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
|
||||
--java-options "-Xss5m"
|
||||
--java-options "-Xmx256m"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
|
||||
--java-options "-Dfile.encoding=\"utf-8\""
|
||||
--java-options "-Djava.net.useSystemProxies=true"
|
||||
--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.showTrayIcon=true"
|
||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NUM}\""
|
||||
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
|
||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
||||
--resource-dir dist/linux/resources
|
||||
- name: Patch Cryptomator.AppDir
|
||||
@@ -155,7 +189,8 @@ jobs:
|
||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||
- name: Download AppImageKit
|
||||
run: |
|
||||
curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${{ matrix.appimage-suffix }}.AppImage -o appimagetool.AppImage
|
||||
curl --silent --fail-with-body --proto "=https" -L "https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${{ matrix.arch }}.AppImage" -o appimagetool.AppImage
|
||||
echo "${{ matrix.appimagetool-sha }} appimagetool.AppImage" | shasum -a256 --check
|
||||
chmod +x appimagetool.AppImage
|
||||
./appimagetool.AppImage --appimage-extract
|
||||
- name: Prepare GPG-Agent for signing with key 615D449FE6E6A235
|
||||
@@ -167,26 +202,27 @@ jobs:
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Build AppImage
|
||||
run: >
|
||||
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.appimage-suffix }}.AppImage
|
||||
-u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.appimage-suffix }}.AppImage.zsync"
|
||||
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.AppImage
|
||||
-u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.arch }}.AppImage.zsync"
|
||||
--sign --sign-key=615D449FE6E6A235
|
||||
- name: Create detached GPG signatures
|
||||
run: |
|
||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: appimage-${{ matrix.appimage-suffix }}
|
||||
name: appimage-${{ matrix.arch }}
|
||||
path: |
|
||||
cryptomator-*.AppImage
|
||||
cryptomator-*.AppImage.zsync
|
||||
cryptomator-*.asc
|
||||
if-no-files-found: error
|
||||
- name: Publish AppImage on GitHub Releases
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
if: inputs.upload-to-draft
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
draft: true
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
files: |
|
||||
@@ -194,67 +230,24 @@ jobs:
|
||||
cryptomator-*.zsync
|
||||
cryptomator-*.asc
|
||||
|
||||
create-aur-bin-pr:
|
||||
name: Create PR for aur-bin repo
|
||||
needs: [build, get-version]
|
||||
collect-sha256sums:
|
||||
name: Collect AppImage checksums
|
||||
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:
|
||||
- name: Download AppImages
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
- name: Download AppImage artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
with:
|
||||
path: downloads/
|
||||
merge-multiple: true
|
||||
- name: Compute sha256 hash of AppImages
|
||||
id: checksums
|
||||
pattern: appimage-*
|
||||
path: appimage-artifacts
|
||||
- name: Compute SHA256 sums
|
||||
id: sha256sum
|
||||
run: |
|
||||
X64_SHA256=$(sha256sum downloads/cryptomator-*-x86_64.AppImage | cut -d ' ' -f1)
|
||||
echo "x64-sha256sum=${X64_SHA256}" >> "$GITHUB_OUTPUT"
|
||||
AARCH64_SHA256=$(sha256sum downloads/cryptomator-*-aarch64.AppImage | cut -d ' ' -f1)
|
||||
echo "aarch64-sha256sum=${AARCH64_SHA256}" >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
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
|
||||
read -ra X64_SUM < <(sha256sum appimage-artifacts/appimage-x86_64/cryptomator-*-x86_64.AppImage)
|
||||
read -ra AARCH64_SUM < <(sha256sum appimage-artifacts/appimage-aarch64/cryptomator-*-aarch64.AppImage)
|
||||
echo "x64-sha256sum=${X64_SUM[0]}" >> "$GITHUB_OUTPUT"
|
||||
echo "aarch64-sha256sum=${AARCH64_SUM[0]}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
115
.github/workflows/aur-bin.yml
vendored
Normal file
115
.github/workflows/aur-bin.yml
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
name: PR for aur-bin repo
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
src-tag:
|
||||
description: 'Source or Release tag'
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
uses: ./.github/workflows/get-version.yml
|
||||
with:
|
||||
version: ${{ inputs.src-tag }}
|
||||
|
||||
create-aur-bin-pr:
|
||||
name: Create PR for aur-bin repo
|
||||
if: (github.event_name == 'workflow_dispatch') || (github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable')
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get-version]
|
||||
container:
|
||||
image: archlinux:base-devel
|
||||
env:
|
||||
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
|
||||
PKGDEST: ${{ github.workspace }}/pkgdest
|
||||
SRCDEST: ${{ github.workspace }}/srcdest
|
||||
steps:
|
||||
- name: Prepare pacman
|
||||
run: |
|
||||
pacman-key --init
|
||||
pacman-key --populate archlinux
|
||||
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip github-cli curl pacman-contrib
|
||||
- name: Checkout cryptomator/aur-bin
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: 'cryptomator/aur-bin'
|
||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
- name: Create build user
|
||||
run: |
|
||||
useradd -m builder
|
||||
echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/builder
|
||||
chown -R builder:builder "$GITHUB_WORKSPACE"
|
||||
install -d -m 0755 -o builder -g builder "$PKGDEST" "$SRCDEST"
|
||||
- name: Import Cryptomator release signing key
|
||||
# try first ubuntu. on failure try openpgp keyservers
|
||||
run: >
|
||||
sudo -u builder gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
|
||||
|| sudo -u builder gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
|
||||
- name: Checkout release branch
|
||||
run: |
|
||||
git config --global safe.directory '*'
|
||||
git checkout -b "release/${SEMVER_STR}"
|
||||
- name: Determine pkgrel
|
||||
id: pkgrel
|
||||
run: |
|
||||
CURRENT_VERSION="$(sed -nE 's/^pkgver=(.*)$/\1/p' PKGBUILD | head -n1)"
|
||||
CURRENT_REL="$(sed -nE 's/^pkgrel=([0-9]+).*$/\1/p' PKGBUILD | head -n1)"
|
||||
|
||||
if [[ "$CURRENT_VERSION" == "$TARGET_VERSION" && "$CURRENT_REL" =~ ^[0-9]+$ ]]; then
|
||||
NEXT_REL=$((CURRENT_REL + 1))
|
||||
else
|
||||
NEXT_REL=1
|
||||
fi
|
||||
|
||||
echo "value=${NEXT_REL}" >> "$GITHUB_OUTPUT"
|
||||
echo "dist-version=${TARGET_VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Update build file
|
||||
run: |
|
||||
sed -i -e "s|^pkgver=.*$|pkgver=${PKG_VERSION}|" PKGBUILD
|
||||
sed -i -e "s|^pkgrel=.*$|pkgrel=${PKG_RELEASE}|" PKGBUILD
|
||||
sudo -u builder updpkgsums
|
||||
sudo -u builder makepkg --printsrcinfo > .SRCINFO
|
||||
env:
|
||||
PKG_VERSION: ${{ needs.get-version.outputs.semVerNum }}
|
||||
PKG_RELEASE: ${{ steps.pkgrel.outputs.value }}
|
||||
- name: Build package with makepkg
|
||||
run: >
|
||||
sudo -u builder
|
||||
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
|
||||
makepkg --syncdeps --cleanbuild --noconfirm --log
|
||||
- name: Commit and push
|
||||
run: |
|
||||
git config user.name "cryptobot"
|
||||
git config user.email "cryptobot@users.noreply.github.com"
|
||||
git config push.autoSetupRemote true
|
||||
git stage PKGBUILD .SRCINFO
|
||||
git commit -m "Prepare release ${DIST_VERSION}"
|
||||
git push
|
||||
env:
|
||||
DIST_VERSION: ${{ steps.pkgrel.outputs.dist-version }}
|
||||
- name: Create pull request
|
||||
id: create-pr
|
||||
run: |
|
||||
printf "Created by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" > pr_body.md
|
||||
PR_URL=$(gh pr create --title "Release ${DIST_VERSION}" --body-file pr_body.md)
|
||||
echo "url=$PR_URL" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
DIST_VERSION: ${{ steps.pkgrel.outputs.dist-version }}
|
||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
SLACK_ICON: ''
|
||||
SLACK_ICON_EMOJI: ':bot:'
|
||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||
SLACK_TITLE: "AUR-bin release PR for ${{ github.event.repository.name }} ${{ needs.get-version.outputs.semVerStr }} created."
|
||||
SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.url }}|PR> on how to proceed."
|
||||
SLACK_FOOTER: ''
|
||||
MSG_MINIMAL: true
|
||||
95
.github/workflows/aur.yml
vendored
95
.github/workflows/aur.yml
vendored
@@ -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
|
||||
14
.github/workflows/av-whitelist.yml
vendored
14
.github/workflows/av-whitelist.yml
vendored
@@ -37,7 +37,7 @@ on:
|
||||
jobs:
|
||||
download-file:
|
||||
name: Downloads the file into the VM
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
outputs:
|
||||
fileName: ${{ steps.extractName.outputs.fileName}}
|
||||
env:
|
||||
@@ -49,21 +49,21 @@ jobs:
|
||||
url="${INPUT_URL}"
|
||||
echo "fileName=${url##*/}" >> $GITHUB_OUTPUT
|
||||
- name: Download file
|
||||
run: curl "${INPUT_URL}" -L -o "${{steps.extractName.outputs.fileName}}" --fail-with-body
|
||||
run: curl --silent --fail-with-body --proto "=https" -L "${INPUT_URL}" -o "${{steps.extractName.outputs.fileName}}"
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: ${{ steps.extractName.outputs.fileName }}
|
||||
path: ${{ steps.extractName.outputs.fileName }}
|
||||
if-no-files-found: error
|
||||
allowlist-kaspersky:
|
||||
name: Anti Virus Allowlisting Kaspersky
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
needs: download-file
|
||||
if: inputs.kaspersky
|
||||
steps:
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: ${{ needs.download-file.outputs.fileName }}
|
||||
path: upload
|
||||
@@ -78,12 +78,12 @@ jobs:
|
||||
local-dir: ./upload/
|
||||
allowlist-avast:
|
||||
name: Anti Virus Allowlisting Avast
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
needs: download-file
|
||||
if: inputs.avast
|
||||
steps:
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: ${{ needs.download-file.outputs.fileName }}
|
||||
path: upload
|
||||
|
||||
39
.github/workflows/build.yml
vendored
39
.github/workflows/build.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
@@ -47,40 +47,3 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
- name: Draft a release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
draft: true
|
||||
discussion_category_name: releases
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
generate_release_notes: true
|
||||
body: |-
|
||||
> [!NOTE]
|
||||
> 🚧 Work in Progress 🚧
|
||||
>
|
||||
> Please be patient, the [builds are still running](https://github.com/cryptomator/cryptomator/actions). Binary packages can be found here in a few moments.
|
||||
|
||||
<!--REPLACE with auto-generated release notes (see below)
|
||||
### What's New 🎉
|
||||
### Bugfixes 🐛
|
||||
### Other Changes 📎
|
||||
END REPLACE-->
|
||||
|
||||
Feel free to also read our [CHANGELOG.md](https://github.com/cryptomator/cryptomator/blob/develop/CHANGELOG.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
<!-- Don't forget to include the
|
||||
💾 SHA-256 checksums of release artifacts:
|
||||
```
|
||||
```
|
||||
-->
|
||||
|
||||
> [!TIP]
|
||||
> You can verify the GPG signature of all assets using our public key: [`5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
||||
|
||||
|
||||
|
||||
<!-- Auto-Generated Release Notes: -->
|
||||
|
||||
4
.github/workflows/check-jdk-updates.yml
vendored
4
.github/workflows/check-jdk-updates.yml
vendored
@@ -74,10 +74,10 @@ jobs:
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
SLACK_ICON: false
|
||||
SLACK_ICON: ''
|
||||
SLACK_ICON_EMOJI: ':bot:'
|
||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||
SLACK_TITLE: "JDK update available"
|
||||
SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ steps.determine.outputs.LATEST_JDK_VERSION }}. Check the Nextcloud collective for instructions."
|
||||
SLACK_FOOTER: false
|
||||
SLACK_FOOTER: ''
|
||||
MSG_MINIMAL: true
|
||||
8
.github/workflows/debian.yml
vendored
8
.github/workflows/debian.yml
vendored
@@ -1,6 +1,8 @@
|
||||
name: Build Debian Package
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 22 20 * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
semver:
|
||||
@@ -71,11 +73,11 @@ jobs:
|
||||
- name: Download OpenJFX jmods
|
||||
id: download-jmods
|
||||
run: |
|
||||
curl -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
|
||||
curl --silent --fail-with-body --proto "=https" -L ${{ env.OPENJFX_JMODS_AMD64 }} -o openjfx-amd64.zip
|
||||
echo "${{ env.OPENJFX_JMODS_AMD64_HASH }} openjfx-amd64.zip" | shasum -a256 --check
|
||||
mkdir -p jmods/amd64
|
||||
unzip -j openjfx-amd64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/amd64
|
||||
curl -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
|
||||
curl --silent --fail-with-body --proto "=https" -L ${{ env.OPENJFX_JMODS_AARCH64 }} -o openjfx-aarch64.zip
|
||||
echo "${{ env.OPENJFX_JMODS_AARCH64_HASH }} openjfx-aarch64.zip" | shasum -a256 --check
|
||||
mkdir -p jmods/aarch64
|
||||
unzip -j openjfx-aarch64.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods/aarch64
|
||||
@@ -143,7 +145,7 @@ jobs:
|
||||
run: |
|
||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: linux-deb-package
|
||||
path: |
|
||||
|
||||
157
.github/workflows/draft-release.yml
vendored
Normal file
157
.github/workflows/draft-release.yml
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
name: Draft a Cryptomator Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
uses: ./.github/workflows/get-version.yml
|
||||
with:
|
||||
version: ''
|
||||
|
||||
create-release-draft:
|
||||
name: Compile and Test
|
||||
runs-on: ubuntu-latest
|
||||
needs: get-version
|
||||
if: needs.get-version.outputs.versionType != 'unknown'
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check the git tag is signed
|
||||
run: git cat-file -p "${GITHUB_REF_NAME}" | grep "BEGIN SSH SIGNATURE"
|
||||
- name: Check the git tag is on release or main branch
|
||||
run: git branch -r --contains "${GITHUB_REF_NAME}" | grep -E '^\s*origin/(main|release/.*)\s*$'
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: ${{ env.JAVA_DIST }}
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Build and Test
|
||||
run: xvfb-run mvn -B verify -Plinux
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||
- name: Draft a release
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
draft: true
|
||||
discussion_category_name: releases
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
generate_release_notes: true
|
||||
body_path: .github/release-body.md.template
|
||||
- name: Download source tarball
|
||||
run: |
|
||||
curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/${{ github.ref }}.tar.gz --output cryptomator-${{ github.ref_name }}.tar.gz
|
||||
- name: Sign source tarball with key 615D449FE6E6A235
|
||||
run: |
|
||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Publish asc on GitHub Releases
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
with:
|
||||
draft: true
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
files: |
|
||||
cryptomator-*.tar.gz.asc
|
||||
|
||||
build-exe-and-msi:
|
||||
needs: [get-version, create-release-draft]
|
||||
uses: ./.github/workflows/win-exe.yml
|
||||
with:
|
||||
semVerNum: ${{needs.get-version.outputs.semVerNum}}
|
||||
revisionNum: ${{needs.get-version.outputs.revNum}}
|
||||
semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}}
|
||||
secrets: inherit
|
||||
|
||||
build-dmg-arm64:
|
||||
needs: [get-version, create-release-draft]
|
||||
uses: ./.github/workflows/mac-dmg.yml
|
||||
with:
|
||||
semVerNum: ${{needs.get-version.outputs.semVerNum}}
|
||||
revisionNum: ${{needs.get-version.outputs.revNum}}
|
||||
semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}}
|
||||
secrets: inherit
|
||||
|
||||
build-dmg-x64:
|
||||
needs: [get-version, create-release-draft]
|
||||
uses: ./.github/workflows/mac-dmg-x64.yml
|
||||
with:
|
||||
semVerNum: ${{needs.get-version.outputs.semVerNum}}
|
||||
revisionNum: ${{needs.get-version.outputs.revNum}}
|
||||
semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}}
|
||||
secrets: inherit
|
||||
|
||||
build-appimages:
|
||||
needs: [get-version, create-release-draft]
|
||||
uses: ./.github/workflows/appimage.yml
|
||||
with:
|
||||
semVerNum: ${{needs.get-version.outputs.semVerNum}}
|
||||
revisionNum: ${{needs.get-version.outputs.revNum}}
|
||||
semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}}
|
||||
secrets: inherit
|
||||
|
||||
update-sha256sums:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get-version, build-exe-and-msi, build-dmg-arm64, build-dmg-x64, build-appimages]
|
||||
env:
|
||||
TAG: ${{ github.ref_name }}
|
||||
SEMVER: ${{ needs.get-version.outputs.semVerStr }}
|
||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Compute source tarball SHA256
|
||||
id: src-sha256
|
||||
run: |
|
||||
curl --silent --fail-with-body --proto "=https" -L \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz" \
|
||||
--output "cryptomator-${SEMVER}.tar.gz"
|
||||
read -ra CMD_OUTPUT < <(sha256sum "cryptomator-${SEMVER}.tar.gz")
|
||||
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||
- name: Update release body with checksums
|
||||
run: |
|
||||
CURRENT_BODY=$(gh release view "${TAG}" --json body --jq .body)
|
||||
RELEASE_BODY=$(printf '%s\n' "${CURRENT_BODY}" | sed '/<!-- HEADER -->/,/<!-- \/HEADER -->/c\
|
||||
<!-- HEADER -->\
|
||||
> [!NOTE]\
|
||||
> Release artifacts finished building successfully.\
|
||||
>\
|
||||
> SHA-256 checksums have been updated below.\
|
||||
<!-- /HEADER -->')
|
||||
|
||||
export TARBALL="${SRC_SHA} cryptomator-${SEMVER}.tar.gz"
|
||||
export MSI="${MSI_SHA} Cryptomator-${SEMVER}-x64.msi"
|
||||
export EXE="${EXE_SHA} Cryptomator-${SEMVER}-x64.exe"
|
||||
export DMG_arm64="${DMG_ARM64_SHA} Cryptomator-${SEMVER}-arm64.dmg"
|
||||
export DMG_x64="${DMG_X64_SHA} Cryptomator-${SEMVER}-x64.dmg"
|
||||
export APPIMAGE_x86_64="${APPIMAGE_X64_SHA} cryptomator-${SEMVER}-x86_64.AppImage"
|
||||
export APPIMAGE_aarch64="${APPIMAGE_AARCH64_SHA} cryptomator-${SEMVER}-aarch64.AppImage"
|
||||
|
||||
envsubst '$VERSION $TARBALL $EXE $MSI $DMG_x64 $DMG_arm64 $APPIMAGE_x86_64 $APPIMAGE_aarch64' \
|
||||
<<< "${RELEASE_BODY}" \
|
||||
> release-body.md
|
||||
|
||||
gh release edit "${TAG}" --draft --notes-file release-body.md
|
||||
env:
|
||||
VERSION: ${{ needs.get-version.outputs.semVerStr }}
|
||||
SRC_SHA: ${{ steps.src-sha256.outputs.value }}
|
||||
MSI_SHA: ${{ needs.build-exe-and-msi.outputs.sha256-msi }}
|
||||
EXE_SHA: ${{ needs.build-exe-and-msi.outputs.sha256-exe }}
|
||||
DMG_ARM64_SHA: ${{ needs.build-dmg-arm64.outputs.sha256-dmg }}
|
||||
DMG_X64_SHA: ${{ needs.build-dmg-x64.outputs.sha256-dmg }}
|
||||
APPIMAGE_X64_SHA: ${{ needs.build-appimages.outputs.sha256-appimage-x64 }}
|
||||
APPIMAGE_AARCH64_SHA: ${{ needs.build-appimages.outputs.sha256-appimage-aarch64 }}
|
||||
85
.github/workflows/flathub.yml
vendored
85
.github/workflows/flathub.yml
vendored
@@ -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
|
||||
21
.github/workflows/get-version.yml
vendored
21
.github/workflows/get-version.yml
vendored
@@ -14,6 +14,9 @@ on:
|
||||
semVerNum:
|
||||
description: "The numerical part of the version string"
|
||||
value: ${{ jobs.determine-version.outputs.semVerNum}}
|
||||
semVerSuffix:
|
||||
description: "The suffix of the version string"
|
||||
value: ${{ jobs.determine-version.outputs.semVerSuffix}}
|
||||
revNum:
|
||||
description: "The revision number"
|
||||
value: ${{ jobs.determine-version.outputs.revNum}}
|
||||
@@ -32,6 +35,7 @@ jobs:
|
||||
outputs:
|
||||
semVerNum: ${{ steps.versions.outputs.semVerNum }}
|
||||
semVerStr: ${{ steps.versions.outputs.semVerStr }}
|
||||
semVerSuffix: ${{ steps.versions.outputs.semVerSuffix }}
|
||||
revNum: ${{ steps.versions.outputs.revNum }}
|
||||
type: ${{ steps.versions.outputs.type}}
|
||||
steps:
|
||||
@@ -54,25 +58,30 @@ jobs:
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
|
||||
|
||||
SEM_VER_NUM=$(echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
|
||||
SEM_VER_SUFFIX="${SEM_VER_STR#"$SEM_VER_NUM"}"
|
||||
REVCOUNT=`git rev-list --count HEAD`
|
||||
|
||||
TYPE="unknown"
|
||||
if [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
if [[ -z $SEM_VER_SUFFIX ]]; then
|
||||
TYPE="stable"
|
||||
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-alpha[1-9]+$ ]]; then
|
||||
elif [[ $SEM_VER_SUFFIX =~ -alpha[1-9]+$ ]]; then
|
||||
TYPE="alpha"
|
||||
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-beta[1-9]+$ ]]; then
|
||||
elif [[ $SEM_VER_SUFFIX =~ -beta[1-9]+$ ]]; then
|
||||
TYPE="beta"
|
||||
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-rc[1-9]$ ]]; then
|
||||
elif [[ $SEM_VER_SUFFIX =~ -rc[1-9]+$ ]]; then
|
||||
TYPE="rc"
|
||||
fi
|
||||
|
||||
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
|
||||
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
|
||||
echo "semVerSuffix=${SEM_VER_SUFFIX}" >> $GITHUB_OUTPUT
|
||||
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
|
||||
echo "type=${TYPE}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
VERSION_STRING: ${{ inputs.version }}
|
||||
- name: Validate Version
|
||||
uses: skymatic/semver-validation-action@7a6ae1c9e121540d11c9c7e4e667c83d583aa153 # v3.0.0
|
||||
uses: skymatic/semver-validation-action@7c80b6b03a18b42884761daa9862ff5683ec8c8a # v4.0.0
|
||||
with:
|
||||
version: ${{ steps.versions.outputs.semVerStr }}
|
||||
264
.github/workflows/linux-flatpak.yml
vendored
Normal file
264
.github/workflows/linux-flatpak.yml
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
name: Build flatpak
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
src-tag:
|
||||
description: 'Source or Release tag'
|
||||
required: false
|
||||
create-pr:
|
||||
description: 'Create Flathub PR'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
paths:
|
||||
- '.github/workflows/get-version.yml'
|
||||
- '.github/workflows/linux-flatpak.yml'
|
||||
- 'dist/linux/flatpak/**'
|
||||
- 'dist/linux/common/**'
|
||||
- 'dist/linux/resources/**'
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
uses: ./.github/workflows/get-version.yml
|
||||
with:
|
||||
version: ${{ inputs.src-tag }}
|
||||
|
||||
build-flatpak:
|
||||
name: "Build flatpak"
|
||||
needs: [get-version]
|
||||
container:
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08
|
||||
options: --privileged
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant:
|
||||
- arch: x86_64
|
||||
runner: ubuntu-24.04
|
||||
- arch: aarch64
|
||||
runner: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.variant.runner }}
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
SRC_GIT_SHA: ${{ inputs.src-tag || github.sha}}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: flathub/org.cryptomator.Cryptomator
|
||||
submodules: true
|
||||
- name: Checkout build script
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
path: build-scripts
|
||||
- name: Checkout app source
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
path: cryptomator
|
||||
ref: ${{ env.SRC_GIT_SHA }}
|
||||
fetch-depth: 0
|
||||
- name: Prepare build files
|
||||
# using envsubst instead of yq to keep linebreaks
|
||||
run: |
|
||||
cp -r -f build-scripts/dist/linux/flatpak/* .
|
||||
envsubst '$FLATPAK_VERSION $FLATPAK_REVISION $CRYPTOMATOR_SOURCE' < org.cryptomator.Cryptomator.TEMPLATE.yaml > org.cryptomator.Cryptomator.yaml
|
||||
env:
|
||||
FLATPAK_VERSION: ${{ needs.get-version.outputs.semVerNum }}
|
||||
FLATPAK_REVISION: 1
|
||||
CRYPTOMATOR_SOURCE: |-
|
||||
type: git
|
||||
path: cryptomator
|
||||
commit: ${{ env.SRC_GIT_SHA }}
|
||||
- name: Copy build script for upload
|
||||
run: cp org.cryptomator.Cryptomator.yaml org.cryptomator.Cryptomator.${{matrix.variant.arch}}.yaml
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
archive: false
|
||||
if-no-files-found: error
|
||||
path: |
|
||||
org.cryptomator.Cryptomator.${{matrix.variant.arch}}.yaml
|
||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@401fe28a8384095fc1531b9d320b292f0ee45adb # SNAPSHOT due to using keep-build-dirs
|
||||
with:
|
||||
bundle: cryptomator.flatpak
|
||||
manifest-path: org.cryptomator.Cryptomator.yaml
|
||||
cache-key: flatpak-builder-${{ env.SRC_GIT_SHA }}
|
||||
arch: ${{ matrix.variant.arch }}
|
||||
keep-build-dirs: true
|
||||
- name: Collect maven dependencies
|
||||
working-directory: .flatpak-builder/build/cryptomator-1/.m2/repository/
|
||||
run: |
|
||||
find * -type f \( -iname '*.jar' -o -iname '*.pom' \) | sort -V > /tmp/maven-dependency-files.txt
|
||||
grep -v '^org/openjfx/javafx-' /tmp/maven-dependency-files.txt > maven-dependency-files-common.txt
|
||||
grep '^org/openjfx/javafx-' /tmp/maven-dependency-files.txt > maven-dependency-files-javafx.txt
|
||||
- name: Update arch independent maven dependencies
|
||||
run: |
|
||||
(
|
||||
cd .flatpak-builder/build/cryptomator-1/.m2/repository/
|
||||
|
||||
while IFS= read -r dependencyPath; do
|
||||
dependencyName=$(dirname "$dependencyPath")
|
||||
dependencySha=$(sha256sum "$dependencyPath" | cut -c 1-64)
|
||||
cat <<EOF
|
||||
- type: file
|
||||
dest: .m2/repository/${dependencyName}
|
||||
url: https://repo.maven.apache.org/maven2/${dependencyPath}
|
||||
sha256: ${dependencySha}
|
||||
EOF
|
||||
done < maven-dependency-files-common.txt
|
||||
) > maven-dependencies.yaml
|
||||
- name: Update arch specific maven dependencies
|
||||
run: |
|
||||
(
|
||||
cd .flatpak-builder/build/cryptomator-1/.m2/repository/
|
||||
|
||||
while IFS= read -r dependencyPath; do
|
||||
dependencyName=$(dirname "$dependencyPath")
|
||||
dependencySha=$(sha256sum "$dependencyPath" | cut -c 1-64)
|
||||
cat <<EOF
|
||||
- type: file
|
||||
dest: .m2/repository/${dependencyName}
|
||||
url: https://repo.maven.apache.org/maven2/${dependencyPath}
|
||||
sha256: ${dependencySha}
|
||||
only-arches: [${{ matrix.variant.arch }}]
|
||||
EOF
|
||||
done < maven-dependency-files-javafx.txt
|
||||
) > javafx-maven-dependencies-${{ matrix.variant.arch }}.yaml
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: maven-sources-${{ matrix.variant.arch }}
|
||||
if-no-files-found: error
|
||||
path: |
|
||||
maven-dependencies.yaml
|
||||
javafx-maven-dependencies-${{ matrix.variant.arch }}.yaml
|
||||
|
||||
verify-maven-sources:
|
||||
name: Verify maven sources
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-flatpak]
|
||||
permissions:
|
||||
contents: none
|
||||
steps:
|
||||
- name: Download updated maven aarch64 dependencies
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: maven-sources-aarch64
|
||||
path: mvn-src-aarch64
|
||||
- name: Download updated maven x86_64 dependencies
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: maven-sources-x86_64
|
||||
path: mvn-src-x64
|
||||
- name: Verify arch independent maven dependencies
|
||||
run: cmp --silent mvn-src-aarch64/maven-dependencies.yaml mvn-src-x64/maven-dependencies.yaml
|
||||
|
||||
create-pr:
|
||||
name: Create PR for flathub
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get-version, verify-maven-sources]
|
||||
if: (github.event_name == 'workflow_dispatch' && inputs.create-pr ) || (github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable')
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
TARBALL_URL: 'https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name || inputs.src-tag }}.tar.gz'
|
||||
steps:
|
||||
- name: Check that input "src-tag" is actually a tag
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
if [ -z "$SRC_TAG" ]; then
|
||||
echo '::error::Input "src-tag" must be set to create a Flathub PR'
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
SRC_TAG: ${{ inputs.src-tag }}
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: flathub/org.cryptomator.Cryptomator
|
||||
submodules: true #TODO: Update submodule!
|
||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
- name: Checkout release branch
|
||||
run: |
|
||||
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
path: cryptomator
|
||||
- name: Download source tarball and compute checksum
|
||||
id: sha512
|
||||
run: |
|
||||
curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" ${TARBALL_URL} --output cryptomator.tar.gz
|
||||
TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1)
|
||||
echo "value=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT"
|
||||
- name: Download updated maven aarch64 dependencies
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: maven-sources-aarch64
|
||||
path: mvn-src-aarch64
|
||||
- name: Download updated maven x86_64 dependencies
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: maven-sources-x86_64
|
||||
path: mvn-src-x64
|
||||
- name: Determine revision
|
||||
id: revision
|
||||
run: |
|
||||
CURRENT_VERSION="$(yq '(.modules[] | select(.name == "cryptomator") | .build-options.env.VERSION)' org.cryptomator.Cryptomator.yaml)"
|
||||
CURRENT_REVISION="$(yq '(.modules[] | select(.name == "cryptomator") | .build-options.env.REVISION_NO)' org.cryptomator.Cryptomator.yaml)"
|
||||
|
||||
if [[ "$CURRENT_VERSION" == "$TARGET_VERSION" && "$CURRENT_REVISION" =~ ^[0-9]+$ ]]; then
|
||||
NEXT_REVISION=$((CURRENT_REVISION + 1))
|
||||
else
|
||||
NEXT_REVISION=1
|
||||
fi
|
||||
|
||||
echo "value=${NEXT_REVISION}" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }}
|
||||
- name: Update build files
|
||||
run: |
|
||||
cp -r -f cryptomator/dist/linux/flatpak/* .
|
||||
cp -r -f mvn-src-x64/* .
|
||||
cp -r -f mvn-src-aarch64/* .
|
||||
envsubst '$FLATPAK_VERSION $FLATPAK_REVISION $CRYPTOMATOR_SOURCE' < org.cryptomator.Cryptomator.TEMPLATE.yaml > org.cryptomator.Cryptomator.yaml
|
||||
yq -i 'del(.modules[] | select(.name == "cryptomator") | .build-options.build-args)' org.cryptomator.Cryptomator.yaml
|
||||
yq -i '(.modules[] | select(.name == "cryptomator") | .sources) += ["maven-dependencies.yaml", "javafx-maven-dependencies-x86_64.yaml", "javafx-maven-dependencies-aarch64.yaml"]' org.cryptomator.Cryptomator.yaml
|
||||
env:
|
||||
FLATPAK_VERSION: ${{ needs.get-version.outputs.semVerNum }}
|
||||
FLATPAK_REVISION: ${{ steps.revision.outputs.value}}
|
||||
CRYPTOMATOR_SOURCE: |-
|
||||
type: archive
|
||||
sha512: ${{steps.sha512.outputs.value}}
|
||||
url: ${{ env.TARBALL_URL }}
|
||||
- name: Commit and push
|
||||
run: |
|
||||
git config user.name "cryptobot"
|
||||
git config user.email "cryptobot@users.noreply.github.com"
|
||||
git config push.autoSetupRemote true
|
||||
git stage org.cryptomator.Cryptomator.yaml maven-dependencies.yaml javafx-maven-dependencies-aarch64.yaml javafx-maven-dependencies-x86_64.yaml
|
||||
git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}"
|
||||
git push
|
||||
- name: Create pull request
|
||||
id: create-pr
|
||||
run: |
|
||||
printf "Created by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" > pr_body.md
|
||||
PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md)
|
||||
echo "FLATHUB_PR_URL=$PR_URL" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
SLACK_ICON: ''
|
||||
SLACK_ICON_EMOJI: ':bot:'
|
||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||
SLACK_TITLE: "Flathub release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created."
|
||||
SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.FLATHUB_PR_URL }}|PR> on how to proceed."
|
||||
SLACK_FOOTER: ''
|
||||
MSG_MINIMAL: true
|
||||
201
.github/workflows/linux-makepkg.yml
vendored
Normal file
201
.github/workflows/linux-makepkg.yml
vendored
Normal file
@@ -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
|
||||
107
.github/workflows/mac-dmg-x64.yml
vendored
107
.github/workflows/mac-dmg-x64.yml
vendored
@@ -9,13 +9,45 @@ name: Build macOS .dmg for x64
|
||||
#######################################
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
- cron: '0 20 20 * *'
|
||||
workflow_call:
|
||||
inputs:
|
||||
semVerNum:
|
||||
type: string
|
||||
description: 'The Major.Minor.Patch part of the version'
|
||||
required: true
|
||||
revisionNum:
|
||||
type: string
|
||||
description: 'The revision number'
|
||||
required: true
|
||||
semVerSuffix:
|
||||
type: string
|
||||
description: 'The suffix of the version, including dash'
|
||||
required: true
|
||||
notarize:
|
||||
description: 'Notarize'
|
||||
default: true
|
||||
type: boolean
|
||||
upload-to-draft:
|
||||
type: boolean
|
||||
default: true
|
||||
outputs:
|
||||
sha256-dmg:
|
||||
description: "SHA256 sum of the x64 dmg"
|
||||
value: ${{ jobs.build.outputs.sha256sum}}
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
semVerNum:
|
||||
description: 'The Major.Minor.Patch part of the version'
|
||||
required: false
|
||||
revisionNum:
|
||||
description: 'The revision number'
|
||||
required: false
|
||||
semVerSuffix:
|
||||
description: 'The suffix of the version, including dash'
|
||||
required: false
|
||||
default: '-SNAPSHOT'
|
||||
notarize:
|
||||
description: 'Notarize'
|
||||
required: true
|
||||
@@ -25,17 +57,17 @@ on:
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
|
||||
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
|
||||
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
|
||||
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
uses: ./.github/workflows/get-version.yml
|
||||
with:
|
||||
version: ${{ inputs.version }}
|
||||
|
||||
build-arm:
|
||||
build:
|
||||
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [get-version]
|
||||
outputs:
|
||||
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -59,7 +91,7 @@ jobs:
|
||||
- name: Download OpenJFX jmods
|
||||
id: download-jmods
|
||||
run: |
|
||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||
curl --silent --fail-with-body --proto "=https" -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
|
||||
mkdir -p openjfx-jmods/
|
||||
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||
@@ -77,7 +109,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
run : mvn versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pmac -DskipTests
|
||||
- name: Patch target dir
|
||||
@@ -117,8 +149,8 @@ jobs:
|
||||
--dest appdir
|
||||
--name Cryptomator
|
||||
--vendor "Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
||||
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||
--app-version "${VERSION_NUM}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
||||
--java-options "-Xss5m"
|
||||
@@ -127,7 +159,7 @@ jobs:
|
||||
--java-options "-Djava.net.useSystemProxies=true"
|
||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||
--java-options "-Dsun.java2d.metal=true"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||
@@ -137,34 +169,20 @@ jobs:
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NUM}\""
|
||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||
--mac-package-identifier org.cryptomator
|
||||
--resource-dir dist/mac/resources
|
||||
- name: Patch Cryptomator.app
|
||||
run: |
|
||||
mv appdir/Cryptomator.app Cryptomator.app
|
||||
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
|
||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
|
||||
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
|
||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NUM}|g" Cryptomator.app/Contents/Info.plist
|
||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist
|
||||
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
||||
env:
|
||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||
- name: Build and install DockTilePlugin
|
||||
env:
|
||||
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
|
||||
run: |
|
||||
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
|
||||
-scheme DockTilePlugin \
|
||||
-configuration Release \
|
||||
-destination "platform=macOS,arch=x86_64" \
|
||||
-derivedDataPath ${DERIVED_DATA_PATH} \
|
||||
-quiet \
|
||||
clean build
|
||||
mkdir -p Cryptomator.app/Contents/PlugIns
|
||||
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
|
||||
rm -rf ${DERIVED_DATA_PATH}
|
||||
- name: Generate license for dmg
|
||||
run: >
|
||||
mvn -B license:add-third-party
|
||||
@@ -252,16 +270,14 @@ jobs:
|
||||
--eula "dist/mac/dmg/resources/license.rtf"
|
||||
--icon ".background" 128 758
|
||||
--icon ".VolumeIcon.icns" 512 758
|
||||
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
||||
env:
|
||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
|
||||
- name: Codesign .dmg
|
||||
run: |
|
||||
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
||||
env:
|
||||
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||
- name: Notarize .dmg
|
||||
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
|
||||
if: inputs.notarize || github.event_name == 'schedule'
|
||||
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
||||
with:
|
||||
app-path: 'Cryptomator-*.dmg'
|
||||
@@ -269,8 +285,12 @@ jobs:
|
||||
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||
xcode-path: '/Applications/Xcode_16.app'
|
||||
- id: sha256sum
|
||||
run: |
|
||||
read -ra CMD_OUTPUT < <(shasum -a256 Cryptomator-*.dmg)
|
||||
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||
- name: Add possible alpha/beta tags to installer name
|
||||
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
|
||||
run: mv Cryptomator-*.dmg "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg"
|
||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||
run: |
|
||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||
@@ -283,7 +303,7 @@ jobs:
|
||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||
continue-on-error: true
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: dmg-${{ matrix.output-suffix }}
|
||||
path: |
|
||||
@@ -291,9 +311,10 @@ jobs:
|
||||
Cryptomator-*.asc
|
||||
if-no-files-found: error
|
||||
- name: Publish dmg on GitHub Releases
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
if: inputs.upload-to-draft
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
draft: true
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
files: |
|
||||
|
||||
105
.github/workflows/mac-dmg.yml
vendored
105
.github/workflows/mac-dmg.yml
vendored
@@ -1,13 +1,45 @@
|
||||
name: Build macOS .dmg for arm64
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
- cron: '0 20 20 * *'
|
||||
workflow_call:
|
||||
inputs:
|
||||
semVerNum:
|
||||
type: string
|
||||
description: 'The Major.Minor.Patch part of the version'
|
||||
required: true
|
||||
revisionNum:
|
||||
type: string
|
||||
description: 'The revision number'
|
||||
required: true
|
||||
semVerSuffix:
|
||||
type: string
|
||||
description: 'The suffix of the version, including dash'
|
||||
required: true
|
||||
notarize:
|
||||
description: 'Notarize'
|
||||
default: true
|
||||
type: boolean
|
||||
upload-to-draft:
|
||||
type: boolean
|
||||
default: true
|
||||
outputs:
|
||||
sha256-dmg:
|
||||
description: "SHA256 sum of the arm64 dmg"
|
||||
value: ${{ jobs.build.outputs.sha256sum}}
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
semVerNum:
|
||||
description: 'The Major.Minor.Patch part of the version'
|
||||
required: false
|
||||
revisionNum:
|
||||
description: 'The revision number'
|
||||
required: false
|
||||
semVerSuffix:
|
||||
description: 'The suffix of the version, including dash'
|
||||
required: false
|
||||
default: '-SNAPSHOT'
|
||||
notarize:
|
||||
description: 'Notarize'
|
||||
required: true
|
||||
@@ -23,17 +55,17 @@ on:
|
||||
env:
|
||||
JAVA_DIST: 'temurin'
|
||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
||||
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
|
||||
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
|
||||
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
|
||||
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
uses: ./.github/workflows/get-version.yml
|
||||
with:
|
||||
version: ${{ inputs.version }}
|
||||
|
||||
build:
|
||||
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [get-version]
|
||||
outputs:
|
||||
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -57,7 +89,7 @@ jobs:
|
||||
- name: Download OpenJFX jmods
|
||||
id: download-jmods
|
||||
run: |
|
||||
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||
curl --silent --fail-with-body --proto "=https" -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
|
||||
mkdir -p openjfx-jmods/
|
||||
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||
@@ -75,7 +107,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Set version
|
||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
run : mvn versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pmac -DskipTests
|
||||
- name: Patch target dir
|
||||
@@ -115,8 +147,8 @@ jobs:
|
||||
--dest appdir
|
||||
--name Cryptomator
|
||||
--vendor "Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
||||
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||
--app-version "${VERSION_NUM}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
||||
--java-options "-Xss5m"
|
||||
@@ -125,7 +157,7 @@ jobs:
|
||||
--java-options "-Djava.net.useSystemProxies=true"
|
||||
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||
--java-options "-Dsun.java2d.metal=true"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||
@@ -135,35 +167,21 @@ jobs:
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NUM}\""
|
||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||
--mac-package-identifier org.cryptomator
|
||||
--resource-dir dist/mac/resources
|
||||
- name: Patch Cryptomator.app
|
||||
run: |
|
||||
mv appdir/Cryptomator.app Cryptomator.app
|
||||
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
|
||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
|
||||
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
|
||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NUM}|g" Cryptomator.app/Contents/Info.plist
|
||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist
|
||||
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
||||
env:
|
||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||
- name: Build and install DockTilePlugin
|
||||
env:
|
||||
DERIVED_DATA_PATH: dist/mac/DockTilePlugin/build
|
||||
run: |
|
||||
xcodebuild -project dist/mac/DockTilePlugin/DockTilePlugin.xcodeproj \
|
||||
-scheme DockTilePlugin \
|
||||
-configuration Release \
|
||||
-destination "platform=macOS,arch=arm64" \
|
||||
-derivedDataPath ${DERIVED_DATA_PATH} \
|
||||
-quiet \
|
||||
clean build
|
||||
mkdir -p Cryptomator.app/Contents/PlugIns
|
||||
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin Cryptomator.app/Contents/PlugIns/
|
||||
rm -rf ${DERIVED_DATA_PATH}
|
||||
- name: Generate license for dmg
|
||||
run: >
|
||||
mvn -B license:add-third-party
|
||||
@@ -251,16 +269,14 @@ jobs:
|
||||
--eula "dist/mac/dmg/resources/license.rtf"
|
||||
--icon ".background" 128 758
|
||||
--icon ".VolumeIcon.icns" 512 758
|
||||
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
||||
env:
|
||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
|
||||
- name: Codesign .dmg
|
||||
run: |
|
||||
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
||||
env:
|
||||
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||
- name: Notarize .dmg
|
||||
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
|
||||
if: inputs.notarize || github.event_name == 'schedule'
|
||||
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
||||
with:
|
||||
app-path: 'Cryptomator-*.dmg'
|
||||
@@ -268,8 +284,12 @@ jobs:
|
||||
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||
xcode-path: '/Applications/Xcode_16.app'
|
||||
- id: sha256sum
|
||||
run: |
|
||||
read -ra CMD_OUTPUT < <(shasum -a256 Cryptomator-*.dmg)
|
||||
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||
- name: Add possible alpha/beta tags to installer name
|
||||
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
|
||||
run: mv Cryptomator-*.dmg "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg"
|
||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||
run: |
|
||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||
@@ -282,7 +302,7 @@ jobs:
|
||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||
continue-on-error: true
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: dmg-${{ matrix.output-suffix }}
|
||||
path: |
|
||||
@@ -290,9 +310,10 @@ jobs:
|
||||
Cryptomator-*.asc
|
||||
if-no-files-found: error
|
||||
- name: Publish dmg on GitHub Releases
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
if: inputs.upload-to-draft
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
draft: true
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
files: |
|
||||
|
||||
4
.github/workflows/no-response.yml
vendored
4
.github/workflows/no-response.yml
vendored
@@ -7,12 +7,12 @@ on:
|
||||
|
||||
jobs:
|
||||
no-response:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
days-before-stale: 14
|
||||
days-before-close: 0
|
||||
|
||||
154
.github/workflows/post-publish.yml
vendored
154
.github/workflows/post-publish.yml
vendored
@@ -5,35 +5,141 @@ on:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
runs-on: ubuntu-latest
|
||||
notify:
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Download source tarball
|
||||
run: |
|
||||
curl -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
|
||||
- name: Notify about DEB build
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
SLACK_ICON: false
|
||||
SLACK_ICON: ''
|
||||
SLACK_ICON_EMOJI: ':bot:'
|
||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||
SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
|
||||
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/debian.yml|build deb Package>."
|
||||
SLACK_FOOTER: false
|
||||
MSG_MINIMAL: true
|
||||
SLACK_FOOTER: ''
|
||||
MSG_MINIMAL: true
|
||||
- name: Notify about latest-version update
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
SLACK_ICON: ''
|
||||
SLACK_ICON_EMOJI: ':bot:'
|
||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||
SLACK_TITLE: "Requiring version check source update for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}."
|
||||
SLACK_MESSAGE: 'Check S3 bucket for <https://static.cryptomator.org/desktop/latest-version.json|latest-version.json>.'
|
||||
SLACK_FOOTER: ''
|
||||
MSG_MINIMAL: true
|
||||
|
||||
get-asset-urls:
|
||||
name: Get release asset URLs
|
||||
runs-on: ubuntu-slim
|
||||
outputs:
|
||||
is-windows-release: ${{ steps.urls.outputs.urls-present }}
|
||||
msi-url: ${{ steps.urls.outputs.msi }}
|
||||
exe-url: ${{ steps.urls.outputs.exe }}
|
||||
steps:
|
||||
- name: Extract MSI and EXE download URLs
|
||||
id: urls
|
||||
run: |
|
||||
MSI_URL=$(jq -r '[.[] | select(.name | endswith("-x64.msi"))][0].browser_download_url // "null"' <<< "$RELEASE_ASSETS")
|
||||
EXE_URL=$(jq -r '[.[] | select(.name | endswith("-x64.exe"))][0].browser_download_url // "null"' <<< "$RELEASE_ASSETS")
|
||||
if [[ "$MSI_URL" == "null" || -z "$MSI_URL" || "$EXE_URL" == "null" || -z "$EXE_URL" ]]; then
|
||||
echo "urls-present=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "urls-present=true" >> $GITHUB_OUTPUT
|
||||
echo "msi=${MSI_URL}" >> $GITHUB_OUTPUT
|
||||
echo "exe=${EXE_URL}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
RELEASE_ASSETS: ${{ toJson(github.event.release.assets) }}
|
||||
|
||||
allowlist-msi-x64:
|
||||
needs: [get-asset-urls]
|
||||
if: needs.get-asset-urls.outputs.is-windows-release == 'true'
|
||||
uses: ./.github/workflows/av-whitelist.yml
|
||||
with:
|
||||
url: ${{ needs.get-asset-urls.outputs.msi-url }}
|
||||
secrets: inherit
|
||||
|
||||
allowlist-exe-x64:
|
||||
needs: [get-asset-urls, allowlist-msi-x64]
|
||||
if: needs.get-asset-urls.outputs.is-windows-release == 'true'
|
||||
uses: ./.github/workflows/av-whitelist.yml
|
||||
with:
|
||||
url: ${{ needs.get-asset-urls.outputs.exe-url }}
|
||||
secrets: inherit
|
||||
|
||||
check-release:
|
||||
name: Analyzes the release for certain properties
|
||||
runs-on: ubuntu-slim
|
||||
outputs:
|
||||
release-kind: ${{steps.determine-kind.outputs.value}} # Possible values are [alpha, beta, rc, stable, unknown]
|
||||
steps:
|
||||
- id: determine-kind
|
||||
run: |
|
||||
SEM_VER_NUM=$(echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
|
||||
SEM_VER_SUFFIX="${SEM_VER_STR#"$SEM_VER_NUM"}"
|
||||
|
||||
TYPE="unknown"
|
||||
if [[ -z $SEM_VER_SUFFIX ]]; then
|
||||
TYPE="stable"
|
||||
elif [[ $SEM_VER_SUFFIX =~ -alpha[1-9]+$ ]]; then
|
||||
TYPE="alpha"
|
||||
elif [[ $SEM_VER_SUFFIX =~ -beta[1-9]+$ ]]; then
|
||||
TYPE="beta"
|
||||
elif [[ $SEM_VER_SUFFIX =~ -rc[1-9]+$ ]]; then
|
||||
TYPE="rc"
|
||||
fi
|
||||
echo "value=${TYPE}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
SEM_VER_STR: ${{ github.event.release.tag_name }}
|
||||
|
||||
|
||||
notify-winget:
|
||||
name: Notify for winget-release
|
||||
if: needs.get-asset-urls.outputs.is-windows-release == 'true' && needs.check-release.outputs.release-kind == 'stable'
|
||||
needs: [check-release, get-asset-urls]
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
SLACK_ICON: ''
|
||||
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) }} }'
|
||||
|
||||
|
||||
3
.github/workflows/release-check.yml
vendored
3
.github/workflows/release-check.yml
vendored
@@ -43,13 +43,14 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file
|
||||
if: ${{ ! (contains(github.event.head_commit.message, '[skip metadata check]') || contains(github.event.head_commit.message, '[metadata check skip]')) }}
|
||||
run: |
|
||||
if ! grep -q "<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"
|
||||
exit 1
|
||||
fi
|
||||
- name: Cache NVD DB
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
||||
key: dependency-check-${{ github.run_id }}
|
||||
|
||||
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@@ -7,12 +7,12 @@ on:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
days-before-stale: 365
|
||||
days-before-close: 90
|
||||
|
||||
202
.github/workflows/win-exe.yml
vendored
202
.github/workflows/win-exe.yml
vendored
@@ -1,13 +1,48 @@
|
||||
name: Build Windows Installer
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
- cron: '0 19 20 * *'
|
||||
workflow_call:
|
||||
inputs:
|
||||
semVerNum:
|
||||
type: string
|
||||
description: 'The Major.Minor.Patch part of the version'
|
||||
required: true
|
||||
revisionNum:
|
||||
type: string
|
||||
description: 'The revision number'
|
||||
required: true
|
||||
semVerSuffix:
|
||||
type: string
|
||||
description: 'The suffix of the version, including dash'
|
||||
required: true
|
||||
sign:
|
||||
description: 'Sign binaries'
|
||||
default: true
|
||||
type: boolean
|
||||
upload-to-draft:
|
||||
type: boolean
|
||||
default: true
|
||||
outputs:
|
||||
sha256-msi:
|
||||
description: "SHA256 sum of the x64 msi"
|
||||
value: ${{ jobs.build-msi.outputs.sha256sum}}
|
||||
sha256-exe:
|
||||
description: "SHA256 sum of the x64 exe"
|
||||
value: ${{ jobs.build-exe.outputs.sha256sum}}
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
semVerNum:
|
||||
description: 'The Major.Minor.Patch part of the version'
|
||||
required: false
|
||||
revisionNum:
|
||||
description: 'The revision number'
|
||||
required: false
|
||||
semVerSuffix:
|
||||
description: 'The suffix of the version, including dash'
|
||||
required: false
|
||||
default: '-SNAPSHOT'
|
||||
sign:
|
||||
description: 'Sign binaries'
|
||||
required: false
|
||||
@@ -22,6 +57,9 @@ on:
|
||||
|
||||
|
||||
env:
|
||||
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
|
||||
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
|
||||
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
|
||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_windows-x64_bin-jmods.zip'
|
||||
OPENJFX_JMODS_AMD64_HASH: '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3'
|
||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
||||
@@ -34,15 +72,11 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
uses: ./.github/workflows/get-version.yml
|
||||
with:
|
||||
version: ${{ inputs.version }}
|
||||
|
||||
build-msi:
|
||||
name: Build .msi Installer
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [ get-version ]
|
||||
outputs:
|
||||
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -72,7 +106,7 @@ jobs:
|
||||
if: matrix.arch == 'x64'
|
||||
#In the last step we move all jmods files a dir level up because jmods are placed inside a directory in the zip
|
||||
run: |
|
||||
curl --output openjfx-jmods.zip -L "${{ env.OPENJFX_JMODS_AMD64 }}"
|
||||
curl --silent --fail-with-body --proto "=https" -L "${{ env.OPENJFX_JMODS_AMD64 }}" --output openjfx-jmods.zip
|
||||
if(!(Get-FileHash -Path openjfx-jmods.zip -Algorithm SHA256).Hash.ToLower().equals("${{ env.OPENJFX_JMODS_AMD64_HASH }}")) {
|
||||
throw "Wrong checksum of JMOD archive downloaded from ${{ env.OPENJFX_JMODS_AMD64 }}.";
|
||||
}
|
||||
@@ -94,7 +128,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Set version
|
||||
run: mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||
run: mvn versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
|
||||
- name: Run maven
|
||||
run: mvn -B clean package -Pwin -DskipTests
|
||||
- name: Patch target dir
|
||||
@@ -134,13 +168,13 @@ jobs:
|
||||
--dest appdir
|
||||
--name Cryptomator
|
||||
--vendor "Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||
--app-version "${VERSION_NUM}.${REVISION_NUM}"
|
||||
--java-options "--enable-preview"
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
|
||||
--java-options "-Xss5m"
|
||||
--java-options "-Xmx256m"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
|
||||
--java-options "-Dfile.encoding=\"utf-8\""
|
||||
--java-options "-Djava.net.useSystemProxies=true"
|
||||
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
|
||||
@@ -151,12 +185,13 @@ jobs:
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
|
||||
--java-options "-Dcryptomator.loopbackAlias=\"cryptomator-vault\""
|
||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
||||
--java-options "-Dcryptomator.buildNumber=\"msi-${REVISION_NUM}\""
|
||||
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
|
||||
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json\""
|
||||
--java-options "-Dcryptomator.disableUpdateCheck=false"
|
||||
--java-options "-XX:ErrorFile=C:/cryptomator/cryptomator_crash.log"
|
||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||
--resource-dir dist/win/resources
|
||||
--icon dist/win/resources/Cryptomator.ico
|
||||
--add-launcher "Cryptomator (Debug)=dist/win/debug-launcher.properties"
|
||||
@@ -192,7 +227,7 @@ jobs:
|
||||
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
|
||||
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
|
||||
- name: Sign DLLs with Azure Trusted Signing
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
if: inputs.sign || github.event_name == 'schedule'
|
||||
uses: ./.github/actions/win-sign-action
|
||||
with:
|
||||
base-dir: ${{ github.workspace }}\appdir
|
||||
@@ -201,17 +236,6 @@ jobs:
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
- name: Sign DLLs with Actalis CodeSigner
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
||||
with:
|
||||
base-dir: 'appdir'
|
||||
file-extensions: 'dll,exe,ps1'
|
||||
recursive: true
|
||||
sign-description: 'Cryptomator'
|
||||
sign-url: 'https://cryptomator.org'
|
||||
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
|
||||
password: ${{ secrets.WIN_CODESIGN_PW }}
|
||||
- name: Replace DLLs inside jars with signed ones
|
||||
shell: pwsh
|
||||
run: |
|
||||
@@ -247,8 +271,8 @@ jobs:
|
||||
--dest installer
|
||||
--name Cryptomator
|
||||
--vendor "Skymatic GmbH"
|
||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
||||
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||
--app-version "${VERSION_NUM}.${REVISION_NUM}"
|
||||
--win-menu
|
||||
--win-dir-chooser
|
||||
--win-shortcut-prompt
|
||||
@@ -261,7 +285,7 @@ jobs:
|
||||
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
|
||||
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
|
||||
- name: Sign MSI with Azure Trusted Signing
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
if: inputs.sign || github.event_name == 'schedule'
|
||||
uses: ./.github/actions/win-sign-action
|
||||
with:
|
||||
base-dir: ${{ github.workspace }}\installer
|
||||
@@ -270,8 +294,12 @@ jobs:
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
- id: sha256sum
|
||||
run: |
|
||||
read -ra CMD_OUTPUT < <(sha256sum installer/Cryptomator-*.msi)
|
||||
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||
- name: Add possible alpha/beta tags and architecture to installer name
|
||||
run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.arch }}.msi
|
||||
run: mv installer/Cryptomator-*.msi "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.msi"
|
||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||
run: |
|
||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||
@@ -280,7 +308,7 @@ jobs:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: msi-${{ matrix.arch }}
|
||||
path: |
|
||||
@@ -291,7 +319,9 @@ jobs:
|
||||
build-exe:
|
||||
name: Build .exe installer
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [ get-version, build-msi ]
|
||||
needs: [ build-msi ]
|
||||
outputs:
|
||||
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -311,7 +341,7 @@ jobs:
|
||||
env:
|
||||
WIX_VERSION: ${{ env.WIX_VERSION }}
|
||||
- name: Download .msi
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: msi-${{ matrix.arch }}
|
||||
path: dist/win/bundle/resources
|
||||
@@ -338,38 +368,39 @@ jobs:
|
||||
shell: pwsh
|
||||
- name: Download WinFsp
|
||||
run: |
|
||||
curl --output $env:WINFSP_PATH -L ${{ env.WINFSP_MSI }}
|
||||
$computedHash = (Get-FileHash -Path $env:WINFSP_PATH -Algorithm SHA256).Hash.ToLower()
|
||||
if ($computedHash -ne "${{ env.WINFSP_MSI_HASH }}") {
|
||||
throw "Checksum mismatch for $env:WINFSP_PATH (expected ${{ env.WINFSP_MSI_HASH }}, got $computedHash)."
|
||||
curl --silent --fail-with-body --proto "=https" -L "$env:WINFSP_MSI" --output $env:WINFSP_PATH
|
||||
$computedHash = (Get-FileHash -Path "$env:WINFSP_PATH" -Algorithm SHA256).Hash.ToLower()
|
||||
if ($computedHash -ne "$env:WINFSP_MSI_HASH") {
|
||||
throw "Checksum mismatch for ${env:WINFSP_PATH} (expected ${env:WINFSP_MSI_HASH}, got $computedHash)."
|
||||
}
|
||||
env:
|
||||
WINFSP_PATH: 'dist/win/bundle/resources/winfsp.msi'
|
||||
shell: pwsh
|
||||
- name: Download Legacy-WinFsp uninstaller
|
||||
run: |
|
||||
curl --output dist/win/bundle/resources/winfsp-uninstaller.exe -L ${{ env.WINFSP_UNINSTALLER }}
|
||||
curl --silent --fail-with-body --proto "=https" -L ${{ env.WINFSP_UNINSTALLER }} --output dist/win/bundle/resources/winfsp-uninstaller.exe
|
||||
shell: pwsh
|
||||
- name: Create Wix Burn bundle
|
||||
working-directory: dist/win
|
||||
run: >
|
||||
wix build
|
||||
-define BundleName="Cryptomator"
|
||||
-define BundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
||||
-define BundleVersion="${VERSION_NUM}.${REVISION_NUM}"
|
||||
-define BundleVendor="Skymatic GmbH"
|
||||
-define BundleCopyright="(C) 2016 - 2025 Skymatic GmbH"
|
||||
-define BundleCopyright="(C) 2016 - 2026 Skymatic GmbH"
|
||||
-define AboutUrl="https://cryptomator.org"
|
||||
-define HelpUrl="https://cryptomator.org/contact"
|
||||
-define UpdateUrl="https://cryptomator.org/downloads/"
|
||||
-ext "WixToolset.Util.wixext"
|
||||
-ext "WixToolset.BootstrapperApplications.wixext"
|
||||
./bundle/bundleWithWinfsp.wxs
|
||||
-out "../../installer/unsigned/Cryptomator-Installer.exe"
|
||||
-out "../../installer/Cryptomator-Installer.exe"
|
||||
- name: Detach burn engine in preparation to sign
|
||||
if: inputs.sign || github.event_name == 'schedule'
|
||||
run: >
|
||||
wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe
|
||||
wix burn detach installer/Cryptomator-Installer.exe -engine tmp/engine.exe
|
||||
- name: Sign WiX burn engine with Azure Trusted Signing
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
if: inputs.sign || github.event_name == 'schedule'
|
||||
uses: ./.github/actions/win-sign-action
|
||||
with:
|
||||
base-dir: ${{ github.workspace }}\tmp
|
||||
@@ -379,21 +410,14 @@ jobs:
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
- name: Sign burn engine with Actalis CodeSigner
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
||||
with:
|
||||
base-dir: 'tmp'
|
||||
file-extensions: 'exe'
|
||||
sign-description: 'Cryptomator Bundle Installer'
|
||||
sign-url: 'https://cryptomator.org'
|
||||
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
|
||||
password: ${{ secrets.WIN_CODESIGN_PW }}
|
||||
- name: Reattach signed burn engine to installer
|
||||
run: >
|
||||
wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
|
||||
if: inputs.sign || github.event_name == 'schedule'
|
||||
shell: pwsh
|
||||
run: |
|
||||
Move-Item -Path installer/Cryptomator-Installer.exe -Destination tmp/Cryptomator-Installer.exe
|
||||
wix burn reattach tmp/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
|
||||
- name: Sign EXE installer with Azure Trusted Signing
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
if: inputs.sign || github.event_name == 'schedule'
|
||||
uses: ./.github/actions/win-sign-action
|
||||
with:
|
||||
base-dir: ${{ github.workspace }}\installer
|
||||
@@ -403,18 +427,12 @@ jobs:
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
- name: Sign installer with Actalis CodeSigner
|
||||
if: inputs.sign || github.event_name == 'release'
|
||||
uses: skymatic/workflows/.github/actions/win-sign-action@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # no specific version
|
||||
with:
|
||||
base-dir: 'installer'
|
||||
file-extensions: 'exe'
|
||||
sign-description: 'Cryptomator Bundle Installer'
|
||||
sign-url: 'https://cryptomator.org'
|
||||
username: ${{ secrets.WIN_CODESIGN_USERNAME }}
|
||||
password: ${{ secrets.WIN_CODESIGN_PW }}
|
||||
- id: sha256sum
|
||||
run: |
|
||||
read -ra CMD_OUTPUT < <(sha256sum installer/Cryptomator-*.exe)
|
||||
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||
- name: Add possible alpha/beta tags to installer name
|
||||
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.executable-suffix }}.exe
|
||||
run: mv installer/Cryptomator-Installer.exe "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.executable-suffix }}.exe"
|
||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||
run: |
|
||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||
@@ -423,7 +441,7 @@ jobs:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: exe-${{ matrix.executable-suffix }}
|
||||
path: |
|
||||
@@ -433,58 +451,22 @@ jobs:
|
||||
|
||||
publish:
|
||||
name: Publish installers to the github release
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||
if: inputs.upload-to-draft
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ build-msi, build-exe ]
|
||||
outputs:
|
||||
download-url-msi-x64: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }}
|
||||
download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }}
|
||||
steps:
|
||||
- name: Download installers
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
merge-multiple: true
|
||||
- name: Publish installers on GitHub Releases
|
||||
id: publish
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||
with:
|
||||
draft: true
|
||||
fail_on_unmatched_files: true
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
# do not change ordering of filelist, required for correct job output
|
||||
files: |
|
||||
*x64.msi
|
||||
*x64.exe
|
||||
*.asc
|
||||
|
||||
allowlist-msi-x64:
|
||||
uses: ./.github/workflows/av-whitelist.yml
|
||||
needs: [ publish ]
|
||||
with:
|
||||
url: ${{ needs.publish.outputs.download-url-msi-x64 }}
|
||||
secrets: inherit
|
||||
|
||||
allowlist-exe-x64:
|
||||
uses: ./.github/workflows/av-whitelist.yml
|
||||
needs: [ publish, allowlist-msi-x64 ]
|
||||
with:
|
||||
url: ${{ needs.publish.outputs.download-url-exe-x64 }}
|
||||
secrets: inherit
|
||||
|
||||
notify-winget:
|
||||
name: Notify for winget-release
|
||||
if: needs.get-version.outputs.versionType == 'stable'
|
||||
needs: [publish, get-version]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_USERNAME: 'Cryptobot'
|
||||
SLACK_ICON: false
|
||||
SLACK_ICON_EMOJI: ':bot:'
|
||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||
SLACK_TITLE: "MSI packages of ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
|
||||
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/winget.yml| release them to winget>."
|
||||
SLACK_FOOTER: false
|
||||
MSG_MINIMAL: true
|
||||
|
||||
2
.github/workflows/winget.yml
vendored
2
.github/workflows/winget.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||
- name: Submit package
|
||||
uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # no_specific_version
|
||||
uses: vedantmgoyal2009/winget-releaser@7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 # no_specific_version
|
||||
with:
|
||||
identifier: Cryptomator.Cryptomator
|
||||
version: ${{ inputs.tag }}
|
||||
|
||||
2
.idea/runConfigurations/Cryptomator_Linux.xml
generated
2
.idea/runConfigurations/Cryptomator_Linux.xml
generated
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{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">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{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">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
2
.idea/runConfigurations/Cryptomator_Windows.xml
generated
2
.idea/runConfigurations/Cryptomator_Windows.xml
generated
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{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">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{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">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
@@ -5,7 +5,7 @@
|
||||
</envs>
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{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">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</envs>
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{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">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
58
CHANGELOG.md
58
CHANGELOG.md
@@ -7,7 +7,45 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
The changelog starts with version 1.19.0.
|
||||
Changes to prior versions can be found on the [Github release page](https://github.com/cryptomator/cryptomator/releases).
|
||||
|
||||
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.18.0...HEAD)
|
||||
|
||||
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.19.2...HEAD)
|
||||
|
||||
### Changed
|
||||
* Refactored release pipeline to allow immutable releases ([#4205](https://github.com/cryptomator/cryptomator/pull/4205))
|
||||
|
||||
|
||||
## [1.19.2](https://github.com/cryptomator/cryptomator/releases/1.19.2) - 2026-03-20
|
||||
|
||||
### Security
|
||||
* Cryptomamtor Hub Vaults: Additional patch for (#4179, [GHSA-34rf-rwr3-7g43](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-34rf-rwr3-7g43))
|
||||
|
||||
|
||||
## [1.19.1](https://github.com/cryptomator/cryptomator/releases/1.19.1) - 2026-03-12
|
||||
|
||||
### Security
|
||||
* Cryptomamtor Hub Vaults: Fixed possible man-in-the-middle attack with tampered vault config (#4179, [GHSA-34rf-rwr3-7g43](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-34rf-rwr3-7g43))
|
||||
* Disallow unencrypted http connections to hub by default ([CVE-2026-32309](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-vv33-h7qx-c264))
|
||||
* Disallow loading of masterkey file from arbitrary paths (#4180, [CVE-2026-32310](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-5phc-5pfx-hr52))
|
||||
* Fixed not-configured plugin directory does not disable plugin search ([#4176](https://github.com/cryptomator/cryptomator/pull/4176))
|
||||
|
||||
### Added
|
||||
* Trust on first use, adding new config properties `cryptomator.hub.allowedHosts` and `cryptomator.hub.enableTrustOnFirstUse` (#4179)
|
||||
|
||||
### Fixed
|
||||
* Fixed Finder window opens twice when revealing vault on macOS ([#4177](https://github.com/cryptomator/cryptomator/pull/4177))
|
||||
* Fixed app does not start due to secret service detection failure on Linux ([#4175](https://github.com/cryptomator/cryptomator/pull/4175))
|
||||
|
||||
### Changed
|
||||
* Pin version of appimagetool([#4181](https://github.com/cryptomator/cryptomator/pull/4181))
|
||||
* Updated translations
|
||||
* Updated dependencies:
|
||||
* `org.cryptomator:integrations-api` from 1.8.0-beta1 to 1.8.0
|
||||
* `org.cryptomator:integrations-linux` from 1.7.0-beta4 to 1.7.0
|
||||
* `org.cryptomator:integrations-mac` from 1.5.0-beta3 to 1.5.0
|
||||
|
||||
|
||||
|
||||
## [1.19.0](https://github.com/cryptomator/cryptomator/releases/tag/1.19.0) - 2026-03-09
|
||||
|
||||
### Added
|
||||
* Self-Update Mechanism ([#3948](https://github.com/cryptomator/cryptomator/pull/3948))
|
||||
@@ -20,31 +58,33 @@ Changes to prior versions can be found on the [Github release page](https://gith
|
||||
* Support automatic app theme selection according to OS theme on Linux ([#4027](https://github.com/cryptomator/cryptomator/issues/4027))
|
||||
* Admin configuration: Allow overwriting certain app properties by external config file ([#4105](https://github.com/cryptomator/cryptomator/pull/4105))
|
||||
* New keychain backend using [secret service API](https://specifications.freedesktop.org/secret-service/0.2) for Linux ([#4025](https://github.com/cryptomator/cryptomator/pull/4025))
|
||||
* Liquid Glass icon for macOS ([#4166](https://github.com/cryptomator/cryptomator/pull/4166))
|
||||
|
||||
### Fixed
|
||||
* Fixed password reset/show recovery possible for vaults without masterkey file ([#4120](https://github.com/cryptomator/cryptomator/pull/4120))
|
||||
* Fixed restore vault config failed due to selecting a directory instead of file ([#4141](https://github.com/cryptomator/cryptomator/issues/4141))
|
||||
* Fixed leaking of cleartext paths into application log ([GHSA-j83j-mwhc-rcgw](https://github.com/cryptomator/cryptomator/security/advisories/GHSA-j83j-mwhc-rcgw))
|
||||
|
||||
### Changed
|
||||
* Disable user defined app start config on Windows ([#4132](https://github.com/cryptomator/cryptomator/issues/4132))
|
||||
* Disable plugin loading by default ([#4136](https://github.com/cryptomator/cryptomator/4136))
|
||||
* Use JDK 25 ([#4031](https://github.com/cryptomator/cryptomator/pull/4031))
|
||||
* Update JavaFX to 25.0.2 ([#4145](https://github.com/cryptomator/cryptomator/pull/4145)))
|
||||
* Update JavaFX to 25.0.2 ([#4145](https://github.com/cryptomator/cryptomator/pull/4145))
|
||||
* Updated translations
|
||||
* Updated dependencies
|
||||
* `ch.qos.logback:*` from 1.5.19 to 1.5.31
|
||||
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.0
|
||||
* `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.0
|
||||
* `ch.qos.logback:*` from 1.5.19 to 1.5.32
|
||||
* `com.fasterxml.jackson.core:jackson-databind` from 2.20.0 to 2.21.1
|
||||
* `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.20.0 to 2.21.1
|
||||
* `com.github.ben-manes.caffeine:caffeine` from 3.2.2 to 3.2.3
|
||||
* `com.google.dagger:*` from 2.57.2 to 2.59.1
|
||||
* `com.google.dagger:*` from 2.57.2 to 2.59.2
|
||||
* `org.apache.commons:commons-lang3` from 3.19.0 to 3.20.0
|
||||
* `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0-beta3
|
||||
* `org.cryptomator:cryptofs` from 2.9.0 to 2.10.0
|
||||
* `org.cryptomator:cryptolib` from 2.2.1 to 2.2.2
|
||||
* `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.0
|
||||
* `org.cryptomator:fuse-nio-adapter` from 5.1.0 to 6.0.1
|
||||
* `org.cryptomator:integrations-api` from 1.7.0 to 1.8.0-beta1
|
||||
* `org.cryptomator:integrations-linux` from 1.6.1 to 1.7.0-beta4
|
||||
* `org.cryptomator:integrations-mac` from 1.4.1 to 1.5.0-beta3
|
||||
* `org.cryptomator:integrations-win` from 1.5.1 to 1.6.0
|
||||
* `org.cryptomator:webdav-nio-adapter` from 3.0.0 to 3.0.1
|
||||
|
||||
* `org.cryptomator:webdav-nio-adapter-servlet` to 1.2.12
|
||||
|
||||
|
||||
5
dist/linux/appimage/build.sh
vendored
5
dist/linux/appimage/build.sh
vendored
@@ -82,7 +82,7 @@ ${JAVA_HOME}/bin/jpackage \
|
||||
--vendor "Skymatic GmbH" \
|
||||
--java-options "--enable-preview" \
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
||||
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
|
||||
--copyright "(C) 2016 - 2026 Skymatic GmbH" \
|
||||
--java-options "-Xss5m" \
|
||||
--java-options "-Xmx256m" \
|
||||
--app-version "${VERSION}.${REVISION_NO}" \
|
||||
@@ -99,6 +99,7 @@ ${JAVA_HOME}/bin/jpackage \
|
||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
|
||||
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
|
||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log" \
|
||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
|
||||
--resource-dir ../resources
|
||||
|
||||
# transform AppDir
|
||||
@@ -123,7 +124,7 @@ ln -s org.cryptomator.Cryptomator.metainfo.xml Cryptomator.AppDir/usr/share/meta
|
||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||
|
||||
# load AppImageTool
|
||||
curl -L https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
|
||||
curl -L https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
|
||||
chmod +x /tmp/appimagetool.AppImage
|
||||
|
||||
# create AppImage
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
<url type="faq">https://community.cryptomator.org/c/kb/faq</url>
|
||||
<url type="help">https://docs.cryptomator.org/</url>
|
||||
<url type="translate">https://translate.cryptomator.org</url>
|
||||
<url type="vcs-browser">https://github.com/cryptomator/cryptomator</url>
|
||||
|
||||
<developer id="de.skymatic">
|
||||
<name>Skymatic GmbH</name>
|
||||
@@ -83,6 +84,15 @@
|
||||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<release date="2026-03-20" version="1.19.2">
|
||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.2</url>
|
||||
</release>
|
||||
<release date="2026-03-12" version="1.19.1">
|
||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.1</url>
|
||||
</release>
|
||||
<release date="2026-03-09" version="1.19.0">
|
||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.0</url>
|
||||
</release>
|
||||
<release date="2025-11-12" version="1.18.0">
|
||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.18.0</url>
|
||||
</release>
|
||||
|
||||
3
dist/linux/debian/rules
vendored
3
dist/linux/debian/rules
vendored
@@ -46,7 +46,7 @@ override_dh_auto_build:
|
||||
--vendor "Skymatic GmbH" \
|
||||
--java-options "--enable-preview" \
|
||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
||||
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
|
||||
--copyright "(C) 2016 - 2026 Skymatic GmbH" \
|
||||
--java-options "-Xss5m" \
|
||||
--java-options "-Xmx256m" \
|
||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||
@@ -64,6 +64,7 @@ override_dh_auto_build:
|
||||
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
|
||||
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
|
||||
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\"" \
|
||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
|
||||
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
||||
--resource-dir resources \
|
||||
--verbose
|
||||
|
||||
15
dist/linux/flatpak/build-aux/fusermount-wrapper.sh
vendored
Normal file
15
dist/linux/flatpak/build-aux/fusermount-wrapper.sh
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
# From: https://gitlab.gnome.org/GNOME/gnome-builder/-/blob/main/build-aux/flatpak/fusermount-wrapper.sh
|
||||
|
||||
if [ -z "$_FUSE_COMMFD" ]; then
|
||||
FD_ARGS=
|
||||
else
|
||||
FD_ARGS="--env=_FUSE_COMMFD=${_FUSE_COMMFD} --forward-fd=${_FUSE_COMMFD}"
|
||||
fi
|
||||
|
||||
if [ -e /proc/self/fd/3 ] && [ 3 != "$_FUSE_COMMFD" ]; then
|
||||
FD_ARGS="$FD_ARGS --forward-fd=3"
|
||||
fi
|
||||
|
||||
exec flatpak-spawn --host --forward-fd=1 --forward-fd=2 $FD_ARGS fusermount3 "$@"
|
||||
182
dist/linux/flatpak/org.cryptomator.Cryptomator.TEMPLATE.yaml
vendored
Normal file
182
dist/linux/flatpak/org.cryptomator.Cryptomator.TEMPLATE.yaml
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
app-id: org.cryptomator.Cryptomator
|
||||
command: cryptomator
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '25.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
separate-locales: false
|
||||
finish-args:
|
||||
# Required for FUSE, see https://github.com/flathub/org.cryptomator.Cryptomator/pull/68#issuecomment-1935136502
|
||||
- --device=all
|
||||
# Set the PATH environment variable in the application, as flatpak is resetting the shell's PATH
|
||||
- --env=PATH=/app/bin/:/usr/bin/
|
||||
# Allow filesystem access to the user's home dir
|
||||
# Needed to manage vaults there
|
||||
- --filesystem=home
|
||||
# Reading system certificates
|
||||
- --filesystem=host-etc:ro
|
||||
# Allow access to the XDG data directory
|
||||
# Needed to connect to KeePassXC's UNIX domain socket
|
||||
- --filesystem=xdg-run/org.keepassxc.KeePassXC.BrowserServer
|
||||
- --filesystem=xdg-run/app/org.keepassxc.KeePassXC/
|
||||
# Share IPC namespace with the host, without it the X11 shared memory extension will not work
|
||||
- --share=ipc
|
||||
# Allow access to the network
|
||||
- --share=network
|
||||
# Show windows using X11
|
||||
- --socket=x11
|
||||
# Needed to reveal encrypted files
|
||||
- --talk-name=org.freedesktop.FileManager1
|
||||
# Run any command on the host
|
||||
# Needed to spawn fusermount on the host
|
||||
- --talk-name=org.freedesktop.Flatpak
|
||||
# Allow desktop notifications
|
||||
- --talk-name=org.freedesktop.Notifications
|
||||
# Allow access to the GNOME secret service API and to talk to the GNOME keyring daemon
|
||||
- --talk-name=org.freedesktop.secrets
|
||||
- --talk-name=org.gnome.keyring
|
||||
# Allow to talk to the KDE kwallet daemon
|
||||
- --talk-name=org.kde.kwalletd5
|
||||
- --talk-name=org.kde.kwalletd6
|
||||
# Needed to talk to the gvfs daemons over D-Bus and list mounts using the GIO APIs
|
||||
- --talk-name=org.gtk.vfs.*
|
||||
# Allow access to appindicator icons
|
||||
- --talk-name=org.ayatana
|
||||
# Allow access to appindicator icons on KDE
|
||||
- --talk-name=org.kde.StatusNotifierWatcher
|
||||
cleanup:
|
||||
- /include
|
||||
- /lib/pkgconfig
|
||||
modules:
|
||||
- shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json
|
||||
- name: libfuse
|
||||
buildsystem: meson
|
||||
config-opts:
|
||||
- -Dexamples=false
|
||||
- -Dinitscriptdir=
|
||||
- -Duseroot=false
|
||||
- -Dtests=false
|
||||
# don't install rules on the host
|
||||
- -Dudevrulesdir=/tmp/
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/libfuse/libfuse/releases/download/fuse-3.16.2/fuse-3.16.2.tar.gz
|
||||
sha256: f797055d9296b275e981f5f62d4e32e089614fc253d1ef2985851025b8a0ce87
|
||||
x-checker-data:
|
||||
type: anitya
|
||||
project-id: 861
|
||||
url-template: https://github.com/libfuse/libfuse/releases/download/fuse-$version/fuse-$version.tar.gz
|
||||
versions: {<: '3.17.0'}
|
||||
- name: host-command-wrapper
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- install fusermount-wrapper.sh /app/bin/fusermount3
|
||||
sources:
|
||||
- type: file
|
||||
path: build-aux/fusermount-wrapper.sh
|
||||
- name: cryptomator
|
||||
buildsystem: simple
|
||||
build-options:
|
||||
build-args:
|
||||
- --share=network
|
||||
env:
|
||||
PATH: /app/bin:/usr/bin
|
||||
MAVEN_OPTS: -Dmaven.repo.local=.m2/repository
|
||||
JAVA_HOME: jdk
|
||||
JMODS_PATH: jmods
|
||||
VERSION: $FLATPAK_VERSION
|
||||
REVISION_NO: '$FLATPAK_REVISION'
|
||||
build-commands:
|
||||
# Setup Java
|
||||
- tar xvfz jdk.tar.gz --transform 's!^[^/]*!jdk!'
|
||||
- mkdir jmods
|
||||
- unzip -j openjfx.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d jmods
|
||||
# Setup Maven
|
||||
- mkdir maven
|
||||
- tar xf maven.tar.gz --strip-components=1 --exclude=jansi-native --directory=maven
|
||||
# Build project
|
||||
- maven/bin/mvn clean package -DskipTests -P"linux-$(uname -m)"
|
||||
- cp target/cryptomator-*.jar target/mods
|
||||
- cd target
|
||||
- $JAVA_HOME/bin/jlink
|
||||
--output runtime
|
||||
--module-path $JMODS_PATH
|
||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
|
||||
--no-header-files
|
||||
--no-man-pages
|
||||
--strip-debug
|
||||
--compress=zip-0
|
||||
- $JAVA_HOME/bin/jpackage
|
||||
--type app-image
|
||||
--runtime-image runtime
|
||||
--input target/libs
|
||||
--module-path target/mods
|
||||
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
|
||||
--dest .
|
||||
--name Cryptomator
|
||||
--vendor 'Skymatic GmbH'
|
||||
--copyright '(C) 2016 - 2026 Skymatic GmbH'
|
||||
--java-options '--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator'
|
||||
--java-options "--sun-misc-unsafe-memory-access=allow"
|
||||
--java-options '-Xss5m'
|
||||
--java-options '-Xmx256m'
|
||||
--java-options '-Dfile.encoding='utf-8''
|
||||
--java-options '-Djava.net.useSystemProxies=true'
|
||||
--java-options "-Dcryptomator.appVersion='${VERSION}'"
|
||||
--java-options "-Dcryptomator.buildNumber='flatpak-${REVISION_NO}'"
|
||||
--java-options '-Dcryptomator.ipcSocketPath='@{userhome}/.config/Cryptomator/ipc.socket''
|
||||
--java-options '-Dcryptomator.adminConfigPath='/run/host/etc/cryptomator/config.properties''
|
||||
--java-options '-Dcryptomator.logDir='@{userhome}/.local/share/Cryptomator/logs''
|
||||
--java-options '-Dcryptomator.mountPointsDir='@{userhome}/.local/share/Cryptomator/mnt''
|
||||
--java-options '-Dcryptomator.pluginDir='@{userhome}/.local/share/Cryptomator/plugins''
|
||||
--java-options '-Dcryptomator.p12Path='@{userhome}/.config/Cryptomator/key.p12''
|
||||
--java-options '-Dcryptomator.settingsPath='@{userhome}/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json''
|
||||
--java-options '-Dcryptomator.showTrayIcon=true'
|
||||
--java-options '-Dcryptomator.updateMechanism=org.cryptomator.linux.update.FlatpakUpdater'
|
||||
--java-options '-Dcryptomator.networking.truststore.p12Path='/run/host/etc/cryptomator/certs.p12''
|
||||
--java-options '-Dcryptomator.hub.enableTrustOnFirstUse=true'
|
||||
--app-version "${VERSION}.${REVISION_NO}"
|
||||
--verbose
|
||||
- cp -R Cryptomator /app/
|
||||
- ln -s /app/Cryptomator/bin/Cryptomator /app/bin/cryptomator
|
||||
- cp -R /app/lib/* /app/Cryptomator/lib/app/
|
||||
- install -D -m0644 -t /app/share/applications/ dist/linux/common/org.cryptomator.Cryptomator.desktop
|
||||
- install -D -m0644 -t /app/share/icons/hicolor/scalable/apps/ dist/linux/common/org.cryptomator.Cryptomator.svg
|
||||
- install -D -m0644 -T dist/linux/common/org.cryptomator.Cryptomator.tray.svg /app/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-symbolic.svg
|
||||
- install -D -m0644 -T dist/linux/common/org.cryptomator.Cryptomator.tray-unlocked.svg /app/share/icons/hicolor/symbolic/apps/org.cryptomator.Cryptomator.tray-unlocked-symbolic.svg
|
||||
- install -D -m0644 -t /app/share/metainfo/ dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml
|
||||
sources:
|
||||
- $CRYPTOMATOR_SOURCE
|
||||
- type: file
|
||||
dest-filename: jdk.tar.gz
|
||||
only-arches:
|
||||
- x86_64
|
||||
url: https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.2%2B10/OpenJDK25U-jdk_x64_linux_hotspot_25.0.2_10.tar.gz
|
||||
sha512: 29043fde119a031c2ca8d57aed445fedd9e7f74608fcdc7a809076ba84cfd1c31f08de2ecccf352e159fdcd1cae172395ed46363007552ff242057826c81ab3a
|
||||
- type: file
|
||||
dest-filename: jdk.tar.gz
|
||||
only-arches:
|
||||
- aarch64
|
||||
url: https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.2%2B10/OpenJDK25U-jdk_aarch64_linux_hotspot_25.0.2_10.tar.gz
|
||||
sha512: f1d3ccec3e1f1bed9d632f14b9223709d6e5c2e0d922125d068870dd3016492a2ca8f08924d4a9d0dc5eb2159fa09efee366a748fd0093475baf29e5c70c781a
|
||||
- type: file
|
||||
dest-filename: openjfx.zip
|
||||
only-arches:
|
||||
- x86_64
|
||||
url: https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip
|
||||
sha512: 21f550217101c513f9eb1d7947eba30cb79618238e6539ce770e54e84b01574cdaeba40af602391145f163dd8e43e3794395467413152f13ffffeff948b0ca1b
|
||||
- type: file
|
||||
dest-filename: openjfx.zip
|
||||
only-arches:
|
||||
- aarch64
|
||||
url: https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip
|
||||
sha512: a9268409b3803e386490bf1319d0f0a14173cebe862c12254cd51b430ee0a297437d9e38d5ebeae0da8899be898b312b103330d09dcfd3e63c1e7d15f2f14311
|
||||
- type: file
|
||||
dest-filename: maven.tar.gz
|
||||
url: https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.9.13/apache-maven-3.9.13-bin.tar.gz
|
||||
sha512: d9ccd44ba2991586e359c29eb86780ae8ff4ec1b88b0b8af3af074803472690cf2017782a9c4401343c62cbcd056231db9612e1e551cbd9747c21746d732c015
|
||||
x-checker-data:
|
||||
type: anitya
|
||||
project-id: 1894
|
||||
stable-only: true
|
||||
url-template: https://repo1.maven.org/maven2/org/apache/maven/apache-maven/$version/apache-maven-$version-bin.tar.gz
|
||||
versions: {<: '4.0'}
|
||||
119
dist/linux/makepkg/PKGBUILD.template
vendored
Normal file
119
dist/linux/makepkg/PKGBUILD.template
vendored
Normal file
@@ -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
dist/mac/.gitignore
vendored
1
dist/mac/.gitignore
vendored
@@ -1,2 +1 @@
|
||||
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 */;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -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>
|
||||
17
dist/mac/dmg/build.sh
vendored
17
dist/mac/dmg/build.sh
vendored
@@ -24,7 +24,7 @@ rm -rf runtime dmg *.app *.dmg
|
||||
# set variables
|
||||
APP_NAME="Cryptomator"
|
||||
VENDOR="Skymatic GmbH"
|
||||
COPYRIGHT_YEARS="2016 - 2025"
|
||||
COPYRIGHT_YEARS="2016 - 2026"
|
||||
PACKAGE_IDENTIFIER="org.cryptomator"
|
||||
MAIN_JAR_GLOB="cryptomator-*.jar"
|
||||
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||
@@ -125,28 +125,17 @@ ${JAVA_HOME}/bin/jpackage \
|
||||
--java-options "-Dcryptomator.showTrayIcon=true" \
|
||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
|
||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true" \
|
||||
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
|
||||
--resource-dir ../resources
|
||||
|
||||
# transform app dir
|
||||
cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/
|
||||
cp ../resources/Assets.car ${APP_NAME}.app/Contents/Resources/
|
||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
||||
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
|
||||
|
||||
# build and install dock tile plugin
|
||||
echo "Building and installing Cryptomator.docktileplugin..."
|
||||
DERIVED_DATA_PATH=../DockTilePlugin/build
|
||||
xcodebuild -project ../DockTilePlugin/DockTilePlugin.xcodeproj \
|
||||
-scheme DockTilePlugin \
|
||||
-configuration Release \
|
||||
-derivedDataPath ${DERIVED_DATA_PATH} \
|
||||
-quiet \
|
||||
clean build
|
||||
mkdir -p ${APP_NAME}.app/Contents/PlugIns
|
||||
cp -R ${DERIVED_DATA_PATH}/Build/Products/Release/Cryptomator.docktileplugin ${APP_NAME}.app/Contents/PlugIns/
|
||||
rm -rf ${DERIVED_DATA_PATH}
|
||||
|
||||
# generate license
|
||||
mvn -B -f../../../pom.xml license:add-third-party \
|
||||
-Dlicense.thirdPartyFilename=license.rtf \
|
||||
|
||||
BIN
dist/mac/resources/Assets.car
vendored
Normal file
BIN
dist/mac/resources/Assets.car
vendored
Normal file
Binary file not shown.
5
dist/mac/resources/Info.plist
vendored
5
dist/mac/resources/Info.plist
vendored
@@ -12,6 +12,8 @@
|
||||
<string>Cryptomator</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Cryptomator.icns</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>Cryptomator</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.cryptomator</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
@@ -117,8 +119,5 @@
|
||||
<!-- allow utilization of integrated GPU, see https://developer.apple.com/library/mac/qa/qa1734/_index.html -->
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<!-- register dock tile plugin -->
|
||||
<key>NSDockTilePlugIn</key>
|
||||
<string>Cryptomator.docktileplugin</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
1
dist/win/build.ps1
vendored
1
dist/win/build.ps1
vendored
@@ -167,6 +167,7 @@ $javaOptions = @(
|
||||
"--java-options", "-Dcryptomator.showTrayIcon=true"
|
||||
"--java-options", "-Dcryptomator.buildNumber=`"msi-$revisionNo`""
|
||||
"--java-options", "-Dcryptomator.disableUpdateCheck=false"
|
||||
"--java-options", "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||
)
|
||||
|
||||
|
||||
|
||||
2
dist/win/resources/main.wxs
vendored
2
dist/win/resources/main.wxs
vendored
@@ -109,7 +109,7 @@
|
||||
</ns0:CreateFolder>
|
||||
</ns0:Component>
|
||||
<ns0:Component Id="AdminConfigFile" NeverOverwrite="yes" Permanent="yes">
|
||||
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\cryptomator.config" Name="cryptomator.config" KeyPath="yes">
|
||||
<ns0:File Id="EmptyAdminConfig" Source="$(env.JP_WIXWIZARD_RESOURCES)\..\..\common\config.properties" Name="config.properties" KeyPath="yes">
|
||||
<util:PermissionEx User="SYSTEM" GenericAll="yes"/>
|
||||
<util:PermissionEx User="Administrators" GenericAll="yes"/>
|
||||
<util:PermissionEx User="Users" GenericRead="yes" GenericExecute="yes"/>
|
||||
|
||||
96
pom.xml
96
pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptomator</artifactId>
|
||||
<version>1.19.0</version>
|
||||
<version>1.20.0-SNAPSHOT</version>
|
||||
<name>Cryptomator Desktop App</name>
|
||||
|
||||
<organization>
|
||||
@@ -33,45 +33,46 @@
|
||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.10.0-beta3</cryptomator.cryptofs.version>
|
||||
<cryptomator.cryptofs.version>2.10.0</cryptomator.cryptofs.version>
|
||||
<cryptomator.cryptolib.version>2.2.2</cryptomator.cryptolib.version>
|
||||
<cryptomator.integrations.version>1.8.0-beta1</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.version>1.8.0</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.6.0</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.5.0-beta3</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.7.0-beta4</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>6.0.0</cryptomator.fuse.version>
|
||||
<cryptomator.integrations.mac.version>1.5.0</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.7.0</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>6.0.1</cryptomator.fuse.version>
|
||||
<cryptomator.webdav.version>3.0.1</cryptomator.webdav.version>
|
||||
<cryptomator.webdav-servlet.version>1.2.12</cryptomator.webdav-servlet.version>
|
||||
|
||||
<!-- 3rd party dependencies -->
|
||||
<caffeine.version>3.2.3</caffeine.version>
|
||||
<commons-lang3.version>3.20.0</commons-lang3.version>
|
||||
<dagger.version>2.59.1</dagger.version>
|
||||
<dagger.version>2.59.2</dagger.version>
|
||||
<easybind.version>2.2</easybind.version>
|
||||
<jackson.version>2.21.0</jackson.version>
|
||||
<jackson.version>2.21.1</jackson.version>
|
||||
<javafx.version>25.0.2</javafx.version>
|
||||
<jwt.version>4.5.0</jwt.version>
|
||||
<jwt.version>4.5.1</jwt.version>
|
||||
<nimbus-jose.version>10.5</nimbus-jose.version>
|
||||
<logback.version>1.5.31</logback.version>
|
||||
<logback.version>1.5.32</logback.version>
|
||||
<slf4j.version>2.0.17</slf4j.version>
|
||||
<tinyoauth2.version>0.8.1</tinyoauth2.version>
|
||||
<zxcvbn.version>1.9.0</zxcvbn.version>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<junit.jupiter.version>5.13.4</junit.jupiter.version>
|
||||
<mockito.version>5.20.0</mockito.version>
|
||||
<junit.jupiter.version>6.0.3</junit.jupiter.version>
|
||||
<mockito.version>5.22.0</mockito.version>
|
||||
<hamcrest.version>3.0</hamcrest.version>
|
||||
|
||||
<!-- build-time dependencies -->
|
||||
<jetbrains.annotations.version>26.0.2-1</jetbrains.annotations.version>
|
||||
<dependency-check.version>12.1.5</dependency-check.version>
|
||||
<jetbrains.annotations.version>26.1.0</jetbrains.annotations.version>
|
||||
<dependency-check.version>12.2.0</dependency-check.version>
|
||||
<jacoco.version>0.8.14</jacoco.version>
|
||||
<license-generator.version>2.7.0</license-generator.version>
|
||||
<junit-tree-reporter.version>1.4.0</junit-tree-reporter.version>
|
||||
<mvn-compiler.version>3.14.1</mvn-compiler.version>
|
||||
<mvn-resources.version>3.3.1</mvn-resources.version>
|
||||
<mvn-dependency.version>3.8.1</mvn-dependency.version>
|
||||
<mvn-surefire.version>3.5.4</mvn-surefire.version>
|
||||
<mvn-jar.version>3.4.2</mvn-jar.version>
|
||||
<license-generator.version>2.7.1</license-generator.version>
|
||||
<junit-tree-reporter.version>1.5.1</junit-tree-reporter.version>
|
||||
<mvn-compiler.version>3.15.0</mvn-compiler.version>
|
||||
<mvn-resources.version>3.5.0</mvn-resources.version>
|
||||
<mvn-dependency.version>3.10.0</mvn-dependency.version>
|
||||
<mvn-surefire.version>3.5.3</mvn-surefire.version>
|
||||
<mvn-jar.version>3.5.0</mvn-jar.version>
|
||||
|
||||
<!-- Property used by surefire to determine jacoco engine -->
|
||||
<surefire.jacoco.args></surefire.jacoco.args>
|
||||
@@ -93,6 +94,11 @@
|
||||
|
||||
<dependencies>
|
||||
<!-- Cryptomator Libs -->
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>webdav-nio-adapter-servlet</artifactId>
|
||||
<version>${cryptomator.webdav-servlet.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptolib</artifactId>
|
||||
@@ -521,11 +527,57 @@
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>linux</id>
|
||||
<id>linux-aarch64</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>unix</family>
|
||||
<name>Linux</name>
|
||||
<arch>aarch64</arch>
|
||||
</os>
|
||||
<property>
|
||||
<name>idea.version</name>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>integrations-linux</artifactId>
|
||||
<version>${cryptomator.integrations.linux.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-base</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
<classifier>linux-aarch64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-graphics</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
<classifier>linux-aarch64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-controls</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
<classifier>linux-aarch64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-fxml</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
<classifier>linux-aarch64</classifier>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>linux-x86_64</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>unix</family>
|
||||
<name>Linux</name>
|
||||
<arch>amd64</arch>
|
||||
</os>
|
||||
<property>
|
||||
<name>idea.version</name>
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.cryptomator.common.locationpresets.OneDriveLinuxLocationPresetsProvid
|
||||
import org.cryptomator.common.locationpresets.OneDriveMacLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.OneDriveWindowsLocationPresetsProvider;
|
||||
import org.cryptomator.common.locationpresets.PCloudLocationPresetsProvider;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||
@@ -20,6 +21,7 @@ import org.cryptomator.networking.SSLContextProvider;
|
||||
import org.cryptomator.networking.SSLContextWithMacKeychain;
|
||||
import org.cryptomator.networking.SSLContextWithPKCS12TrustStore;
|
||||
import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
||||
import org.cryptomator.ui.fxapp.JfxRevealPathService;
|
||||
import org.cryptomator.ui.fxapp.JfxUiAppearanceProvider;
|
||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||
|
||||
@@ -64,6 +66,7 @@ open module org.cryptomator.desktop {
|
||||
uses org.cryptomator.event.NotificationHandler;
|
||||
|
||||
provides UiAppearanceProvider with JfxUiAppearanceProvider;
|
||||
provides RevealPathService with JfxRevealPathService;
|
||||
provides TrayMenuController with AwtTrayMenuController;
|
||||
provides Configurator with LogbackConfiguratorFactory;
|
||||
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
|
||||
|
||||
@@ -22,8 +22,6 @@ import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
@@ -76,8 +74,8 @@ public abstract class CommonsModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<RevealPathService> provideRevealPathService() {
|
||||
return RevealPathService.get().findFirst();
|
||||
static RevealPathService provideRevealPathService() {
|
||||
return RevealPathService.get().findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,10 +9,13 @@ import org.slf4j.LoggerFactory;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
@@ -20,20 +23,22 @@ public class Environment {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Environment.class);
|
||||
private static final int DEFAULT_MIN_PW_LENGTH = 8;
|
||||
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
||||
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
||||
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
||||
private static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
|
||||
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
||||
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
||||
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
||||
private static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
|
||||
private static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
|
||||
private static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
|
||||
private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
|
||||
private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
|
||||
private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
|
||||
private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
|
||||
public static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
||||
public static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
||||
public static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
||||
public static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
|
||||
public static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
||||
public static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
||||
public static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
||||
public static final String MOUNTPOINT_DIR_PROP_NAME = "cryptomator.mountPointsDir";
|
||||
public static final String MIN_PW_LENGTH_PROP_NAME = "cryptomator.minPwLength";
|
||||
public static final String APP_VERSION_PROP_NAME = "cryptomator.appVersion";
|
||||
public static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber";
|
||||
public static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir";
|
||||
public static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon";
|
||||
public static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck";
|
||||
public static final String HUB_ALLOWED_HOSTS_PROP_NAME = "cryptomator.hub.allowedHosts";
|
||||
public static final String HUB_TOFU_PROP_NAME = "cryptomator.hub.enableTrustOnFirstUse";
|
||||
|
||||
private Environment() {}
|
||||
|
||||
@@ -57,6 +62,8 @@ public class Environment {
|
||||
logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME);
|
||||
logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME);
|
||||
logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME);
|
||||
logCryptomatorSystemProperty(HUB_ALLOWED_HOSTS_PROP_NAME);
|
||||
logCryptomatorSystemProperty(HUB_TOFU_PROP_NAME);
|
||||
}
|
||||
|
||||
public static Environment getInstance() {
|
||||
@@ -145,6 +152,18 @@ public class Environment {
|
||||
return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME);
|
||||
}
|
||||
|
||||
public Set<String> hubAllowedHosts() {
|
||||
var allowedHubHostsString = System.getProperty(HUB_ALLOWED_HOSTS_PROP_NAME, "");
|
||||
return Arrays.stream(allowedHubHostsString.split(","))
|
||||
.map(String::trim)
|
||||
.filter(Predicate.not(String::isEmpty))
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
public boolean hubTrustOnFirstUse() {
|
||||
return Boolean.getBoolean(HUB_TOFU_PROP_NAME);
|
||||
}
|
||||
|
||||
private Optional<Path> getPath(String propertyName) {
|
||||
String value = System.getProperty(propertyName);
|
||||
return Optional.ofNullable(value).map(Paths::get);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.cryptomator.common;
|
||||
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@@ -13,10 +13,12 @@ public class SubstitutingProperties extends PropertiesDecorator {
|
||||
private static final Pattern TEMPLATE = Pattern.compile("@\\{(\\w+)}");
|
||||
|
||||
private final Map<String, String> env;
|
||||
private final Logger logger;
|
||||
|
||||
public SubstitutingProperties(Properties props, Map<String, String> systemEnvironment) {
|
||||
public SubstitutingProperties(Properties props, Map<String, String> systemEnvironment, Logger logger) {
|
||||
super(props);
|
||||
this.env = systemEnvironment;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +46,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
|
||||
case "localappdata" -> resolveFrom("LOCALAPPDATA", Source.ENV);
|
||||
case "userhome" -> resolveFrom("user.home", Source.PROPS);
|
||||
default -> {
|
||||
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Unknown variable {} in property value {}.", match.group(), value);
|
||||
logger.warn("Unknown variable {} in property value {}.", match.group(), value);
|
||||
yield match.group();
|
||||
}
|
||||
});
|
||||
@@ -56,7 +58,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
|
||||
case PROPS -> delegate.getProperty(key);
|
||||
};
|
||||
if (val == null) {
|
||||
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
|
||||
logger.warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
|
||||
return "";
|
||||
} else {
|
||||
return Matcher.quoteReplacement(val);
|
||||
|
||||
@@ -24,9 +24,12 @@ import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.geometry.NodeOrientation;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Settings {
|
||||
|
||||
@@ -78,6 +81,7 @@ public class Settings {
|
||||
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
|
||||
public final ObjectProperty<Path> previouslyUsedVaultDirectory;
|
||||
public final StringProperty lastUpdateAttemptedByVersion;
|
||||
public final ObservableSet<String> trustedHosts;
|
||||
|
||||
public static Settings create(SettingsProvider provider, Environment env) {
|
||||
var defaults = new SettingsJson();
|
||||
@@ -118,6 +122,7 @@ public class Settings {
|
||||
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
|
||||
this.previouslyUsedVaultDirectory = new SimpleObjectProperty<>(this, "previouslyUsedVaultDirectory", json.previouslyUsedVaultDirectory);
|
||||
this.lastUpdateAttemptedByVersion = new SimpleStringProperty(this, "lastUpdateAttemptedByVersion", json.lastUpdateAttemptedByVersion);
|
||||
this.trustedHosts = FXCollections.observableSet(json.trustedHosts);
|
||||
|
||||
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
|
||||
|
||||
@@ -149,6 +154,7 @@ public class Settings {
|
||||
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
|
||||
previouslyUsedVaultDirectory.addListener(this::somethingChanged);
|
||||
lastUpdateAttemptedByVersion.addListener(this::somethingChanged);
|
||||
trustedHosts.addListener(this::somethingChanged);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@@ -207,6 +213,7 @@ public class Settings {
|
||||
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
|
||||
json.previouslyUsedVaultDirectory = previouslyUsedVaultDirectory.get();
|
||||
json.lastUpdateAttemptedByVersion = lastUpdateAttemptedByVersion.get();
|
||||
json.trustedHosts = Set.copyOf(trustedHosts);
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,23 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||
import com.fasterxml.jackson.annotation.Nulls;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
class SettingsJson {
|
||||
|
||||
@JsonProperty("directories")
|
||||
List<VaultSettingsJson> directories = List.of();
|
||||
@JsonSetter(nulls = Nulls.AS_EMPTY)
|
||||
List<VaultSettingsJson> directories = new ArrayList<>();
|
||||
|
||||
@JsonProperty("writtenByVersion")
|
||||
String writtenByVersion;
|
||||
@@ -99,4 +105,8 @@ class SettingsJson {
|
||||
|
||||
@JsonProperty("lastUpdateAttemptedByVersion")
|
||||
String lastUpdateAttemptedByVersion;
|
||||
|
||||
@JsonProperty("trustedHosts")
|
||||
@JsonSetter(nulls = Nulls.AS_EMPTY)
|
||||
Set<String> trustedHosts = new HashSet<>();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import java.util.Set;
|
||||
* <li>cryptomator.p12Path</li>
|
||||
* <li>cryptomator.mountPointsDir</li>
|
||||
* <li>cryptomator.disableUpdateCheck</li>
|
||||
* <li>cryptomator.hub.allowedHosts</li>
|
||||
* <li>cryptomator.hub.enableTrustOnFirstUse</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see Properties
|
||||
@@ -42,7 +44,9 @@ class AdminPropertiesFactory {
|
||||
"cryptomator.pluginDir", //
|
||||
"cryptomator.p12Path", //
|
||||
"cryptomator.mountPointsDir", //
|
||||
"cryptomator.disableUpdateCheck");
|
||||
"cryptomator.disableUpdateCheck", //
|
||||
"cryptomator.hub.allowedHosts", //
|
||||
"cryptomator.hub.enableTrustOnFirstUse");
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,7 @@ public class Cryptomator {
|
||||
|
||||
static {
|
||||
var adminProps = AdminPropertiesFactory.create();
|
||||
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv());
|
||||
var lazyProcessedProps = new SubstitutingProperties(adminProps, System.getenv(), EventualLogger.INSTANCE);
|
||||
System.setProperties(lazyProcessedProps);
|
||||
CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
|
||||
LOG = LoggerFactory.getLogger(Cryptomator.class);
|
||||
@@ -65,7 +65,6 @@ public class Cryptomator {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
EventualLogger.INSTANCE.drainTo(LOG);
|
||||
var printVersion = Optional.ofNullable(args) //
|
||||
.stream() //Streams either one element (the args-array) or zero elements
|
||||
.flatMap(Arrays::stream) //
|
||||
@@ -91,10 +90,11 @@ public class Cryptomator {
|
||||
* @return Nonzero exit code in case of an error.
|
||||
*/
|
||||
private int run(String[] args) {
|
||||
debugMode.initialize();
|
||||
EventualLogger.INSTANCE.drainTo(LOG);
|
||||
env.log();
|
||||
LOG.debug("Dagger graph initialized after {}ms", System.currentTimeMillis() - STARTUP_TIME);
|
||||
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion(), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
|
||||
debugMode.initialize();
|
||||
supportedLanguages.applyPreferred();
|
||||
changeDefaultSSLContext();
|
||||
/*
|
||||
|
||||
@@ -18,7 +18,7 @@ abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
||||
public SSLContext getContext(SecureRandom csprng) throws SSLContextBuildException {
|
||||
try {
|
||||
KeyStore truststore = getTruststore();
|
||||
truststore.load(null, null);
|
||||
ensureLoaded(truststore);
|
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(truststore);
|
||||
@@ -30,4 +30,13 @@ abstract class SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
||||
throw new SSLContextBuildException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void ensureLoaded(KeyStore truststore) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
|
||||
try {
|
||||
truststore.aliases();
|
||||
} catch (KeyStoreException e) {
|
||||
// Not initialized yet (e.g. custom KeyStore SPI); initialize without replacing preloaded stores.
|
||||
truststore.load(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,73 @@
|
||||
package org.cryptomator.networking;
|
||||
|
||||
import org.cryptomator.common.Nullable;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* SSLContextProvider for Windows using the Windows certificate store as trust store
|
||||
* SSLContextProvider for Windows using the Windows certificate store as trust store and the bundled JDK cacerts as fallback
|
||||
* <p>
|
||||
* In order to work, the jdk.crypto.mscapi jmod is needed
|
||||
*/
|
||||
@OperatingSystem(OperatingSystem.Value.WINDOWS)
|
||||
public class SSLContextWithWindowsCertStore extends SSLContextDifferentTrustStoreBase implements SSLContextProvider {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SSLContextWithWindowsCertStore.class);
|
||||
private static final String DEFAULT_TRUSTSTORE_PASSWORD = "changeit"; //default JDK cacerts password
|
||||
|
||||
@Override
|
||||
KeyStore getTruststore() throws KeyStoreException {
|
||||
return KeyStore.getInstance("WINDOWS-ROOT");
|
||||
KeyStore getTruststore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
|
||||
var windowsKeyStore = KeyStore.getInstance("WINDOWS-ROOT");
|
||||
var jdkKeyStore = getShippedCaCertsStore();
|
||||
if (jdkKeyStore == null) {
|
||||
return windowsKeyStore;
|
||||
}
|
||||
|
||||
ensureLoaded(windowsKeyStore);
|
||||
ensureLoaded(jdkKeyStore);
|
||||
try {
|
||||
CombinedKeyStoreSpi spi = CombinedKeyStoreSpi.create(windowsKeyStore, jdkKeyStore);
|
||||
Provider dummyProvider = new Provider("CombinedKeyStoreProvider", "1.0", "Provides a combined, read-only KeyStore") {};
|
||||
return new KeyStore(spi, dummyProvider, "CombinedKeyStoreProvider") {};
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new KeyStoreException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
KeyStore getShippedCaCertsStore() {
|
||||
return getCaCertsStoreByProperties(System.getProperties());
|
||||
}
|
||||
|
||||
//for testability
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
KeyStore getCaCertsStoreByProperties(Properties props) {
|
||||
var javaHome = Path.of(props.getProperty("java.home"));
|
||||
var trustStorePassword = props.getProperty("javax.net.ssl.trustStorePassword", DEFAULT_TRUSTSTORE_PASSWORD).toCharArray();
|
||||
for (var candidate : List.of(javaHome.resolve("lib/security/cacerts"), javaHome.resolve("conf/security/cacerts"))) {
|
||||
try {
|
||||
if (Files.isRegularFile(candidate)) {
|
||||
return KeyStore.getInstance(candidate.toFile(), trustStorePassword);
|
||||
}
|
||||
} catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException e) {
|
||||
LOG.info("Unable to load fallback cacerts {} file. Skipping fallback.", candidate, e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public enum FxmlFile {
|
||||
HEALTH_START("/fxml/health_start.fxml"), //
|
||||
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
||||
HUB_NO_KEYCHAIN("/fxml/hub_no_keychain.fxml"), //
|
||||
HUB_CHECK_HOST_TRUST("/fxml/hub_check_host_trust.fxml"), //
|
||||
HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), //
|
||||
HUB_INVALID_LICENSE("/fxml/hub_invalid_license.fxml"), //
|
||||
HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), //
|
||||
@@ -29,6 +30,7 @@ public enum FxmlFile {
|
||||
HUB_REGISTER_FAILED("/fxml/hub_register_failed.fxml"), //
|
||||
HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), //
|
||||
HUB_UNAUTHORIZED_DEVICE("/fxml/hub_unauthorized_device.fxml"), //
|
||||
HUB_UNTRUSTED_HOST("/fxml/hub_untrusted_host.fxml"), //
|
||||
HUB_REQUIRE_ACCOUNT_INIT("/fxml/hub_require_account_init.fxml"), //
|
||||
LOCK_FORCED("/fxml/lock_forced.fxml"), //
|
||||
LOCK_FAILED("/fxml/lock_failed.fxml"), //
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.integrations.mount.Mountpoint;
|
||||
import org.cryptomator.integrations.mount.UnmountFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.HostServices;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.IOException;
|
||||
@@ -28,12 +27,12 @@ public class VaultService {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultService.class);
|
||||
|
||||
private final Lazy<Application> application;
|
||||
private final RevealPathService revealPathService;
|
||||
private final ExecutorService executorService;
|
||||
|
||||
@Inject
|
||||
public VaultService(Lazy<Application> application, ExecutorService executorService) {
|
||||
this.application = application;
|
||||
public VaultService(RevealPathService revealPathService, ExecutorService executorService) {
|
||||
this.revealPathService = revealPathService;
|
||||
this.executorService = executorService;
|
||||
}
|
||||
|
||||
@@ -47,9 +46,9 @@ public class VaultService {
|
||||
* @param vault The vault to reveal
|
||||
*/
|
||||
public Task<Vault> createRevealTask(Vault vault) {
|
||||
Task<Vault> task = new RevealVaultTask(vault, application.get().getHostServices());
|
||||
task.setOnSucceeded(evt -> LOG.info("Revealed {}", vault.getDisplayName()));
|
||||
task.setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), evt.getSource().getException()));
|
||||
Task<Vault> task = new RevealVaultTask(vault, revealPathService);
|
||||
task.setOnSucceeded(_ -> LOG.info("Revealed {}", vault.getDisplayName()));
|
||||
task.setOnFailed(evt -> LOG.warn("Failed to reveal {}", vault.getDisplayName(), evt.getSource().getException()));
|
||||
return task;
|
||||
}
|
||||
|
||||
@@ -110,19 +109,18 @@ public class VaultService {
|
||||
private static class RevealVaultTask extends Task<Vault> {
|
||||
|
||||
private final Vault vault;
|
||||
private final HostServices hostServices;
|
||||
private final RevealPathService rs;
|
||||
|
||||
public RevealVaultTask(Vault vault, HostServices hostServices) {
|
||||
public RevealVaultTask(Vault vault, RevealPathService revealPathService) {
|
||||
this.vault = vault;
|
||||
this.hostServices = hostServices;
|
||||
setOnFailed(evt -> LOG.error("Failed to reveal " + vault.getDisplayName(), getException()));
|
||||
this.rs = revealPathService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vault call() {
|
||||
protected Vault call() throws RevealFailedException {
|
||||
switch (vault.getMountPoint()) {
|
||||
case null -> LOG.warn("Not currently mounted");
|
||||
case Mountpoint.WithPath m -> hostServices.showDocument(m.uri().toString());
|
||||
case Mountpoint.WithPath m -> rs.reveal(m.path());
|
||||
case Mountpoint.WithUri m -> LOG.info("Vault mounted at {}", m.uri()); // TODO show in UI?
|
||||
}
|
||||
return vault;
|
||||
|
||||
@@ -58,8 +58,6 @@ public class DecryptFileNamesViewController implements FxController {
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final List<Path> initialList;
|
||||
|
||||
@FXML
|
||||
public TableColumn<CipherAndCleartext, String> ciphertextColumn;
|
||||
@FXML
|
||||
@@ -68,12 +66,11 @@ public class DecryptFileNamesViewController implements FxController {
|
||||
public TableView<CipherAndCleartext> cipherToCleartextTable;
|
||||
|
||||
@Inject
|
||||
public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, @DecryptNameWindow List<Path> pathsToDecrypt, ResourceBundle resourceBundle) {
|
||||
public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, ResourceBundle resourceBundle) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||
this.initialList = pathsToDecrypt;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -97,8 +94,7 @@ public class DecryptFileNamesViewController implements FxController {
|
||||
});
|
||||
cipherToCleartextTable.setOnDragDropped(event -> {
|
||||
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
|
||||
checkAndDecrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
|
||||
cipherToCleartextTable.setItems(mapping);
|
||||
decrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
|
||||
}
|
||||
});
|
||||
cipherToCleartextTable.setOnDragExited(_ -> cipherToCleartextTable.setItems(mapping));
|
||||
@@ -124,9 +120,7 @@ public class DecryptFileNamesViewController implements FxController {
|
||||
});
|
||||
}
|
||||
});
|
||||
if (!initialList.isEmpty()) {
|
||||
checkAndDecrypt(initialList);
|
||||
}
|
||||
window.setOnHidden(_ -> mapping.clear());
|
||||
}
|
||||
|
||||
private void copySingleCelltoClipboard() {
|
||||
@@ -149,10 +143,18 @@ public class DecryptFileNamesViewController implements FxController {
|
||||
fileChooser.setInitialDirectory(vault.getPath().toFile());
|
||||
var ciphertextNodes = fileChooser.showOpenMultipleDialog(window);
|
||||
if (ciphertextNodes != null) {
|
||||
checkAndDecrypt(ciphertextNodes.stream().map(File::toPath).toList());
|
||||
decrypt(ciphertextNodes.stream().map(File::toPath).toList());
|
||||
}
|
||||
}
|
||||
|
||||
public void decrypt(List<Path> pathsToDecrypt) {
|
||||
if (pathsToDecrypt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
checkAndDecrypt(pathsToDecrypt);
|
||||
cipherToCleartextTable.setItems(mapping);
|
||||
}
|
||||
|
||||
private void checkAndDecrypt(List<Path> pathsToDecrypt) {
|
||||
mapping.clear();
|
||||
//Assumption: All files are in the same directory
|
||||
|
||||
@@ -28,23 +28,28 @@ public interface DecryptNameComponent {
|
||||
@FxmlScene(FxmlFile.DECRYPTNAMES)
|
||||
Lazy<Scene> decryptNamesView();
|
||||
|
||||
DecryptFileNamesViewController controller();
|
||||
|
||||
@DecryptNameWindow
|
||||
Vault vault();
|
||||
|
||||
default void showDecryptFileNameWindow() {
|
||||
default void showDecryptFileNameWindow(List<Path> pathsToDecrypt) {
|
||||
Stage s = window();
|
||||
s.setScene(decryptNamesView().get());
|
||||
s.sizeToScene();
|
||||
if (vault().isUnlocked()) {
|
||||
controller().decrypt(pathsToDecrypt);
|
||||
s.show();
|
||||
s.requestFocus();
|
||||
} else {
|
||||
LOG.error("Aborted showing DecryptFileName window: vault state is not {}, but {}.", VaultState.Value.UNLOCKED, vault().getState());
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
|
||||
DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner, @BindsInstance @DecryptNameWindow List<Path> pathsToDecrypt);
|
||||
DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,17 @@ package org.cryptomator.ui.eventview;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||
import org.cryptomator.event.FSEventBucket;
|
||||
import org.cryptomator.event.FSEventBucketContent;
|
||||
import org.cryptomator.event.FileSystemEventAggregator;
|
||||
import org.cryptomator.common.Nullable;
|
||||
import org.cryptomator.common.ObservableUtil;
|
||||
import org.cryptomator.cryptofs.CryptoPath;
|
||||
import org.cryptomator.cryptofs.event.BrokenDirFileEvent;
|
||||
import org.cryptomator.cryptofs.event.BrokenFileNodeEvent;
|
||||
import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent;
|
||||
import org.cryptomator.cryptofs.event.ConflictResolvedEvent;
|
||||
import org.cryptomator.cryptofs.event.DecryptionFailedEvent;
|
||||
import org.cryptomator.cryptofs.event.FileIsInUseEvent;
|
||||
import org.cryptomator.event.FSEventBucket;
|
||||
import org.cryptomator.event.FSEventBucketContent;
|
||||
import org.cryptomator.event.FileSystemEventAggregator;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
@@ -46,7 +45,6 @@ import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -57,7 +55,6 @@ public class EventListCellController implements FxController {
|
||||
private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withZone(ZoneId.systemDefault());
|
||||
|
||||
private final FileSystemEventAggregator fileSystemEventAggregator;
|
||||
@Nullable
|
||||
private final RevealPathService revealService;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final ObjectProperty<Map.Entry<FSEventBucket, FSEventBucketContent>> eventEntry;
|
||||
@@ -82,15 +79,17 @@ public class EventListCellController implements FxController {
|
||||
Button eventActionsButton;
|
||||
|
||||
@Inject
|
||||
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator, Optional<RevealPathService> revealService, ResourceBundle resourceBundle) {
|
||||
public EventListCellController(FileSystemEventAggregator fileSystemEventAggregator,
|
||||
RevealPathService revealService,
|
||||
ResourceBundle resourceBundle) {
|
||||
this.fileSystemEventAggregator = fileSystemEventAggregator;
|
||||
this.revealService = revealService.orElseGet(() -> null);
|
||||
this.revealService = revealService;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.eventEntry = new SimpleObjectProperty<>(null);
|
||||
this.eventMessage = new SimpleStringProperty();
|
||||
this.eventDescription = new SimpleStringProperty();
|
||||
this.eventIcon = new SimpleObjectProperty<>();
|
||||
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1? "" : "("+ e.getValue().count() +")", "");
|
||||
this.eventCount = ObservableUtil.mapWithDefault(eventEntry, e -> e.getValue().count() == 1 ? "" : "(" + e.getValue().count() + ")", "");
|
||||
this.vaultUnlocked = ObservableUtil.mapWithDefault(eventEntry.flatMap(e -> e.getKey().vault().unlockedProperty()), Function.identity(), false);
|
||||
this.readableTime = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_TIME_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
|
||||
this.readableDate = ObservableUtil.mapWithDefault(eventEntry, e -> LOCAL_DATE_FORMATTER.format(e.getValue().mostRecentEvent().getTimestamp()), "");
|
||||
@@ -136,13 +135,8 @@ public class EventListCellController implements FxController {
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.inUse.message"));
|
||||
var indexFileName = fiiue.cleartextPath().lastIndexOf("/");
|
||||
eventDescription.setValue(fiiue.cleartextPath().substring(indexFileName + 1));
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
|
||||
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.inUse.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(fiiue.cleartextPath()).toString()));
|
||||
addLocalizedAction("eventView.entry.inUse.copyEncrypted", () -> copyToClipboard(fiiue.ciphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.inUse.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(fiiue.cleartextPath())));
|
||||
addLocalizedAction("eventView.entry.inUse.showEncrypted", () -> reveal(revealService, fiiue.ciphertextPath()));
|
||||
|
||||
var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR);
|
||||
var user = userAndDevice[0];
|
||||
@@ -156,11 +150,7 @@ public class EventListCellController implements FxController {
|
||||
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message"));
|
||||
eventDescription.setValue(bfe.ciphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath()));
|
||||
addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString()));
|
||||
}
|
||||
|
||||
@@ -168,46 +158,29 @@ public class EventListCellController implements FxController {
|
||||
eventIcon.setValue(FontAwesome5Icon.CHECK);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message"));
|
||||
eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath())));
|
||||
}
|
||||
|
||||
private void adjustToConflictEvent(ConflictResolutionFailedEvent cfe) {
|
||||
eventIcon.setValue(FontAwesome5Icon.COMPRESS_ALT);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message"));
|
||||
eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
||||
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString()));
|
||||
addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath())));
|
||||
addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath()));
|
||||
}
|
||||
|
||||
private void adjustToDecryptionFailedEvent(DecryptionFailedEvent dfe) {
|
||||
eventIcon.setValue(FontAwesome5Icon.BAN);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message"));
|
||||
eventDescription.setValue(dfe.ciphertextPath().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath()));
|
||||
}
|
||||
|
||||
private void adjustToBrokenDirFileEvent(BrokenDirFileEvent bde) {
|
||||
eventIcon.setValue(FontAwesome5Icon.TIMES);
|
||||
eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message"));
|
||||
eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString());
|
||||
if (revealService != null) {
|
||||
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
||||
} else {
|
||||
addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString()));
|
||||
}
|
||||
addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath()));
|
||||
}
|
||||
|
||||
private void addLocalizedAction(String localizationKey, Runnable action) {
|
||||
@@ -270,7 +243,7 @@ public class EventListCellController implements FxController {
|
||||
}
|
||||
|
||||
var mountPoint = v.getMountPoint().uri().getPath();
|
||||
if(SystemUtils.IS_OS_WINDOWS) {
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
mountPoint = mountPoint.substring(1); //strip away any leading "/", otherwise there are errors
|
||||
}
|
||||
return Path.of(mountPoint, vaultInternalPath.substring(1)); //vaultPaths are always absolute
|
||||
|
||||
@@ -10,16 +10,20 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplication {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
||||
|
||||
static final AtomicReference<Application> INSTANCE = new AtomicReference<>();
|
||||
|
||||
private final long startupTime;
|
||||
private final Environment environment;
|
||||
private final Settings settings;
|
||||
@@ -33,7 +37,8 @@ public class FxApplication {
|
||||
private final FxNotificationManager notificationManager;
|
||||
|
||||
@Inject
|
||||
FxApplication(@Named("startupTime") long startupTime, //
|
||||
FxApplication(Application fxApp,
|
||||
@Named("startupTime") long startupTime, //
|
||||
Environment environment, //
|
||||
Settings settings, //
|
||||
AppLaunchEventHandler launchEventHandler, //
|
||||
@@ -55,6 +60,8 @@ public class FxApplication {
|
||||
this.autoUnlocker = autoUnlocker;
|
||||
this.fxFSEventList = fxFSEventList;
|
||||
this.notificationManager = notificationManager;
|
||||
|
||||
INSTANCE.set(fxApp);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
|
||||
@@ -64,17 +64,6 @@ abstract class FxApplicationModule {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
@@ -88,10 +77,4 @@ abstract class FxApplicationModule {
|
||||
return factory.create();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static NotificationComponent provideNotificationComponent(NotificationComponent.Factory factory) {
|
||||
return factory.create();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplicationWindows {
|
||||
@@ -47,15 +48,15 @@ public class FxApplicationWindows {
|
||||
|
||||
private final Stage primaryStage;
|
||||
private final Optional<TrayIntegrationProvider> trayIntegration;
|
||||
private final Lazy<MainWindowComponent> mainWindow;
|
||||
private final Lazy<PreferencesComponent> preferencesWindow;
|
||||
private final CachedLazy<MainWindowComponent> mainWindow;
|
||||
private final CachedLazy<PreferencesComponent> preferencesWindow;
|
||||
private final QuitComponent.Builder quitWindowBuilder;
|
||||
private final UnlockComponent.Factory unlockWorkflowFactory;
|
||||
private final UpdateReminderComponent.Factory updateReminderWindowFactory;
|
||||
private final LockComponent.Factory lockWorkflowFactory;
|
||||
private final ErrorComponent.Factory errorWindowFactory;
|
||||
private final Lazy<EventViewComponent> eventViewWindow;
|
||||
private final Lazy<NotificationComponent> notificationWindow;
|
||||
private final CachedLazy<EventViewComponent> eventViewWindow;
|
||||
private final CachedLazy<NotificationComponent> notificationWindow;
|
||||
private final ExecutorService executor;
|
||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||
private final ShareVaultComponent.Factory shareVaultWindow;
|
||||
@@ -65,8 +66,8 @@ public class FxApplicationWindows {
|
||||
@Inject
|
||||
public FxApplicationWindows(@PrimaryStage Stage primaryStage, //
|
||||
Optional<TrayIntegrationProvider> trayIntegration, //
|
||||
Lazy<MainWindowComponent> mainWindow, //
|
||||
Lazy<PreferencesComponent> preferencesWindow, //
|
||||
MainWindowComponent.Builder mainWindowBuilder, //
|
||||
PreferencesComponent.Builder preferencesWindowBuilder, //
|
||||
QuitComponent.Builder quitWindowBuilder, //
|
||||
UnlockComponent.Factory unlockWorkflowFactory, //
|
||||
UpdateReminderComponent.Factory updateReminderWindowFactory, //
|
||||
@@ -74,21 +75,21 @@ public class FxApplicationWindows {
|
||||
ErrorComponent.Factory errorWindowFactory, //
|
||||
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
||||
ShareVaultComponent.Factory shareVaultWindow, //
|
||||
Lazy<EventViewComponent> eventViewWindow, //
|
||||
Lazy<NotificationComponent> notificationWindow,
|
||||
EventViewComponent.Factory eventViewWindowFactory, //
|
||||
NotificationComponent.Factory notificationWindowFactory, //
|
||||
ExecutorService executor, //
|
||||
Dialogs dialogs) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.trayIntegration = trayIntegration;
|
||||
this.mainWindow = mainWindow;
|
||||
this.preferencesWindow = preferencesWindow;
|
||||
this.mainWindow = new CachedLazy<>(mainWindowBuilder::build);
|
||||
this.preferencesWindow = new CachedLazy<>(preferencesWindowBuilder::build);
|
||||
this.quitWindowBuilder = quitWindowBuilder;
|
||||
this.unlockWorkflowFactory = unlockWorkflowFactory;
|
||||
this.updateReminderWindowFactory = updateReminderWindowFactory;
|
||||
this.lockWorkflowFactory = lockWorkflowFactory;
|
||||
this.errorWindowFactory = errorWindowFactory;
|
||||
this.eventViewWindow = eventViewWindow;
|
||||
this.notificationWindow = notificationWindow;
|
||||
this.eventViewWindow = new CachedLazy<>(eventViewWindowFactory::create);
|
||||
this.notificationWindow = new CachedLazy<>(notificationWindowFactory::create);
|
||||
this.executor = executor;
|
||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||
this.shareVaultWindow = shareVaultWindow;
|
||||
@@ -218,4 +219,29 @@ public class FxApplicationWindows {
|
||||
LOG.error("Failed to display stage", error);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CachedLazy<T> implements Lazy<T> {
|
||||
|
||||
private final Supplier<T> supplier;
|
||||
private volatile T instance = null;
|
||||
|
||||
public CachedLazy(Supplier<T> supplier) {
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
T value = instance;
|
||||
if (value == null) {
|
||||
synchronized (this) {
|
||||
value = instance;
|
||||
if (value == null) {
|
||||
value = supplier.get();
|
||||
instance = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.VaultConfig;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
@@ -25,6 +28,7 @@ import java.util.stream.Collectors;
|
||||
@HealthCheckScoped
|
||||
public class ReportWriter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReportWriter.class);
|
||||
private static final String REPORT_HEADER = """
|
||||
*******************************************
|
||||
* Cryptomator Vault Health Report *
|
||||
@@ -43,14 +47,14 @@ public class ReportWriter {
|
||||
|
||||
private final Vault vault;
|
||||
private final VaultConfig vaultConfig;
|
||||
private final Application application;
|
||||
private final RevealPathService revealPathService;
|
||||
private final Path exportDestination;
|
||||
|
||||
@Inject
|
||||
public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference<VaultConfig> vaultConfigRef, Application application, Environment env) {
|
||||
public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference<VaultConfig> vaultConfigRef, RevealPathService revealPathService, Environment env) {
|
||||
this.vault = vault;
|
||||
this.vaultConfig = Objects.requireNonNull(vaultConfigRef.get());
|
||||
this.application = application;
|
||||
this.revealPathService = revealPathService;
|
||||
this.exportDestination = env.getLogDir().orElse(Path.of(System.getProperty("user.home"))).resolve("healthReport_" + vault.getDisplayName() + "_" + TIME_STAMP.format(Instant.now()) + ".log");
|
||||
}
|
||||
|
||||
@@ -92,7 +96,11 @@ public class ReportWriter {
|
||||
}
|
||||
|
||||
private void reveal() {
|
||||
application.getHostServices().showDocument(exportDestination.getParent().toUri().toString());
|
||||
try {
|
||||
revealPathService.reveal(exportDestination.getParent());
|
||||
} catch (RevealFailedException e) {
|
||||
LOG.warn("Failed to reveal export destination location of report", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.cryptomator.ui.keyloading.hub;
|
||||
|
||||
import com.nimbusds.jose.JWEObject;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
@@ -12,8 +11,6 @@ import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.concurrent.WorkerStateEvent;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_CHECK_HOST_TRUST)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideHubCheckHostTrustScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_CHECK_HOST_TRUST);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_AUTH_FLOW)
|
||||
@KeyLoadingScoped
|
||||
@@ -168,6 +175,13 @@ public abstract class HubKeyLoadingModule {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_UNTRUSTED_HOST)
|
||||
@KeyLoadingScoped
|
||||
static Scene provideHubUntrustedHostScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HUB_UNTRUSTED_HOST);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT)
|
||||
@KeyLoadingScoped
|
||||
@@ -180,6 +194,11 @@ public abstract class HubKeyLoadingModule {
|
||||
@FxControllerKey(NoKeychainController.class)
|
||||
abstract FxController bindNoKeychainController(NoKeychainController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(CheckHostTrustController.class)
|
||||
abstract FxController bindCheckHostAuthenticityController(CheckHostTrustController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(AuthFlowController.class)
|
||||
@@ -225,6 +244,11 @@ public abstract class HubKeyLoadingModule {
|
||||
@FxControllerKey(UnauthorizedDeviceController.class)
|
||||
abstract FxController bindUnauthorizedDeviceController(UnauthorizedDeviceController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(UntrustedHostController.class)
|
||||
abstract FxController bindUnauthorizedHostController(UntrustedHostController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(RequireAccountInitController.class)
|
||||
|
||||
@@ -36,19 +36,19 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
|
||||
private final Stage window;
|
||||
private final KeychainManager keychainManager;
|
||||
private final AtomicReference<String> fsOwnerId;
|
||||
private final Lazy<Scene> authFlowScene;
|
||||
private final Lazy<Scene> checkHostTrustScene;
|
||||
private final Lazy<Scene> noKeychainScene;
|
||||
private final CompletableFuture<ReceivedKey> result;
|
||||
private final DeviceKey deviceKey;
|
||||
|
||||
@Inject
|
||||
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy<Scene> authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference<String> fsOwnerId) {
|
||||
public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_CHECK_HOST_TRUST) Lazy<Scene> checkHostTrustScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy<Scene> noKeychainScene, CompletableFuture<ReceivedKey> result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference<String> fsOwnerId) {
|
||||
this.window = window;
|
||||
this.keychainManager = keychainManager;
|
||||
this.fsOwnerId = fsOwnerId;
|
||||
window.setTitle(windowTitle);
|
||||
window.setOnCloseRequest(_ -> result.cancel(true));
|
||||
this.authFlowScene = authFlowScene;
|
||||
this.checkHostTrustScene = checkHostTrustScene;
|
||||
this.noKeychainScene = noKeychainScene;
|
||||
this.result = result;
|
||||
this.deviceKey = deviceKey;
|
||||
@@ -62,7 +62,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne
|
||||
throw new NoKeychainAccessProviderException();
|
||||
}
|
||||
var keypair = deviceKey.get();
|
||||
showWindow(authFlowScene);
|
||||
showWindow(checkHostTrustScene);
|
||||
var jwe = result.get();
|
||||
return jwe.decryptMasterkey(keypair.getPrivate());
|
||||
} catch (NoKeychainAccessProviderException e) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.cryptomator.ui.keyloading.masterkeyfile;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.common.Passphrase;
|
||||
import org.cryptomator.common.keychain.KeychainManager;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
@@ -63,16 +64,21 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
|
||||
public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException {
|
||||
window.setTitle(resourceBundle.getString("unlock.title").formatted(vault.getDisplayName()));
|
||||
Preconditions.checkArgument(SCHEME.equalsIgnoreCase(keyId.getScheme()), "Only supports keys with scheme " + SCHEME);
|
||||
if (!Constants.MASTERKEY_FILENAME.equals(keyId.getSchemeSpecificPart())) {
|
||||
LOG.warn("unsupported masterkey path found in vault.cryptomator: {}", keyId.getSchemeSpecificPart());
|
||||
}
|
||||
try {
|
||||
Path filePath = vault.getPath().resolve(keyId.getSchemeSpecificPart());
|
||||
// determine masterkey file path:
|
||||
Path filePath = vault.getPath().resolve(Constants.MASTERKEY_FILENAME);
|
||||
if (!Files.exists(filePath)) {
|
||||
filePath = askUserForMasterkeyFilePath();
|
||||
}
|
||||
// unlock:
|
||||
if (passphrase == null) {
|
||||
askForPassphrase();
|
||||
}
|
||||
var masterkey = masterkeyFileAccess.load(filePath, passphrase);
|
||||
//backup
|
||||
// backup on successful unlock:
|
||||
if (filePath.startsWith(vault.getPath())) {
|
||||
try {
|
||||
BackupHelper.attemptBackup(filePath);
|
||||
|
||||
@@ -2,14 +2,17 @@ package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
import org.cryptomator.ui.common.AutoAnimator;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
@@ -19,10 +22,12 @@ import javafx.fxml.FXML;
|
||||
@MainWindowScoped
|
||||
public class VaultDetailController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultDetailController.class);
|
||||
|
||||
private final ReadOnlyObjectProperty<Vault> vault;
|
||||
private final Application application;
|
||||
private final ObservableValue<FontAwesome5Icon> glyph;
|
||||
private final BooleanBinding anyVaultSelected;
|
||||
private final RevealPathService revealPathService;
|
||||
|
||||
private AutoAnimator spinAnimation;
|
||||
|
||||
@@ -31,11 +36,11 @@ public class VaultDetailController implements FxController {
|
||||
|
||||
|
||||
@Inject
|
||||
VaultDetailController(ObjectProperty<Vault> vault, Application application) {
|
||||
VaultDetailController(ObjectProperty<Vault> vault, RevealPathService revealPathService) {
|
||||
this.vault = vault;
|
||||
this.application = application;
|
||||
this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState);
|
||||
this.anyVaultSelected = vault.isNotNull();
|
||||
this.revealPathService = revealPathService;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
@@ -61,7 +66,11 @@ public class VaultDetailController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void revealStorageLocation() {
|
||||
application.getHostServices().showDocument(vault.get().getPath().toUri().toString());
|
||||
try {
|
||||
revealPathService.reveal(vault.get().getPath());
|
||||
} catch (RevealFailedException e) {
|
||||
LOG.warn("Failed to reveal vault storage location", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* Observable Properties */
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Nullable;
|
||||
@@ -21,7 +20,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
@@ -31,7 +29,6 @@ import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.input.DataFormat;
|
||||
import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import javafx.stage.FileChooser;
|
||||
@@ -40,12 +37,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -60,10 +53,11 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
private final VaultService vaultService;
|
||||
private final WrongFileAlertComponent.Builder wrongFileAlert;
|
||||
private final Stage mainWindow;
|
||||
private final Optional<RevealPathService> revealPathService;
|
||||
private final RevealPathService revealPathService;
|
||||
private final DecryptNameComponent.Factory decryptNameWindowFactory;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
|
||||
private final LoadingCache<Vault, DecryptNameComponent> decryptNameWindows;
|
||||
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
|
||||
private final ObservableValue<Boolean> accessibleViaPath;
|
||||
private final ObservableValue<Boolean> accessibleViaUri;
|
||||
@@ -84,7 +78,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
VaultStatisticsComponent.Builder vaultStatsBuilder, //
|
||||
WrongFileAlertComponent.Builder wrongFileAlert, //
|
||||
@MainWindow Stage mainWindow, //
|
||||
Optional<RevealPathService> revealPathService, //
|
||||
RevealPathService revealPathService, //
|
||||
DecryptNameComponent.Factory decryptNameWindowFactory, //
|
||||
ResourceBundle resourceBundle) {
|
||||
this.vault = vault;
|
||||
@@ -95,7 +89,8 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
this.revealPathService = revealPathService;
|
||||
this.decryptNameWindowFactory = decryptNameWindowFactory;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
|
||||
this.vaultStats = Caffeine.newBuilder().weakValues().build(this::buildVaultStats);
|
||||
this.decryptNameWindows = Caffeine.newBuilder().weakValues().build(this::buildDecryptNameWindow);
|
||||
this.vaultStatsBuilder = vaultStatsBuilder;
|
||||
var mp = vault.flatMap(Vault::mountPointProperty);
|
||||
this.accessibleViaPath = mp.map(m -> m instanceof Mountpoint.WithPath).orElse(false);
|
||||
@@ -111,7 +106,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
|
||||
public void initialize() {
|
||||
revealEncryptedDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverLocateEncrypted));
|
||||
revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealOrCopyPaths));
|
||||
revealEncryptedDropZone.setOnDragDropped(e -> handleDragDropped(e, this::getCiphertextPath, this::revealPaths));
|
||||
revealEncryptedDropZone.setOnDragExited(_ -> draggingOverLocateEncrypted.setValue(false));
|
||||
|
||||
decryptNameDropZone.setOnDragOver(e -> handleDragOver(e, draggingOverDecryptName));
|
||||
@@ -156,7 +151,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
if (cleartextFile != null) {
|
||||
var ciphertextPath = getCiphertextPath(cleartextFile.toPath());
|
||||
if (ciphertextPath != null) {
|
||||
revealOrCopyPaths(List.of(ciphertextPath));
|
||||
revealPaths(List.of(ciphertextPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,7 +162,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
}
|
||||
|
||||
private void showDecryptNameWindow(List<Path> pathsToDecrypt) {
|
||||
decryptNameWindowFactory.create(vault.get(), mainWindow, pathsToDecrypt).showDecryptFileNameWindow();
|
||||
decryptNameWindows.get(vault.get()).showDecryptFileNameWindow(pathsToDecrypt);
|
||||
}
|
||||
|
||||
private boolean startsWithVaultAccessPoint(Path path) {
|
||||
@@ -188,38 +183,26 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
private void revealOrCopyPaths(List<Path> paths) {
|
||||
revealPathService.ifPresentOrElse(svc -> revealPaths(svc, paths), () -> {
|
||||
LOG.warn("No service provider to reveal files found.");
|
||||
copyPathsToClipboard(paths);
|
||||
});
|
||||
}
|
||||
|
||||
private void revealPaths(RevealPathService service, List<Path> paths) {
|
||||
private void revealPaths(List<Path> paths) {
|
||||
paths.forEach(path -> {
|
||||
try {
|
||||
LOG.debug("Revealing {}", path);
|
||||
service.reveal(path);
|
||||
revealPathService.reveal(path);
|
||||
} catch (RevealFailedException e) {
|
||||
//TODO: show popup in ui
|
||||
LOG.error("Revealing ciphertext file failed.", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void copyPathsToClipboard(List<Path> paths) {
|
||||
StringBuilder clipboardString = new StringBuilder();
|
||||
paths.forEach(p -> clipboardString.append(p.toString()).append("\n"));
|
||||
Clipboard.getSystemClipboard().setContent(Map.of(DataFormat.PLAIN_TEXT, clipboardString.toString()));
|
||||
ciphertextPathsCopied.setValue(true);
|
||||
CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS, Platform::runLater).execute(() -> {
|
||||
ciphertextPathsCopied.set(false);
|
||||
});
|
||||
}
|
||||
|
||||
private VaultStatisticsComponent buildVaultStats(Vault vault) {
|
||||
return vaultStatsBuilder.vault(vault).build();
|
||||
}
|
||||
|
||||
private DecryptNameComponent buildDecryptNameWindow(Vault vault) {
|
||||
return decryptNameWindowFactory.create(vault, mainWindow);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void revealAccessLocation() {
|
||||
vaultService.reveal(vault.get());
|
||||
@@ -239,7 +222,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void showVaultStatistics() {
|
||||
vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
|
||||
vaultStats.get(vault.get()).showVaultStatisticsWindow();
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
@@ -10,16 +10,18 @@ import org.cryptomator.integrations.common.NamedServiceProvider;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||
import org.cryptomator.integrations.quickaccess.QuickAccessService;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
@@ -41,7 +43,7 @@ public class GeneralPreferencesController implements FxController {
|
||||
private final Settings settings;
|
||||
private final Optional<AutoStartProvider> autoStartProvider;
|
||||
private final List<QuickAccessService> quickAccessServices;
|
||||
private final Application application;
|
||||
private final RevealPathService revealPathService;
|
||||
private final Environment environment;
|
||||
private final List<KeychainAccessProvider> keychainAccessProviders;
|
||||
private final KeychainManager keychain;
|
||||
@@ -55,14 +57,21 @@ public class GeneralPreferencesController implements FxController {
|
||||
public CheckBox autoCloseVaultsCheckbox;
|
||||
public CheckBox debugModeCheckbox;
|
||||
public CheckBox autoStartCheckbox;
|
||||
public Button resetTrustedHostsButton;
|
||||
public ToggleGroup nodeOrientation;
|
||||
|
||||
private CompletionStage<Void> keychainMigrations = CompletableFuture.completedFuture(null);
|
||||
|
||||
@Inject
|
||||
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, //
|
||||
List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, //
|
||||
Environment environment, FxApplicationWindows appWindows, ExecutorService backgroundExecutor) {
|
||||
GeneralPreferencesController(@PreferencesWindow Stage window, //
|
||||
Settings settings, //
|
||||
Optional<AutoStartProvider> autoStartProvider, //
|
||||
List<KeychainAccessProvider> keychainAccessProviders, //
|
||||
KeychainManager keychain, //
|
||||
RevealPathService revealPathService, //
|
||||
Environment environment, //
|
||||
FxApplicationWindows appWindows, //
|
||||
ExecutorService backgroundExecutor) {
|
||||
this.window = window;
|
||||
this.settings = settings;
|
||||
this.autoStartProvider = autoStartProvider;
|
||||
@@ -70,7 +79,7 @@ public class GeneralPreferencesController implements FxController {
|
||||
this.keychain = keychain;
|
||||
this.backgroundExecutor = backgroundExecutor;
|
||||
this.quickAccessServices = QuickAccessService.get().toList();
|
||||
this.application = application;
|
||||
this.revealPathService = revealPathService;
|
||||
this.environment = environment;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
@@ -98,6 +107,9 @@ public class GeneralPreferencesController implements FxController {
|
||||
quickAccessServiceChoiceBox.setConverter(new NamedServiceConverter<>());
|
||||
Bindings.bindBidirectional(settings.quickAccessService, quickAccessServiceChoiceBox.valueProperty(), quickAccessSettingsConverter);
|
||||
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
|
||||
if (resetTrustedHostsButton != null) {
|
||||
resetTrustedHostsButton.disableProperty().bind(Bindings.isEmpty(settings.trustedHosts));
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateKeychainEntries(Observable observable, KeychainAccessProvider oldProvider, KeychainAccessProvider newProvider) {
|
||||
@@ -124,6 +136,10 @@ public class GeneralPreferencesController implements FxController {
|
||||
return autoStartProvider.isPresent();
|
||||
}
|
||||
|
||||
public boolean isHubTrustOnFirstUseEnabled() {
|
||||
return environment.hubTrustOnFirstUse();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void toggleAutoStart() {
|
||||
autoStartProvider.ifPresent(autoStart -> {
|
||||
@@ -146,9 +162,18 @@ public class GeneralPreferencesController implements FxController {
|
||||
return !quickAccessServices.isEmpty();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void resetTrustedHosts() {
|
||||
settings.trustedHosts.clear();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void showLogfileDirectory() {
|
||||
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
|
||||
try {
|
||||
revealPathService.reveal(environment.getLogDir().orElseThrow());
|
||||
} catch (RevealFailedException e) {
|
||||
LOG.warn("Failed to reveal log files directory.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper classes */
|
||||
@@ -196,4 +221,5 @@ public class GeneralPreferencesController implements FxController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,18 +3,19 @@ package org.cryptomator.ui.preferences;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.integrations.revealpath.RevealFailedException;
|
||||
import org.cryptomator.integrations.revealpath.RevealPathService;
|
||||
import org.cryptomator.integrations.update.UpdateStep;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.cryptomator.updater.UpdateChecker;
|
||||
import org.cryptomator.updater.FallbackUpdateInfo;
|
||||
import org.cryptomator.updater.UpdateChecker;
|
||||
import org.cryptomator.updater.UpdateService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.animation.PauseTransition;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
@@ -49,7 +50,7 @@ public class UpdatesPreferencesController implements FxController {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpdatesPreferencesController.class);
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
|
||||
|
||||
private final Application application;
|
||||
private final RevealPathService revealPathService;
|
||||
private final Environment environment;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final Settings settings;
|
||||
@@ -72,8 +73,8 @@ public class UpdatesPreferencesController implements FxController {
|
||||
public CheckBox checkForUpdatesCheckbox;
|
||||
|
||||
@Inject
|
||||
UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
|
||||
this.application = application;
|
||||
UpdatesPreferencesController(RevealPathService revealPathService, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker, ObservableList<Vault> vaults, VaultService vaultService) {
|
||||
this.revealPathService = revealPathService;
|
||||
this.environment = environment;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.settings = settings;
|
||||
@@ -106,9 +107,14 @@ public class UpdatesPreferencesController implements FxController {
|
||||
updateService.setOnFailed(this::updateFailed);
|
||||
}
|
||||
|
||||
|
||||
@FXML
|
||||
public void showLogfileDirectory() {
|
||||
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
|
||||
try {
|
||||
revealPathService.reveal(environment.getLogDir().orElseThrow());
|
||||
} catch (RevealFailedException e) {
|
||||
LOG.warn("Failed to reveal log files directory.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -254,7 +260,7 @@ public class UpdatesPreferencesController implements FxController {
|
||||
|
||||
public boolean isProhibitUpdateWhileUnlocked() {
|
||||
// If the result of the last update check was from the fallback mechanism, we don't need to show the warning
|
||||
return !unlockedVaults.isEmpty() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate());
|
||||
return !unlockedVaults.isEmpty() && updateChecker.isUpdateAvailable() && !FallbackUpdateInfo.class.isInstance(updateChecker.getUpdate());
|
||||
}
|
||||
|
||||
public BooleanBinding prohibitUpdateWhileUnlockedProperty() {
|
||||
|
||||
@@ -18,7 +18,10 @@ import java.net.URISyntaxException;
|
||||
public class ShareVaultController implements FxController {
|
||||
|
||||
private static final String SCHEME_PREFIX = "hub+";
|
||||
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/";
|
||||
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/" //
|
||||
+ "?utm_source=cryptomator-desktop" //
|
||||
+ "&utm_medium=app" //
|
||||
+ "&utm_campaign=share-vault";
|
||||
private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/security/best-practices/#sharing-of-vaults";
|
||||
|
||||
private final Stage window;
|
||||
|
||||
53
src/main/resources/fxml/hub_check_host_trust.fxml
Normal file
53
src/main/resources/fxml/hub_check_host_trust.fxml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<?import javafx.scene.text.TextFlow?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.CheckHostTrustController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT"
|
||||
accessibleRole="DIALOG">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="QUESTION" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="${controller.messageLabel}" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<TextFlow fx:id="hostnamesFlow" styleClass="text-flow" minHeight="60"/>
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
|
||||
<buttons>
|
||||
<Button text="%hub.checkHostTrust.denyBtn" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#deny"/>
|
||||
<Button text="%hub.checkHostTrust.trustBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#trust"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
||||
52
src/main/resources/fxml/hub_untrusted_host.fxml
Normal file
52
src/main/resources/fxml/hub_untrusted_host.fxml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.Group?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.keyloading.hub.UntrustedHostController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12"
|
||||
alignment="TOP_LEFT"
|
||||
accessibleRole="DIALOG">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Group>
|
||||
<StackPane>
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
|
||||
</StackPane>
|
||||
</Group>
|
||||
<VBox HBox.hgrow="ALWAYS">
|
||||
<Label styleClass="label-large" text="%hub.untrustedHost.message" wrapText="true" textAlignment="LEFT">
|
||||
<padding>
|
||||
<Insets bottom="6" top="6"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="%hub.untrustedHost.description" wrapText="true"/>
|
||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+C">
|
||||
<buttons>
|
||||
<Button text="%generic.button.close" ButtonBar.buttonData="CANCEL_CLOSE" defaultButton="true" onAction="#close"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
</children>
|
||||
</HBox>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<BorderPane xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
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"
|
||||
accessibleRole="DIALOG">
|
||||
<padding>
|
||||
@@ -65,7 +65,7 @@
|
||||
<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}"/>
|
||||
<Region minHeight="6"/>
|
||||
<ScrollPane minViewportWidth="370" minViewportHeight="50">
|
||||
<ScrollPane minViewportWidth="370" minViewportHeight="70">
|
||||
<Label text="${controller.description}" styleClass="label" wrapText="true" maxWidth="370"/>
|
||||
</ScrollPane>
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</ImageView>
|
||||
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
|
||||
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
|
||||
<Label text="© 2016 – 2025 Skymatic GmbH"/>
|
||||
<Label text="© 2016 – 2026 Skymatic GmbH"/>
|
||||
</VBox>
|
||||
</HBox>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.control.ChoiceBox?>
|
||||
<?import javafx.scene.control.Hyperlink?>
|
||||
@@ -34,6 +35,8 @@
|
||||
<CheckBox fx:id="useQuickAccessCheckbox" text="%preferences.general.quickAccessService"/>
|
||||
<ChoiceBox fx:id="quickAccessServiceChoiceBox" accessibleText="%preferences.general.quickAccessService"/>
|
||||
</HBox>
|
||||
|
||||
<Button fx:id="resetTrustedHostsButton" text="%preferences.general.resetTrustedHosts" visible="${controller.hubTrustOnFirstUseEnabled}" managed="${controller.hubTrustOnFirstUseEnabled}" onAction="#resetTrustedHosts"/>
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||
|
||||
@@ -162,6 +162,12 @@ unlock.error.title=Unlock "%s" failed
|
||||
hub.noKeychain.message=Unable to access device key
|
||||
hub.noKeychain.description=In order to unlock Hub vaults, a device key is required, which is secured using a keychain. To proceed, enable “%s” and select a keychain in the preferences.
|
||||
hub.noKeychain.openBtn=Open Preferences
|
||||
### Check Host Authenticity
|
||||
hub.checkHostTrust.message.check=Checking Configuration…
|
||||
hub.checkHostTrust.message.ask=Trust this host?
|
||||
hub.checkHostTrust.message.ask.plural=Trust these hosts?
|
||||
hub.checkHostTrust.trustBtn=Trust
|
||||
hub.checkHostTrust.denyBtn=Deny
|
||||
### Waiting
|
||||
hub.auth.message=Waiting for authentication…
|
||||
hub.auth.description=You should automatically be redirected to the login page.
|
||||
@@ -193,6 +199,9 @@ hub.archived.description=This vault has been archived and is no longer accessibl
|
||||
### Unauthorized
|
||||
hub.unauthorized.message=Access denied
|
||||
hub.unauthorized.description=You are not authorized to open this vault. Contact the vault's owner to request access.
|
||||
### Untrusted Host
|
||||
hub.untrustedHost.message=Host not trusted
|
||||
hub.untrustedHost.description=Connection to Hub was blocked for your security. If you believe the Hub host is safe, contact your Hub administrator or try again.
|
||||
### Requires Account Initialization
|
||||
hub.requireAccountInit.message=Action required
|
||||
hub.requireAccountInit.description.0=To proceed, please complete the steps required in your
|
||||
@@ -306,6 +315,7 @@ preferences.general.debugDirectory=Reveal log files
|
||||
preferences.general.autoStart=Launch Cryptomator on system start
|
||||
preferences.general.keychainBackend=Store passwords with
|
||||
preferences.general.quickAccessService=Add unlocked vaults to the quick access area
|
||||
preferences.general.resetTrustedHosts=Reset trusted hosts
|
||||
## Interface
|
||||
preferences.interface=Interface
|
||||
preferences.interface.theme=Look & Feel
|
||||
@@ -696,33 +706,25 @@ eventView.cell.actionsButton.tooltip=Event actions
|
||||
eventView.entry.vaultLocked.description=Unlock "%s" for details
|
||||
eventView.entry.conflictResolved.message=Resolved conflict
|
||||
eventView.entry.conflictResolved.showDecrypted=Show decrypted file
|
||||
eventView.entry.conflictResolved.copyDecrypted=Copy decrypted path
|
||||
eventView.entry.conflict.message=Conflict resolution failed
|
||||
eventView.entry.conflict.showDecrypted=Show decrypted, original file
|
||||
eventView.entry.conflict.copyDecrypted=Copy decrypted, original path
|
||||
eventView.entry.conflict.showEncrypted=Show conflicting, encrypted file
|
||||
eventView.entry.conflict.copyEncrypted=Copy conflicting, encrypted path
|
||||
eventView.entry.decryptionFailed.message=Decryption failed
|
||||
eventView.entry.decryptionFailed.showEncrypted=Show encrypted file
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Copy encrypted path
|
||||
eventView.entry.brokenDirFile.message=Broken directory link
|
||||
eventView.entry.brokenDirFile.showEncrypted=Show broken, encrypted link
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Copy path of broken link
|
||||
eventView.entry.brokenFileNode.message=Broken filesystem node
|
||||
eventView.entry.brokenFileNode.showEncrypted=Show broken, encrypted node
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Copy path of broken, encrypted node
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Copy decrypted path
|
||||
eventView.entry.inUse.message=Locked File
|
||||
eventView.entry.inUse.message=File in use
|
||||
eventView.entry.inUse.showDecrypted=Show decrypted file
|
||||
eventView.entry.inUse.copyDecrypted=Copy decrypted path
|
||||
eventView.entry.inUse.showEncrypted=Show encrypted file
|
||||
eventView.entry.inUse.copyEncrypted=Copy encrypted path
|
||||
eventView.entry.inUse.copyUserAndDevice=Copy locking user and device name
|
||||
eventView.entry.inUse.ignoreLock=Ignore Lock
|
||||
eventView.entry.inUse.ignoreLock=Ignore use status
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=File is locked by 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.action=Ignore Lock
|
||||
notification.inUse.message=File is in use on another device
|
||||
notification.inUse.description=The file is open by %s on %s. Ask them to close the file and let synchronization finish. You can ignore the status to open it now, but this may cause conflicts or overwrite newer changes.
|
||||
notification.inUse.action=Ignore Use Status
|
||||
|
||||
@@ -630,26 +630,18 @@ eventView.clearListButton.tooltip=تفريغ القائمة
|
||||
eventView.entry.vaultLocked.description=فتح "%s" للحصول على التفاصيل
|
||||
eventView.entry.conflictResolved.message=تم حل التضارب
|
||||
eventView.entry.conflictResolved.showDecrypted=إظهار الملف غير المشفر
|
||||
eventView.entry.conflictResolved.copyDecrypted=نسخ المسار غير المشفر
|
||||
eventView.entry.conflict.message=فشل حل التضارب
|
||||
eventView.entry.conflict.showDecrypted=إظهار الملَف غير المشفر الأصلي
|
||||
eventView.entry.conflict.copyDecrypted=نسخ المسار غير المشفر والأصلي
|
||||
eventView.entry.conflict.showEncrypted=إظهار ملف متضارب ومشفر
|
||||
eventView.entry.conflict.copyEncrypted=نسخ مسار التشفير المتعارض
|
||||
eventView.entry.decryptionFailed.message=فشل فك التشفير
|
||||
eventView.entry.decryptionFailed.showEncrypted=عرض ملَف المشفر
|
||||
eventView.entry.decryptionFailed.copyEncrypted=نسخ مسار المشفر
|
||||
eventView.entry.brokenDirFile.message=رابط الدليل المكسور
|
||||
eventView.entry.brokenDirFile.showEncrypted=إظهار الرابط المكسور، المشفر
|
||||
eventView.entry.brokenDirFile.copyEncrypted=نسخ مسار الرابط المكسور
|
||||
eventView.entry.brokenFileNode.message=عقدة ملفات النظام التافلة
|
||||
eventView.entry.brokenFileNode.showEncrypted=عرض العقدة المشفّرة التافلة
|
||||
eventView.entry.brokenFileNode.copyEncrypted=نسخ مسار العقدة المشفّرة التافلة
|
||||
eventView.entry.brokenFileNode.copyDecrypted=نسخ المسار غير المشفر
|
||||
eventView.entry.inUse.showDecrypted=إظهار الملف غير المشفر
|
||||
eventView.entry.inUse.copyDecrypted=نسخ المسار غير المشفر
|
||||
eventView.entry.inUse.showEncrypted=عرض ملَف المشفر
|
||||
eventView.entry.inUse.copyEncrypted=نسخ مسار المشفر
|
||||
|
||||
|
||||
# Notifications
|
||||
|
||||
@@ -18,6 +18,12 @@ generic.button.print=Отпечатване
|
||||
generic.button.remove=Премахване
|
||||
|
||||
## Vault state
|
||||
vault.state.locked=Заключено
|
||||
vault.state.unlocked=Отключено
|
||||
vault.state.missing=Липсващ
|
||||
vault.state.migrationNeeded=Необходима е миграция
|
||||
vault.state.processing=Обработване
|
||||
vault.state.error=Грешка
|
||||
|
||||
# Error
|
||||
error.message=Възникна грешка
|
||||
@@ -98,6 +104,7 @@ addvault.new.readme.accessLocation.4=При желание можете да п
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Добавяне на съществуващо хранилище
|
||||
addvaultwizard.existing.instruction=Изберете файла „vault.cryptomator“ от съществуващото хранилище, но ако има само файл „masterkey.cryptomator“, изберете него.
|
||||
addvaultwizard.existing.restore=Възстановяване…
|
||||
addvaultwizard.existing.chooseBtn=Избиране…
|
||||
addvaultwizard.existing.filePickerTitle=Избор на файл на хранилището
|
||||
addvaultwizard.existing.filePickerMimeDesc=Хранилище на Криптоматор
|
||||
@@ -145,6 +152,7 @@ unlock.error.customPath.description.hideawayNotDir=Временният, скр
|
||||
unlock.error.customPath.description.couldNotBeCleaned=Хранилището не може да бъде монтирано на „%s“. Опитайте отново или изберете друг път.
|
||||
unlock.error.customPath.description.notEmptyDir=Потребителският път на монтиране „%s“ не е празна папка. Изберете празна папка и опитайте отново.
|
||||
unlock.error.customPath.description.generic=Избрали сте потребителски път за монтиране на това хранилище, но при използването му възникна следната грешка: %2$s
|
||||
unlock.error.restartRequired.message=Хранилището не може да бъде отключено
|
||||
unlock.error.title=Неуспешно отключване на „%s“
|
||||
## Hub
|
||||
hub.noKeychain.message=Няма достъп до ключа на устройството
|
||||
@@ -369,6 +377,7 @@ main.vaultlist.contextMenu.unlock=Отключване…
|
||||
main.vaultlist.contextMenu.unlockNow=Отключване сега
|
||||
main.vaultlist.contextMenu.vaultoptions=Настройки на хранилището
|
||||
main.vaultlist.contextMenu.reveal=Разкриване на диска
|
||||
main.vaultlist.contextMenu.share=Споделяне…
|
||||
main.vaultlist.addVaultButton.tooltip=Добавяне на хранилище
|
||||
##Notification
|
||||
## Vault Detail
|
||||
|
||||
@@ -269,6 +269,8 @@ health.check.detail.checkFinishedAndFound=Kontrolproceduren er kørt færdig. Ge
|
||||
health.check.detail.checkFailed=Kontrolproceduren blev afbrudt af en fejl.
|
||||
health.check.detail.checkCancelled=Kontrolproceduren blev annulleret.
|
||||
health.check.detail.listFilters.label=Filter
|
||||
health.check.detail.filterSeverity=Filtrér efter sværhedsgrad
|
||||
health.check.detail.filterFixState=Filtrér efter fix status
|
||||
health.check.detail.fixAllSpecificBtn=Løs alle af type
|
||||
health.check.exportBtn=Eksportér rapport
|
||||
## Result view
|
||||
@@ -358,6 +360,7 @@ preferences.contribute.promptText=Indsæt koden for supporter-certifikatet her
|
||||
preferences.contribute.thankYou=Tak fordi du støtter Cryptomators open source-udvikling!
|
||||
preferences.contribute.donate=Donér
|
||||
preferences.contribute.sponsor=Sponsor
|
||||
preferences.contribute.removeCert.tooltip=Fjern certifikat
|
||||
|
||||
### Remove License Key Dialog
|
||||
removeCert.title=Fjern certifikat
|
||||
@@ -367,6 +370,7 @@ removeCert.description=Cryptomators kernefunktioner påvirkes ikke af dette. Hve
|
||||
|
||||
## About
|
||||
preferences.about=Om
|
||||
preferences.about.thirdPartyLicenses=Tredjepartslicenser
|
||||
|
||||
# Vault Statistics
|
||||
stats.title=Statistik for %s
|
||||
@@ -406,6 +410,7 @@ stats.access.total=Samlede adgang: %d
|
||||
# Main Window
|
||||
## Vault List
|
||||
main.vaultlist=Bokse
|
||||
main.vaultlist.listEntry=Boks %s (%s)
|
||||
main.vaultlist.emptyList.onboardingInstruction=Klik her for at tilføje en boks
|
||||
main.vaultlist.contextMenu.remove=Fjern…
|
||||
main.vaultlist.contextMenu.lock=Lås
|
||||
@@ -419,12 +424,15 @@ main.vaultlist.addVaultBtn.menuItemExisting=Åbn eksisterende boks…
|
||||
main.vaultlist.addVaultBtn.menuItemRecover=Genopret eksisterende boks…
|
||||
main.vaultlist.addVaultButton.tooltip=Tilføj boks
|
||||
main.vaultlist.showEventsButton.tooltip=Åbn begivenhedsvisning
|
||||
main.vaultlist.showPreferencesButton.tooltip=Vis Indstillinger
|
||||
##Notification
|
||||
main.notification.updateAvailable=Opdatering er tilgængelig.
|
||||
main.notification.support=Støt Cryptomator.
|
||||
main.notification.closeButton.tooltip=Luk infobjælke
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Tak fordi du valgte Cryptomator til at beskytte dine filer. Hvis du har brug for hjælp, så tjek vores guider for at komme i gang:
|
||||
main.vaultDetail.storageLocation=Placering af boks
|
||||
### Locked
|
||||
main.vaultDetail.lockedStatus=LÅST
|
||||
main.vaultDetail.unlockBtn=Lås op…
|
||||
@@ -482,6 +490,7 @@ vaultOptions.general=Generelt
|
||||
vaultOptions.general.vaultName=Boks-navn
|
||||
vaultOptions.general.autoLock.lockAfterTimePart1=Lås efter inaktivitet i
|
||||
vaultOptions.general.autoLock.lockAfterTimePart2=minutter
|
||||
vaultOptions.general.autoLock.accessibleText=Lås timeout i minutter
|
||||
vaultOptions.general.unlockAfterStartup=Lås boksen op når Cryptomator starter
|
||||
vaultOptions.general.actionAfterUnlock=Efter oplåsning af boks
|
||||
vaultOptions.general.actionAfterUnlock.ignore=Gør intet
|
||||
@@ -512,6 +521,7 @@ vaultOptions.masterkey.forgetSavedPasswordBtn=Glem gemt adgangskode
|
||||
vaultOptions.masterkey.recoveryKeyExplanation=En gendannelsesnøgle er den eneste måde du kan få adgang til din boks på, hvis du har glemt dit password.
|
||||
vaultOptions.masterkey.showRecoveryKeyBtn=Vis gendannelsesnøgle
|
||||
vaultOptions.masterkey.recoverPasswordBtn=Nulstil adgangskode
|
||||
vaultOptions.masterkey.missingMasterkeyFile=Disse tilvalg er kun tilgængelige hvis masterkeyfilen er til stede i boksmappen.
|
||||
## Hub
|
||||
vaultOptions.hub=Gendannelse
|
||||
vaultOptions.hub.convertInfo=Du kan bruge gendannelsesnøglen til at konvertere denne Hub-boks til en adgangskode-baseret boks i en nødsituation.
|
||||
@@ -665,6 +675,8 @@ decryptNames.filePicker.title=Vælg krypteret fil
|
||||
decryptNames.filePicker.extensionDescription=Cryptomator krypteret fil
|
||||
decryptNames.copyTable.tooltip=Kopiér tabel
|
||||
decryptNames.clearTable.tooltip=Ryd tabel
|
||||
decryptNames.column.encrypted=Krypteret
|
||||
decryptNames.column.decrypted=Dekrypteret
|
||||
decryptNames.copyHint=Kopiér celleindhold med %s
|
||||
decryptNames.dropZone.message=Slip filer eller klik for at vælge
|
||||
decryptNames.dropZone.error.vaultInternalFiles=Boks interne filer med intet dekryptérbart navn valgt
|
||||
@@ -677,34 +689,31 @@ decryptNames.dropZone.error.generic=Kunne ikke dekryptere filnavne
|
||||
eventView.title=Begivenheder
|
||||
eventView.filter.allVaults=Alle
|
||||
eventView.clearListButton.tooltip=Ryd liste
|
||||
eventView.filterVaults=Filtrér efter boks
|
||||
eventView.cell.actionsButton.tooltip=Begivenhedshandlinger
|
||||
## event list entries
|
||||
eventView.entry.vaultLocked.description=Lås "%s" op for detaljer
|
||||
eventView.entry.conflictResolved.message=Løst konflikt
|
||||
eventView.entry.conflictResolved.showDecrypted=Vis dekrypteret fil
|
||||
eventView.entry.conflictResolved.copyDecrypted=Kopiér dekrypteret sti
|
||||
eventView.entry.conflict.message=Konfliktløsning mislykkedes
|
||||
eventView.entry.conflict.showDecrypted=Vis dekrypteret, original fil
|
||||
eventView.entry.conflict.copyDecrypted=Kopier dekrypteret, original sti
|
||||
eventView.entry.conflict.showEncrypted=Vis modstridende, krypteret fil
|
||||
eventView.entry.conflict.copyEncrypted=Kopier modstridende, krypteret sti
|
||||
eventView.entry.decryptionFailed.message=Dekryptering mislykkedes
|
||||
eventView.entry.decryptionFailed.showEncrypted=Vis krypteret fil
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Kopiér krypteret sti
|
||||
eventView.entry.brokenDirFile.message=Brudt mappelink
|
||||
eventView.entry.brokenDirFile.showEncrypted=Vis brudt, krypteret link
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Kopiér sti til brudt link
|
||||
eventView.entry.brokenFileNode.message=Brudt filsystem-node
|
||||
eventView.entry.brokenFileNode.showEncrypted=Vis brudt, krypteret node
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Kopiér sti af brudt, krypteret node
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Kopiér dekrypteret sti
|
||||
eventView.entry.inUse.message=Fil i brug
|
||||
eventView.entry.inUse.showDecrypted=Vis dekrypteret fil
|
||||
eventView.entry.inUse.copyDecrypted=Kopiér dekrypteret sti
|
||||
eventView.entry.inUse.showEncrypted=Vis krypteret fil
|
||||
eventView.entry.inUse.copyEncrypted=Kopiér krypteret sti
|
||||
eventView.entry.inUse.copyUserAndDevice=Kopiér låsebruger og enhedsnavn
|
||||
eventView.entry.inUse.ignoreLock=Ignorér anvendelsesstatus
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=Filen er låst af en anden enhed
|
||||
notification.inUse.description=Filen åbnes af %s på enheden %s. Bed brugeren om at lukke filen og synkronisere igen. Ellers kan du ignorere låsen og åbne den alligevel.
|
||||
notification.inUse.action=Ignorér Lås
|
||||
notification.inUse.message=Filen er i brug på en anden enhed
|
||||
notification.inUse.description=Filen er åbnet af %s på %s. Bed dem om at lukke filen og lade synkroniseringen afslutte. Du kan ignorere status for at åbne den nu, men det kan forårsage konflikter eller overskrive nyere ændringer.
|
||||
notification.inUse.action=Ignorér anvendelsesstatus
|
||||
@@ -684,32 +684,21 @@ eventView.cell.actionsButton.tooltip=Ereignisaktionen
|
||||
eventView.entry.vaultLocked.description=Entsperre "%s" für Details
|
||||
eventView.entry.conflictResolved.message=Konflikt gelöst
|
||||
eventView.entry.conflictResolved.showDecrypted=Entschlüsselte Datei anzeigen
|
||||
eventView.entry.conflictResolved.copyDecrypted=Pfad der entschlüsselten Datei kopieren
|
||||
eventView.entry.conflict.message=Konfliktlösung fehlgeschlagen
|
||||
eventView.entry.conflict.showDecrypted=Entschlüsselte, ursprüngliche Datei anzeigen
|
||||
eventView.entry.conflict.copyDecrypted=Entschlüsselten, ursprünglicher Pfad kopieren
|
||||
eventView.entry.conflict.showEncrypted=Zeige verschlüsselte Konfliktdatei
|
||||
eventView.entry.conflict.copyEncrypted=Verschlüsselten Konfliktpfad kopieren
|
||||
eventView.entry.decryptionFailed.message=Entschlüsselung fehlgeschlagen
|
||||
eventView.entry.decryptionFailed.showEncrypted=Verschlüsselte Datei anzeigen
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Pfad der verschlüsselten Datei kopieren
|
||||
eventView.entry.brokenDirFile.message=Ungültiger Verzeichnislink
|
||||
eventView.entry.brokenDirFile.showEncrypted=Defekten, verschlüsselten Link anzeigen
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Pfad des ungültigen Links kopieren
|
||||
eventView.entry.brokenFileNode.message=Beschädigter Dateisystemknoten
|
||||
eventView.entry.brokenFileNode.showEncrypted=Beschädigten, verschlüsselten Knoten anzeigen
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Pfad des beschädigten, verschlüsselten Knotens kopieren
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Pfad der entschlüsselten Datei kopieren
|
||||
eventView.entry.inUse.message=Gesperrte Datei
|
||||
eventView.entry.inUse.showDecrypted=Entschlüsselte Datei anzeigen
|
||||
eventView.entry.inUse.copyDecrypted=Pfad der entschlüsselten Datei kopieren
|
||||
eventView.entry.inUse.showEncrypted=Verschlüsselte Datei anzeigen
|
||||
eventView.entry.inUse.copyEncrypted=Pfad der verschlüsselten Datei kopieren
|
||||
eventView.entry.inUse.copyUserAndDevice=Benutzer- und Gerätenamen der Sperre kopieren
|
||||
eventView.entry.inUse.ignoreLock=Sperre ignorieren
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=Die Datei ist über ein anderes Gerät gesperrt
|
||||
notification.inUse.action=Sperre ignorieren
|
||||
notification.inUse.action=Verwendungsstatus ignorieren
|
||||
@@ -630,26 +630,18 @@ eventView.clearListButton.tooltip=Εκκαθάριση λίστας
|
||||
eventView.entry.vaultLocked.description=Ξεκλείδωμα "%s" για λεπτομέρειες
|
||||
eventView.entry.conflictResolved.message=Επίλυση σύγκρουσης
|
||||
eventView.entry.conflictResolved.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
|
||||
eventView.entry.conflictResolved.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
|
||||
eventView.entry.conflict.message=Η επίλυση συγκρούσεων απέτυχε
|
||||
eventView.entry.conflict.showDecrypted=Εμφάνιση αποκρυπτογραφημένων, αρχικό αρχείο
|
||||
eventView.entry.conflict.copyDecrypted=Αντιγραφή αποκρυπτογραφημένων, αρχική διαδρομή
|
||||
eventView.entry.conflict.showEncrypted=Εμφάνιση αντικρουόμενου, κρυπτογραφημένο αρχείο
|
||||
eventView.entry.conflict.copyEncrypted=Αντιγραφή σύγκρουσης, κρυπτογραφημένη διαδρομή
|
||||
eventView.entry.decryptionFailed.message=Η αποκρυπτογράφηση απέτυχε
|
||||
eventView.entry.decryptionFailed.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Αντιγραφή κρυπτογραφημένης διαδρομής
|
||||
eventView.entry.brokenDirFile.message=Μη λειτουργικός σύνδεσμος καταλόγου
|
||||
eventView.entry.brokenDirFile.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου συνδέσμου
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Αντιγραφή διαδρομής του μη λειτουργικού συνδέσμου
|
||||
eventView.entry.brokenFileNode.message=Μη λειτουργικός κόμβος συστήματος αρχείων
|
||||
eventView.entry.brokenFileNode.showEncrypted=Εμφάνιση μη λειτουργικού, κρυπτογραφημένου κόμβου
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Αντιγραφή διαδρομής του μη λειτουργικού, κρυπτογραφημένου κόμβου
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
|
||||
eventView.entry.inUse.showDecrypted=Εμφάνιση αποκρυπτογραφημένου αρχείου
|
||||
eventView.entry.inUse.copyDecrypted=Αντιγραφή αποκρυπτογραφημένης διαδρομής
|
||||
eventView.entry.inUse.showEncrypted=Εμφάνιση κρυπτογραφημένου αρχείου
|
||||
eventView.entry.inUse.copyEncrypted=Αντιγραφή κρυπτογραφημένης διαδρομής
|
||||
|
||||
|
||||
# Notifications
|
||||
|
||||
@@ -684,27 +684,24 @@ eventView.cell.actionsButton.tooltip=Acciones del evento
|
||||
eventView.entry.vaultLocked.description=Desbloquear "%s" para más detalles
|
||||
eventView.entry.conflictResolved.message=Conflicto resuelto
|
||||
eventView.entry.conflictResolved.showDecrypted=Mostrar archivo descifrado
|
||||
eventView.entry.conflictResolved.copyDecrypted=Copiar ruta descifrada
|
||||
eventView.entry.conflict.message=Resolución de conflictos fallida
|
||||
eventView.entry.conflict.showDecrypted=Mostrar archivo descifrado, original
|
||||
eventView.entry.conflict.copyDecrypted=Copiar ruta descifrada, original
|
||||
eventView.entry.conflict.showEncrypted=Mostrar archivo en conflicto, cifrado
|
||||
eventView.entry.conflict.copyEncrypted=Copiar ruta en conflicto, cifrada
|
||||
eventView.entry.decryptionFailed.message=Desencriptación fallida
|
||||
eventView.entry.decryptionFailed.showEncrypted=Mostrar archivo cifrado
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Copiar ruta cifrada
|
||||
eventView.entry.brokenDirFile.message=Enlace de directorio roto
|
||||
eventView.entry.brokenDirFile.showEncrypted=Mostrar enlace roto, cifrado
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Copiar ruta del enlace roto
|
||||
eventView.entry.brokenFileNode.message=Nodo de sistema de archivos roto
|
||||
eventView.entry.brokenFileNode.showEncrypted=Mostrar nodo roto, cifrado
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Copiar ruta del enlace roto, encriptado
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Copiar ruta descifrada
|
||||
eventView.entry.inUse.message=Fichero en uso
|
||||
eventView.entry.inUse.showDecrypted=Mostrar archivo descifrado
|
||||
eventView.entry.inUse.copyDecrypted=Copiar ruta descifrada
|
||||
eventView.entry.inUse.showEncrypted=Mostrar archivo cifrado
|
||||
eventView.entry.inUse.copyEncrypted=Copiar ruta cifrada
|
||||
eventView.entry.inUse.ignoreLock=Ignorar el estado de uso
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=El archivo está en uso en otro dispositivo
|
||||
notification.inUse.description=El archivo está abierto por %s en %s. Solicítale que cierre el archivo y deje que finalice la sincronización. Puede ignorar el estado para abrirlo ahora, pero esto puede causar conflictos o sobrescribir cambios más recientes.
|
||||
notification.inUse.action=Ignorar el Estado de Uso
|
||||
@@ -683,26 +683,18 @@ eventView.cell.actionsButton.tooltip=Toiminnot
|
||||
eventView.entry.vaultLocked.description=Avaa "%s" lisätietoja varten
|
||||
eventView.entry.conflictResolved.message=Ratkaistu ristiriita
|
||||
eventView.entry.conflictResolved.showDecrypted=Näytä purettu tiedosto
|
||||
eventView.entry.conflictResolved.copyDecrypted=Kopioi purettu polku
|
||||
eventView.entry.conflict.message=Ristiriidan ratkaisu epäonnistui
|
||||
eventView.entry.conflict.showDecrypted=Näytä purettu, alkuperäinen tiedosto
|
||||
eventView.entry.conflict.copyDecrypted=Näytä purettu, alkuperäinen polku
|
||||
eventView.entry.conflict.showEncrypted=Näytä ristiriitainen, salattu tiedosto
|
||||
eventView.entry.conflict.copyEncrypted=Kopioi ristiriitainen, salattu polku
|
||||
eventView.entry.decryptionFailed.message=Salauksen purku epäonnistui
|
||||
eventView.entry.decryptionFailed.showEncrypted=Näytä salattu tiedosto
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Kopioi salattu polku
|
||||
eventView.entry.brokenDirFile.message=Virheellinen hakemiston linkki
|
||||
eventView.entry.brokenDirFile.showEncrypted=Näytä virheellinen, salattu linkki
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Kopioi virheellisen polun linkki
|
||||
eventView.entry.brokenFileNode.message=Virheellinen tiedostojärjestelmän solmu
|
||||
eventView.entry.brokenFileNode.showEncrypted=Näytä virheellinen, salattu solmu
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Kopioi virheellisen, salatun solmun polku
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Kopioi purettu polku
|
||||
eventView.entry.inUse.showDecrypted=Näytä purettu tiedosto
|
||||
eventView.entry.inUse.copyDecrypted=Kopioi purettu polku
|
||||
eventView.entry.inUse.showEncrypted=Näytä salattu tiedosto
|
||||
eventView.entry.inUse.copyEncrypted=Kopioi salattu polku
|
||||
|
||||
|
||||
# Notifications
|
||||
|
||||
@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Actions d'évènement
|
||||
eventView.entry.vaultLocked.description=Déverrouillez "%s" pour plus de détails
|
||||
eventView.entry.conflictResolved.message=Conflit résolu
|
||||
eventView.entry.conflictResolved.showDecrypted=Afficher le fichier déchiffré
|
||||
eventView.entry.conflictResolved.copyDecrypted=Copier le chemin déchiffré
|
||||
eventView.entry.conflict.message=La résolution des conflits a échoué
|
||||
eventView.entry.conflict.showDecrypted=Afficher le fichier original déchiffré
|
||||
eventView.entry.conflict.copyDecrypted=Copier le chemin original déchiffré
|
||||
eventView.entry.conflict.showEncrypted=Afficher le fichier chiffré en conflit
|
||||
eventView.entry.conflict.copyEncrypted=Copier le chemin chiffré en conflit
|
||||
eventView.entry.decryptionFailed.message=Le déchiffrement a échoué
|
||||
eventView.entry.decryptionFailed.showEncrypted=Afficher le fichier chiffré
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Copier le chemin chiffré
|
||||
eventView.entry.brokenDirFile.message=Lien de répertoire cassé
|
||||
eventView.entry.brokenDirFile.showEncrypted=Afficher le lien chiffré cassé
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Copier le chemin du lien cassé
|
||||
eventView.entry.brokenFileNode.message=Nœud de système de fichiers cassé
|
||||
eventView.entry.brokenFileNode.showEncrypted=Afficher le nœud chiffré cassé
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Copier le chemin du nœud chiffré cassé
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Copier le chemin déchiffré
|
||||
eventView.entry.inUse.message=Fichier verrouillé
|
||||
eventView.entry.inUse.message=Fichier en cours d’utilisation
|
||||
eventView.entry.inUse.showDecrypted=Afficher le fichier déchiffré
|
||||
eventView.entry.inUse.copyDecrypted=Copier le chemin déchiffré
|
||||
eventView.entry.inUse.showEncrypted=Afficher le fichier chiffré
|
||||
eventView.entry.inUse.copyEncrypted=Copier le chemin chiffré
|
||||
eventView.entry.inUse.copyUserAndDevice=Copier le verrouillage de l'utilisateur et le nom de l'appareil
|
||||
eventView.entry.inUse.ignoreLock=Ignorer le verrou
|
||||
eventView.entry.inUse.ignoreLock=Ignorer le statut d’utilisation
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=Le fichier est verrouillé par 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.action=Ignorer le verrou
|
||||
notification.inUse.message=Le fichier est utilisé sur un autre appareil
|
||||
notification.inUse.description=Le fichier est ouvert par %s sur %s. Demandez-lui à fermer le fichier et laissez la synchronisation se terminer. Vous pouvez ignorer le statut pour l’ouvrir maintenant, mais cela pourrait entraîner des conflits ou écraser des modifications récentes.
|
||||
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.conflictResolved.message=Conflitto risolto
|
||||
eventView.entry.conflictResolved.showDecrypted=Mostra file decifrato
|
||||
eventView.entry.conflictResolved.copyDecrypted=Copia percorso decriptato
|
||||
eventView.entry.conflict.message=Risoluzione dei conflitti fallita
|
||||
eventView.entry.conflict.showDecrypted=Mostra il file originale decifrato
|
||||
eventView.entry.conflict.copyDecrypted=Copia il percorso originale decifrato
|
||||
eventView.entry.conflict.showEncrypted=Mostra file cifrato in conflitto
|
||||
eventView.entry.conflict.copyEncrypted=Copia il percorso cifrato in conflitto
|
||||
eventView.entry.decryptionFailed.message=Decifratura fallita
|
||||
eventView.entry.decryptionFailed.showEncrypted=Mostra file cifrato
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Copia percorso cifrato
|
||||
eventView.entry.brokenDirFile.message=Collegamento directory errato
|
||||
eventView.entry.brokenDirFile.showEncrypted=Visualizza il link cifrato errato
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Copia il percorso errato del file cifrato
|
||||
eventView.entry.brokenFileNode.message=Nodo file errato
|
||||
eventView.entry.brokenFileNode.showEncrypted=Visualizza il nodo file cifrato errato
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Copia il percorso errato del nodo file cifrato
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Copia il percorso decifrato
|
||||
eventView.entry.inUse.message=File bloccato
|
||||
eventView.entry.inUse.message=File in uso
|
||||
eventView.entry.inUse.showDecrypted=Mostra file decifrato
|
||||
eventView.entry.inUse.copyDecrypted=Copia percorso decriptato
|
||||
eventView.entry.inUse.showEncrypted=Mostra file cifrato
|
||||
eventView.entry.inUse.copyEncrypted=Copia percorso cifrato
|
||||
eventView.entry.inUse.copyUserAndDevice=Copia il nome del dispositivo e dell'utente che sta bloccando
|
||||
eventView.entry.inUse.ignoreLock=Ignora blocco
|
||||
eventView.entry.inUse.ignoreLock=Ignora lo stato di utilizzo
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=Il file è bloccato da un altro dispositivo
|
||||
notification.inUse.description=Il file è aperto da %s sul dispositivo %s. Chiedi all'utente di chiudere il file e sincronizzalo di nuovo. In caso contrario, puoi ignorare il blocco e aprirlo comunque.
|
||||
notification.inUse.action=Ignora blocco
|
||||
notification.inUse.message=Il file è in uso su un altro dispositivo
|
||||
notification.inUse.description=Il file è aperto da %s su %s. Chiedigli di chiudere il file e permettere che la sincronizzazione termini. È possibile ignorare lo stato ed aprirlo ora, ma questo può causare conflitti o la sovrascrittura delle modifiche più recenti.
|
||||
notification.inUse.action=Ignora lo Stato di Utilizzo
|
||||
@@ -626,16 +626,10 @@ decryptNames.dropZone.error.generic=ファイル名の復号化に失敗しま
|
||||
|
||||
# Event View
|
||||
## event list entries
|
||||
eventView.entry.inUse.message=ロックされたファイル
|
||||
eventView.entry.inUse.showDecrypted=復号化されたファイルを表示
|
||||
eventView.entry.inUse.copyDecrypted=復号化されたパスをコピー
|
||||
eventView.entry.inUse.showEncrypted=暗号化されたファイルを表示
|
||||
eventView.entry.inUse.copyEncrypted=暗号化されたパスをコピー
|
||||
eventView.entry.inUse.copyUserAndDevice=ロックしているユーザーとデバイス名をコピー
|
||||
eventView.entry.inUse.ignoreLock=ロックを無視する
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=ファイルは別のデバイスによってロックされています
|
||||
notification.inUse.action=ロックを無視する
|
||||
## FileIsInUse Notification
|
||||
@@ -42,7 +42,7 @@ defaults.vault.vaultName=Vault
|
||||
|
||||
# Tray Menu
|
||||
traymenu.showMainWindow=보기
|
||||
traymenu.showPreferencesWindow=환경설정
|
||||
traymenu.showPreferencesWindow=환경 설정
|
||||
traymenu.lockAllVaults=모두 잠그기
|
||||
traymenu.quitApplication=종료
|
||||
traymenu.vault.unlock=잠금 해제
|
||||
@@ -114,7 +114,7 @@ addvaultwizard.success.nextStepsInstructions="%s" Vault가 추가되었습니다
|
||||
addvaultwizard.success.unlockNow=지금 잠금 해제
|
||||
|
||||
# Remove Vault
|
||||
removeVault.title=Vault 제거
|
||||
removeVault.title=Vault "%s" 제거
|
||||
removeVault.message=Vault를 삭제하시겠습니까?
|
||||
removeVault.description=이 행위는 Cryptomator에서만 이 Vault를 지웁니다. 나중에 다시 추가할 수 있습니다. 암호화된 파일은 하드디스크에서 삭제되지 않습니다.
|
||||
|
||||
@@ -132,18 +132,18 @@ forgetPassword.confirmBtn=비밀번호 삭제
|
||||
# Unlock
|
||||
unlock.title="%s" 잠금 해제
|
||||
unlock.passwordPrompt="%s"의 비밀번호를 입력하십시오.
|
||||
unlock.savePassword=비밀번호 기억
|
||||
unlock.savePassword=비밀번호 기억하기
|
||||
unlock.unlockBtn=잠금 해제
|
||||
## Select
|
||||
unlock.chooseMasterkey.message=마스터키 파일을 찾을 수 없습니다
|
||||
unlock.chooseMasterkey.description=이 Vault의 Masterkey를 찾지 못했습니다. 마스터 키 위치를 수동으로 선택하여 주십시오.
|
||||
unlock.chooseMasterkey.description=이 "%s" Vault의 마스터키를 찾지 못했습니다. 마스터키 위치를 수동으로 선택하여 주십시오.
|
||||
unlock.chooseMasterkey.restoreInstead=대신 마스터키 파일 복구
|
||||
unlock.chooseMasterkey.filePickerTitle=Masterkey 파일 선택
|
||||
unlock.chooseMasterkey.filePickerMimeDesc=Cryptomator Masterkey
|
||||
## Success
|
||||
unlock.success.message=잠금 해제 성공
|
||||
unlock.success.description="%s"이(가) 성공적으로 잠금 해제되었습니다. 이제 이 Vault를 마운트 지점으로 접근할 수 있습니다.
|
||||
unlock.success.rememberChoice=선택 기억하기, 다시 묻지 않음
|
||||
unlock.success.rememberChoice=선택을 기억하고 다시 묻지 않음
|
||||
unlock.success.revealBtn=드라이브 표시
|
||||
## Failure
|
||||
unlock.error.customPath.message=Vault를 사용자 정의 경로에 마운트할 수 없습니다.
|
||||
@@ -166,7 +166,7 @@ hub.auth.message=인증 대기 중…
|
||||
hub.auth.description=자동으로 로그인 페이지로 리다이렉트 될 것입니다.
|
||||
hub.auth.loginLink=수동으로 열려면 클릭하십시오.
|
||||
### Receive Key
|
||||
hub.receive.message=응답 처리중…
|
||||
hub.receive.message=응답 처리 중…
|
||||
hub.receive.description=Hub로부터 응답을 처리하고 있습니다. 잠시만 기다려 주십시오.
|
||||
### Register Device
|
||||
hub.register.message=새 기기
|
||||
@@ -181,7 +181,7 @@ hub.register.legacy.description=이 기기로부터 첫번째 Hub 접근입니
|
||||
hub.registerSuccess.message=기기 등록됨
|
||||
hub.registerSuccess.description=등록에 성공하였습니다. Vault를 잠금 해제할 수 있습니다.
|
||||
hub.registerSuccess.unlockBtn=잠금 해제
|
||||
hub.registerSuccess.legacy.description=Vault에 접근하기 위해서는 이 기기를 Vault 소유주가 추가적으로 허가해야 합니다.
|
||||
hub.registerSuccess.legacy.description=Vault에 접근하기 위해서는 이 기기를 Vault 소유자가 추가적으로 허가해야 합니다.
|
||||
### Registration Failed
|
||||
hub.registerFailed.message=기기 등록 실패
|
||||
hub.registerFailed.description.generic=등록 중에 오류가 발생했습니다. 앱 로그에서 자세한 정보를 확인할 수 있습니다.
|
||||
@@ -209,18 +209,18 @@ lock.forced.retryBtn=재시도
|
||||
lock.forced.forceBtn=강제 잠금
|
||||
## Failure
|
||||
lock.fail.message=Vault 잠금에 실패하였습니다.
|
||||
lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료 하십시오.
|
||||
lock.fail.description="%s" Vault를 잠글 수 없습니다. 저장되지 않은 작업이 다른 곳에 저장된 것과 중요한 읽기/쓰기 동작이 완료되었는지 확인 하십시요. Vault를 닫기 위해, Cryptomator 프로세스를 강제로 종료하십시오.
|
||||
|
||||
# Migration
|
||||
migration.title=Vault 업그레이드
|
||||
## Start
|
||||
migration.start.header=Vault 업그레이드
|
||||
migration.start.text=Vault "%s"를 현재 버전의 Cryptomator에서 열기 위해서는 해당 vault를 새 버전으로 업그레이드해야 합니다. 업그레이드를 하기 전에 다음 사항들을 알고 있어야 합니다:
|
||||
migration.start.text=Vault "%s"를 현재 버전의 Cryptomator에서 열기 위해서는 해당 Vault를 새 버전으로 업그레이드해야 합니다. 업그레이드를 하기 전에 다음 사항들을 알고 있어야 합니다:
|
||||
migration.start.remarkUndone=이 업그레이드는 되돌릴 수 없습니다.
|
||||
migration.start.remarkVersions=과거 버전의 Cryptomator는 업그레이드된 Vault를 열 수 없습니다.
|
||||
migration.start.remarkCanRun=이 Vault를 열 때 사용하는 모든 기기가 현재 버전의 Cryptomator를 실행할 수 있는지 확인해야 합니다.
|
||||
migration.start.remarkSynced=업그레이드하기 전에 해당 Vault가 모든 기기에 정상적으로 동기화되어야 합니다.
|
||||
migration.start.confirm=나는 위 정보를 읽고 정말 이해했습니다.
|
||||
migration.start.confirm=위 내용을 충분히 숙지하였음을 확인합니다.
|
||||
## Run
|
||||
migration.run.enterPassword="%s"의 비밀번호를 입력하십시오.
|
||||
migration.run.startMigrationBtn=Vault 마이그레이션
|
||||
@@ -231,8 +231,8 @@ migration.success.unlockNow=지금 잠금 해제
|
||||
## Missing file system capabilities
|
||||
migration.error.missingFileSystemCapabilities.title=지원하지 않는 파일 시스템
|
||||
migration.error.missingFileSystemCapabilities.description=Vault가 부적절한 파일 시스템에 있기 때문에 마이그레이션이 시작되지 않았습니다.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=너무 긴 파일 이름을 파일 시스템에서 지원하지 않습니다.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=너무 긴 경로를 파일 시스템에서 지원하지 않습니다.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=파일 시스템이 긴 파일 이름을 지원하지 않습니다.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=파일 시스템이 긴 경로를 지원하지 않습니다.
|
||||
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=파일 시스템이 읽기를 허용하지 않습니다.
|
||||
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=파일 시스템이 쓰기를 허용하지 않습니다.
|
||||
## Impossible
|
||||
@@ -267,7 +267,7 @@ health.check.detail.checkSkipped=선택된 검사항목이 없습니다.
|
||||
health.check.detail.checkFinished=검사가 성공적으로 완료되었습니다.
|
||||
health.check.detail.checkFinishedAndFound=검사가 완료되었습니다. 검사 결과를 확인해주세요.
|
||||
health.check.detail.checkFailed=오류로 인해 검사가 종료되었습니다.
|
||||
health.check.detail.checkCancelled=검사가 취소되었습니다
|
||||
health.check.detail.checkCancelled=검사가 취소되었습니다.
|
||||
health.check.detail.listFilters.label=필터
|
||||
health.check.detail.filterSeverity=중요도로 정렬
|
||||
health.check.detail.filterFixState=해결 상태로 정렬
|
||||
@@ -286,7 +286,7 @@ health.result.severityTip.crit=상태: 심각\nVault 구조가 손상되었습
|
||||
health.result.fixStateFilter.all=모든 문제 해결 상태
|
||||
health.result.fixStateFilter.fixable=문제 해결 가능
|
||||
health.result.fixStateFilter.notFixable=문제 해결 불가
|
||||
health.result.fixStateFilter.fixing=문제 해결중…
|
||||
health.result.fixStateFilter.fixing=문제 해결 중…
|
||||
health.result.fixStateFilter.fixed=문제 해결됨
|
||||
health.result.fixStateFilter.fixFailed=문제 해결 실패
|
||||
## Fix Application
|
||||
@@ -295,7 +295,7 @@ health.fix.successTip=문제 해결이 성공적으로 완료되었습니다
|
||||
health.fix.failTip=문제 해결 실패, 상세 정보는 로그를 참조하십시오.
|
||||
|
||||
# Preferences
|
||||
preferences.title=환경설정
|
||||
preferences.title=환경 설정
|
||||
## General
|
||||
preferences.general=일반
|
||||
preferences.general.startHidden=Cryptomator를 시작할 때 창 숨김
|
||||
@@ -309,9 +309,9 @@ preferences.general.quickAccessService=열린 Vault를 빠른 접근 위치에
|
||||
preferences.interface=인터페이스
|
||||
preferences.interface.theme=테마
|
||||
preferences.interface.theme.automatic=자동
|
||||
preferences.interface.theme.dark=어둡게
|
||||
preferences.interface.theme.light=밝게
|
||||
preferences.interface.unlockThemes=다크모드 해제
|
||||
preferences.interface.theme.dark=다크 모드
|
||||
preferences.interface.theme.light=라이트 모드
|
||||
preferences.interface.unlockThemes=다크 모드 사용 권한을 얻어보세요!
|
||||
preferences.interface.language=언어 (재시작 필요)
|
||||
preferences.interface.language.auto=시스템 기본 설정
|
||||
preferences.interface.interfaceOrientation=인터페이스 방향
|
||||
@@ -356,7 +356,7 @@ preferences.contribute=후원하기
|
||||
preferences.contribute.registeredFor=%s(으)로 후원자 인증 등록됨
|
||||
preferences.contribute.noCertificate=Cryptomator를 후원하시고 후원자 인증을 받으십시오. 라이선스 키와 비슷하지만 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-)
|
||||
preferences.contribute.getCertificate=아직 후원자 인증이 없으신가요? 어떻게 얻는지 배울 수 있습니다.
|
||||
preferences.contribute.promptText=후원자 인증코드를 여기에 붙여넣기
|
||||
preferences.contribute.promptText=후원자 인증 코드를 여기에 붙여넣기
|
||||
preferences.contribute.thankYou=Cryptomator의 오픈 소스 개발을 지원해 주셔서 감사합니다!
|
||||
preferences.contribute.donate=후원하기
|
||||
preferences.contribute.sponsor=스폰서
|
||||
@@ -364,7 +364,7 @@ preferences.contribute.removeCert.tooltip=인증서 제거
|
||||
|
||||
### Remove License Key Dialog
|
||||
removeCert.title=인증서 제거
|
||||
removeCert.message=서포터 인증서를 제거하시겠습니까?
|
||||
removeCert.message=후원자 인증서를 제거하시겠습니까?
|
||||
removeCert.description=Cryptomator의 핵심 기능은 영향을 받지 않습니다. Vault에 대한 접근이 제한되거나 보안이 약화되지 않습니다.
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
@@ -392,7 +392,7 @@ stats.read.accessCount=총 읽기 횟수: %d
|
||||
stats.write.throughput.idle=쓰기: 대기 중
|
||||
stats.write.throughput.kibs=쓰기: %.2f KiB/s
|
||||
stats.write.throughput.mibs=쓰기: %.2f MiB/s
|
||||
stats.write.total.data.none=데이터 기록됨: -
|
||||
stats.write.total.data.none=데이터 쓰기: -
|
||||
stats.write.total.data.kib=데이터 쓰기: %.1f KiB
|
||||
stats.write.total.data.mib=데이터 쓰기: %.1f MiB
|
||||
stats.write.total.data.gib=데이터 쓰기: %.1f GiB
|
||||
@@ -427,11 +427,11 @@ main.vaultlist.showEventsButton.tooltip=이벤트 뷰어 열기
|
||||
main.vaultlist.showPreferencesButton.tooltip=환경 설정 표시
|
||||
##Notification
|
||||
main.notification.updateAvailable=업데이트가 있습니다.
|
||||
main.notification.support=Cryptomator 지원하기.
|
||||
main.notification.support=Cryptomator 지원해 주세요
|
||||
main.notification.closeButton.tooltip=정보 표시줄 닫기
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=파일을 보호하기 위해 Cryptomator를 선택해주셔서 감사합니다. 만약 다른 도움이 필요하시면, 시작 안내서를 참조하시기 바랍니다.
|
||||
main.vaultDetail.welcomeOnboarding=파일 보호를 위해 Cryptomator를 선택해 주셔서 감사합니다. 도움이 필요하시면 시작 가이드를 확인해 주십시오:
|
||||
main.vaultDetail.storageLocation=Vault 저장 위치
|
||||
### Locked
|
||||
main.vaultDetail.lockedStatus=잠김
|
||||
@@ -695,32 +695,25 @@ eventView.cell.actionsButton.tooltip=이벤트별 동작
|
||||
eventView.entry.vaultLocked.description="%s"를 잠금 해제하여 세부정보 보기
|
||||
eventView.entry.conflictResolved.message=해결된 충돌
|
||||
eventView.entry.conflictResolved.showDecrypted=복호화된 파일 보기
|
||||
eventView.entry.conflictResolved.copyDecrypted=복호화된 경로 복사하기
|
||||
eventView.entry.conflict.message=충돌 해결 실패
|
||||
eventView.entry.conflict.showDecrypted=복호화된 원본 파일 보기
|
||||
eventView.entry.conflict.copyDecrypted=복호화된 원본 경로 복사하기
|
||||
eventView.entry.conflict.showEncrypted=충돌하는 암호화된 파일 보기
|
||||
eventView.entry.conflict.copyEncrypted=충돌하는 암호화된 경로 복사하기
|
||||
eventView.entry.decryptionFailed.message=복호화 실패
|
||||
eventView.entry.decryptionFailed.showEncrypted=암호화된 파일 보기
|
||||
eventView.entry.decryptionFailed.copyEncrypted=암호화된 경로 복사하기
|
||||
eventView.entry.brokenDirFile.message=망가진 디렉터리 링크
|
||||
eventView.entry.brokenDirFile.showEncrypted=망가진 암호화된 링크 보기
|
||||
eventView.entry.brokenDirFile.copyEncrypted=망가진 링크의 경로 복사하리
|
||||
eventView.entry.brokenFileNode.message=망가진 파일시스템 노드
|
||||
eventView.entry.brokenFileNode.message=망가진 파일 시스템 노드
|
||||
eventView.entry.brokenFileNode.showEncrypted=망가진 암호화된 노드 보기
|
||||
eventView.entry.brokenFileNode.copyEncrypted=망가진 암호화된 노드의 경로 복사하기
|
||||
eventView.entry.brokenFileNode.copyDecrypted=복호화된 경로 복사하기
|
||||
eventView.entry.inUse.message=잠긴 파일
|
||||
eventView.entry.inUse.message=파일 사용 중
|
||||
eventView.entry.inUse.showDecrypted=복호화된 파일 보기
|
||||
eventView.entry.inUse.copyDecrypted=복호화된 경로 복사하기
|
||||
eventView.entry.inUse.showEncrypted=암호화된 파일 보기
|
||||
eventView.entry.inUse.copyEncrypted=암호화된 경로 복사하기
|
||||
eventView.entry.inUse.copyUserAndDevice=파일 잠금 사용자 및 기기 이름 복사
|
||||
eventView.entry.inUse.ignoreLock=잠금 무시
|
||||
eventView.entry.inUse.ignoreLock=사용 여부 상태 무시
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=다른 기기에서 파일이 잠겼습니다.
|
||||
notification.inUse.action=잠금 무시
|
||||
notification.inUse.message=다른 기기에서 파일 사용 중
|
||||
notification.inUse.description=해당 파일은 %s이(가) %s의 컴퓨터에서 열어 놓은 상태입니다. 파일을 닫고 동기화가 완료될 때까지 기다리도록 요청하세요. 상태를 무시하고 지금 파일을 열 수도 있지만, 이렇게 하면 충돌이 발생하거나 최신 변경 사항이 덮어쓰여질 수 있습니다.
|
||||
notification.inUse.action=사용 여부 상태 무시
|
||||
@@ -694,31 +694,25 @@ eventView.cell.actionsButton.tooltip=Notikumu darbības
|
||||
eventView.entry.vaultLocked.description=Atslēgt "%s", lai redzētu informāciju
|
||||
eventView.entry.conflictResolved.message=Atrisināta nesaderība
|
||||
eventView.entry.conflictResolved.showDecrypted=Parādīt atšifrēto datni
|
||||
eventView.entry.conflictResolved.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu
|
||||
eventView.entry.conflict.message=Nesaderības atrisināšana neizdevās
|
||||
eventView.entry.conflict.showDecrypted=Parādīt atšifrēto, sākotnējo datni
|
||||
eventView.entry.conflict.copyDecrypted=Ievietot starpliktuvē atšifrēto, sākotnējo datni
|
||||
eventView.entry.conflict.showEncrypted=Parādīt nesaderīgo, šifrēto datni
|
||||
eventView.entry.conflict.copyEncrypted=Ievietot starpliktuvē nesaderīgo, šifrēto ceļu
|
||||
eventView.entry.decryptionFailed.message=Atšifrēšana neizdevās
|
||||
eventView.entry.decryptionFailed.showEncrypted=Parādīt šifrēto datni
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Ievietot starpliktuvē šifrēto ceļu
|
||||
eventView.entry.brokenDirFile.message=Bojāta mapes saite
|
||||
eventView.entry.brokenDirFile.showEncrypted=Parādīt bojāto, šifrēto saiti
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Ievietot starpliktuvē bojātās saites ceļu
|
||||
eventView.entry.brokenFileNode.message=Bojāts datņu sistēmas mezgls
|
||||
eventView.entry.brokenFileNode.showEncrypted=Parādīt bojāto, šifrēto mezglu
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Ievietot starpliktuvē botjātā, šifrētā mezgla ceļu
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu
|
||||
eventView.entry.inUse.message=Aizslēgta datne
|
||||
eventView.entry.inUse.message=Datne tiek izmantota
|
||||
eventView.entry.inUse.showDecrypted=Parādīt atšifrēto datni
|
||||
eventView.entry.inUse.copyDecrypted=Ievietot starpliktuvē atšifrēto ceļu
|
||||
eventView.entry.inUse.showEncrypted=Parādīt šifrēto datni
|
||||
eventView.entry.inUse.copyEncrypted=Ievietot starpliktuvē šifrēto ceļu
|
||||
eventView.entry.inUse.copyUserAndDevice=Ievietot starpliktuvē lietotāju un ierīces, kurā aizslēgta, nosaukumu
|
||||
eventView.entry.inUse.ignoreLock=Neņemt vērā slēgu
|
||||
eventView.entry.inUse.ignoreLock=Neņemt vēra izmantošanas stāvokli
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.action=Neņemt vērā slēgu
|
||||
notification.inUse.message=Datne tiek izmantota citā ierīcē
|
||||
notification.inUse.description=Datni atvēra %s ierīcē %s. Jāvaicā aizvērt datni un ļaut pabeigties sinhronizēšanai. Stāvokli tagad var neņemt vērā, lai atvērtu datni, bet tas var radīt nesaderības vai pārrakstīt jaunākas izmaiņas.
|
||||
notification.inUse.action=Neņemt vēra izmantošanas stāvokli
|
||||
@@ -695,33 +695,25 @@ eventView.cell.actionsButton.tooltip=Acties op Gebeurtenissen
|
||||
eventView.entry.vaultLocked.description=Ontgrendel "%s" voor details
|
||||
eventView.entry.conflictResolved.message=Opgelost conflict
|
||||
eventView.entry.conflictResolved.showDecrypted=Toon gedecodeerd bestand
|
||||
eventView.entry.conflictResolved.copyDecrypted=Kopieer gedecodeerd pad
|
||||
eventView.entry.conflict.message=Conflictoplossing is mislukt
|
||||
eventView.entry.conflict.showDecrypted=Toon gedecodeerd, origineel bestand
|
||||
eventView.entry.conflict.copyDecrypted=Kopieer gedecodeerd, origineel pad
|
||||
eventView.entry.conflict.showEncrypted=Conflicterend, versleuteld bestand weergeven
|
||||
eventView.entry.conflict.copyEncrypted=Conflicterend, versleuteld pad kopiëren
|
||||
eventView.entry.decryptionFailed.message=Decodering mislukt
|
||||
eventView.entry.decryptionFailed.showEncrypted=Toon gedecodeerd bestand
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Kopieer gedecodeerd pad
|
||||
eventView.entry.brokenDirFile.message=Verbroken directory link
|
||||
eventView.entry.brokenDirFile.showEncrypted=Toon verbroken gecodeerde link
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Kopieer pad van verbroken link
|
||||
eventView.entry.brokenFileNode.message=Kapot bestandssysteemknooppunt
|
||||
eventView.entry.brokenFileNode.showEncrypted=Toon verbroken gecodeerde link
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Kopieer pad van verbroken, versleuteld knooppunt
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Kopieer gedecodeerd pad
|
||||
eventView.entry.inUse.message=Vergrendeld Bestand
|
||||
eventView.entry.inUse.message=Bestand in gebruik
|
||||
eventView.entry.inUse.showDecrypted=Toon gedecodeerd bestand
|
||||
eventView.entry.inUse.copyDecrypted=Kopieer gedecodeerd pad
|
||||
eventView.entry.inUse.showEncrypted=Toon gedecodeerd bestand
|
||||
eventView.entry.inUse.copyEncrypted=Kopieer gedecodeerd pad
|
||||
eventView.entry.inUse.copyUserAndDevice=Kopieer vergrendeling gebruikersnaam en apparaatnaam
|
||||
eventView.entry.inUse.ignoreLock=Vergrendeling negeren
|
||||
eventView.entry.inUse.ignoreLock=Gebruiksstatus negeren
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.message=Bestand is vergrendeld door een ander apparaat
|
||||
notification.inUse.description=Het bestand wordt geopend door %s op apparaat %s. Vraag de gebruiker om het bestand te sluiten en opnieuw te synchroniseren. Anders kunt u de vergrendeling negeren en het toch openen.
|
||||
notification.inUse.action=Vergrendeling negeren
|
||||
notification.inUse.message=Bestand is op een ander apparaat in gebruik
|
||||
notification.inUse.description=Het bestand is geopend door %s op %s. Vraag ze om het bestand te sluiten en laat de synchronisatie eindigen. Je kunt de status negeren om het nu te openen, maar dit kan conflicten veroorzaken of nieuwere wijzigingen overschrijven.
|
||||
notification.inUse.action=Gebruiksstatus negeren
|
||||
@@ -573,17 +573,13 @@ eventView.clearListButton.tooltip=ਸੂਚੀ ਨੂੰ ਮਿਟਾਓ
|
||||
## event list entries
|
||||
eventView.entry.vaultLocked.description=ਵੇਰਵਿਆਂ ਲਈ "%s" ਨੂੰ ਅਣ-ਲਾਕ ਕਰੋ
|
||||
eventView.entry.conflictResolved.showDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
|
||||
eventView.entry.conflictResolved.copyDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
|
||||
eventView.entry.decryptionFailed.message=ਡਿਕ੍ਰਿਪਟ ਕਰਨਾ ਅਸਫ਼ਲ ਹੈ
|
||||
eventView.entry.decryptionFailed.showEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
|
||||
eventView.entry.decryptionFailed.copyEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
|
||||
eventView.entry.brokenDirFile.message=ਡਾਇਰੈਕਟਰੀ ਲਿੰਕ ਖ਼ਰਾਬ ਹੈ
|
||||
eventView.entry.brokenDirFile.showEncrypted=ਖ਼ਰਾਬ, ਇੰੰਕ੍ਰਿਪਟ ਕੀਤੇ ਲਿੰਕ ਨੂੰ ਵੇਖਾਓ
|
||||
eventView.entry.brokenFileNode.copyDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
|
||||
eventView.entry.inUse.showDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
|
||||
eventView.entry.inUse.copyDecrypted=ਡਿਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
|
||||
eventView.entry.inUse.showEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੀ ਫ਼ਾਇਲ ਨੂੰ ਵੇਖੋ
|
||||
eventView.entry.inUse.copyEncrypted=ਇੰਕ੍ਰਿਪਟ ਕੀਤੇ ਪਾਥ ਨੂੰ ਕਾਪੀ ਕਰੋ
|
||||
|
||||
|
||||
# Notifications
|
||||
|
||||
@@ -695,31 +695,20 @@ eventView.cell.actionsButton.tooltip=Akcje zdarzeń
|
||||
eventView.entry.vaultLocked.description=Odblokuj "%s" po szczegóły
|
||||
eventView.entry.conflictResolved.message=Konflikt rozwiązany
|
||||
eventView.entry.conflictResolved.showDecrypted=Pokaż odszyfrowany plik
|
||||
eventView.entry.conflictResolved.copyDecrypted=Kopiuj odszyfrowaną ścieżkę
|
||||
eventView.entry.conflict.message=Rozwiązywanie konfliktu nie powiodło się
|
||||
eventView.entry.conflict.showDecrypted=Pokaż odszyfrowany, oryginalny plik
|
||||
eventView.entry.conflict.copyDecrypted=Kopiuj odszyfrowaną, oryginalną ścieżkę
|
||||
eventView.entry.conflict.showEncrypted=Pokaż sprzeczny, zaszyfrowany plik
|
||||
eventView.entry.conflict.copyEncrypted=Kopiuj sprzeczną, zaszyfrowaną ścieżkę
|
||||
eventView.entry.decryptionFailed.message=Odszyfrowywanie nie powiodło się
|
||||
eventView.entry.decryptionFailed.showEncrypted=Pokaż zaszyfrowany plik
|
||||
eventView.entry.decryptionFailed.copyEncrypted=Kopiuj zaszyfrowaną ścieżkę
|
||||
eventView.entry.brokenDirFile.message=Uszkodzony link do katalogu
|
||||
eventView.entry.brokenDirFile.showEncrypted=Pokaż uszkodzony, zaszyfrowany link
|
||||
eventView.entry.brokenDirFile.copyEncrypted=Kopiuj ścieżkę uszkodzonego linku
|
||||
eventView.entry.brokenFileNode.message=Uszkodzony węzeł systemu plików
|
||||
eventView.entry.brokenFileNode.showEncrypted=Pokaż uszkodzony, zaszyfrowany węzeł
|
||||
eventView.entry.brokenFileNode.copyEncrypted=Kopiuj ścieżkę uszkodzonego, zaszyfrowanego węzła
|
||||
eventView.entry.brokenFileNode.copyDecrypted=Kopiuj odszyfrowaną ścieżkę
|
||||
eventView.entry.inUse.message=Zablokowany plik
|
||||
eventView.entry.inUse.showDecrypted=Pokaż odszyfrowany plik
|
||||
eventView.entry.inUse.copyDecrypted=Kopiuj odszyfrowaną ścieżkę
|
||||
eventView.entry.inUse.showEncrypted=Pokaż zaszyfrowany plik
|
||||
eventView.entry.inUse.copyEncrypted=Kopiuj zaszyfrowaną ścieżkę
|
||||
eventView.entry.inUse.copyUserAndDevice=Kopiowanie blokady użytkownika i nazwy urządzenia
|
||||
eventView.entry.inUse.ignoreLock=Ignoruj blokadę
|
||||
|
||||
|
||||
# Notifications
|
||||
## FileIsInUse Notification
|
||||
notification.inUse.action=Ignoruj blokadę
|
||||
## FileIsInUse Notification
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user