mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-21 23:44:42 +00:00
Compare commits
147
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b6afb5fd6 | ||
|
|
d2b1f86d85 | ||
|
|
b01478f0fa | ||
|
|
27eb6298ae | ||
|
|
2c53afd70f | ||
|
|
ae84f2f2fd | ||
|
|
ad17f0b835 | ||
|
|
e7db172a89 | ||
|
|
222e1f18ad | ||
|
|
9a3baf7119 | ||
|
|
53baf3c648 | ||
|
|
c8a6b7cf13 | ||
|
|
5d12a62e38 | ||
|
|
40bb0079ac | ||
|
|
fbdf511f05 | ||
|
|
410900ed51 | ||
|
|
1302c1fc34 | ||
|
|
ea806ec97a | ||
|
|
77dbf9414a | ||
|
|
a3fc50fc7e | ||
|
|
4b0e8b5841 | ||
|
|
688d1a4a24 | ||
|
|
56e655c5a9 | ||
|
|
4c9dbb60fe | ||
|
|
7ddedea5c2 | ||
|
|
fc76f5f83f | ||
|
|
bfc8542551 | ||
|
|
24a20911f1 | ||
|
|
b681c26d72 | ||
|
|
34f5c06795 | ||
|
|
f4928fd981 | ||
|
|
f748bfd953 | ||
|
|
cb689946ed | ||
|
|
38bdf6d0e4 | ||
|
|
409209fd89 | ||
|
|
1fd44ff651 | ||
|
|
9bb19b0130 | ||
|
|
1b5319f706 | ||
|
|
33b4aa5ba3 | ||
|
|
1879c6fa77 | ||
|
|
8b4c4dca8e | ||
|
|
32c0327058 | ||
|
|
3be6b6eceb | ||
|
|
1ff01e17f4 | ||
|
|
c932810bd8 | ||
|
|
4b6c4d1ce4 | ||
|
|
3d76ed5048 | ||
|
|
47d2900f37 | ||
|
|
95f5c7aecd | ||
|
|
bda019f0b4 | ||
|
|
69e87c067e | ||
|
|
fc7cf0da2d | ||
|
|
c3de54f256 | ||
|
|
40f0a363c9 | ||
|
|
09e8cc7fdf | ||
|
|
b4ea03a7cd | ||
|
|
a30e887177 | ||
|
|
a428ae6260 | ||
|
|
9c41e348fa | ||
|
|
331f66db0c | ||
|
|
dd5827204c | ||
|
|
8bee60975e | ||
|
|
f96a03ae30 | ||
|
|
41ce350b61 | ||
|
|
64b0e63b21 | ||
|
|
6c62b4d293 | ||
|
|
3f312b183e | ||
|
|
719a91692a | ||
|
|
85b7265b95 | ||
|
|
fbdf1b1bdb | ||
|
|
ae2fd74411 | ||
|
|
49ca7f4891 | ||
|
|
d5d992b1c2 | ||
|
|
01b30b173e | ||
|
|
be4a39c628 | ||
|
|
e63a0e8ee8 | ||
|
|
6c925d6a1f | ||
|
|
602c206dae | ||
|
|
426b33ae1a | ||
|
|
472b10a8a4 | ||
|
|
8e52161fa4 | ||
|
|
f1540e13eb | ||
|
|
2c2165cb22 | ||
|
|
03cb5f6be9 | ||
|
|
8a98048835 | ||
|
|
2fe7fe886c | ||
|
|
459112f5b8 | ||
|
|
ea86124c7d | ||
|
|
cd9e315447 | ||
|
|
1266926ebb | ||
|
|
1e6bc1f043 | ||
|
|
1230e787a9 | ||
|
|
da8493f25f | ||
|
|
4a0a4309c2 | ||
|
|
c52ae0120d | ||
|
|
e6cef947c1 | ||
|
|
08009ca1a0 | ||
|
|
d27a52752b | ||
|
|
c745fca2ea | ||
|
|
d1fcf528b0 | ||
|
|
b6133e481c | ||
|
|
d67aa9c10a | ||
|
|
19a9595f2e | ||
|
|
f21ae0e11c | ||
|
|
54f805a0c9 | ||
|
|
aa239418cf | ||
|
|
8f0c6eb994 | ||
|
|
4751c81d06 | ||
|
|
0e1132133c | ||
|
|
c7d77091f5 | ||
|
|
3a4039d663 | ||
|
|
f94d204604 | ||
|
|
d76dbe0ddb | ||
|
|
235a23ce77 | ||
|
|
357b30684b | ||
|
|
517e12a586 | ||
|
|
96aa8fe180 | ||
|
|
7bffc317ff | ||
|
|
7dde389671 | ||
|
|
a40f17e8f1 | ||
|
|
699e22d31a | ||
|
|
5d5b9137cc | ||
|
|
02767b0caf | ||
|
|
ddd8c572e7 | ||
|
|
aa8ccf53df | ||
|
|
c199561a9e | ||
|
|
e341983ffe | ||
|
|
f10b28ecb1 | ||
|
|
95eed96a37 | ||
|
|
9de0981594 | ||
|
|
4d6ce70afe | ||
|
|
e75deb282c | ||
|
|
87fe209b54 | ||
|
|
21cffb13c7 | ||
|
|
ee92cc4e9f | ||
|
|
9eca05aeb7 | ||
|
|
57c84627f9 | ||
|
|
309a8fc705 | ||
|
|
0b77e6ce04 | ||
|
|
d22e912ca4 | ||
|
|
690726efc3 | ||
|
|
d9c0c4980b | ||
|
|
4e5eef5a16 | ||
|
|
81b2081033 | ||
|
|
d9134b49ad | ||
|
|
92d11d8bd5 | ||
|
|
300a811510 |
@@ -48,7 +48,7 @@ runs:
|
|||||||
echo "client-secret=${{ inputs.client-secret }}" >> "$GITHUB_OUTPUT"
|
echo "client-secret=${{ inputs.client-secret }}" >> "$GITHUB_OUTPUT"
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Sign DLLs with Azure Trusted Signing
|
- name: Sign DLLs with Azure Trusted Signing
|
||||||
uses: azure/artifact-signing-action@87c2e83e6868da99d3380aa309851b32ed9a8346 # v1.1.0
|
uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0
|
||||||
with:
|
with:
|
||||||
files-folder: ${{ inputs.base-dir }}
|
files-folder: ${{ inputs.base-dir }}
|
||||||
files-folder-filter: ${{ inputs.file-extensions }}
|
files-folder-filter: ${{ inputs.file-extensions }}
|
||||||
|
|||||||
+4
-36
@@ -3,51 +3,19 @@ updates:
|
|||||||
- package-ecosystem: "maven"
|
- package-ecosystem: "maven"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "monthly"
|
||||||
day: "monday"
|
|
||||||
time: "06:00"
|
|
||||||
timezone: "Etc/UTC"
|
|
||||||
ignore:
|
ignore:
|
||||||
- dependency-name: "org.cryptomator:integrations-api"
|
- dependency-name: "org.cryptomator:integrations-api"
|
||||||
versions: ["2.0.0-alpha1"]
|
versions: ["2.0.0-alpha1"]
|
||||||
- dependency-name: "jakarta.inject:jakarta.inject-api"
|
- dependency-name: "jakarta.inject:jakarta.inject-api"
|
||||||
versions: ["2.0.1.MR"]
|
versions: ["2.0.1.MR"]
|
||||||
- dependency-name: "org.openjfx:*"
|
- dependency-name: "org.openjfx:*"
|
||||||
update-types: ["version-update:semver-major"]
|
- dependency-name: "com.fasterxml.jackson.*"
|
||||||
# due to https://github.com/fabriciorby/maven-surefire-junit5-tree-reporter/issues/68
|
versions: [ "[2.22,)" ] # 2.21.x is LTS, see https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.21
|
||||||
- dependency-name: "org.apache.maven.plugins:maven-surefire-plugin"
|
|
||||||
versions: [ "3.5.4", "3.5.5" ]
|
|
||||||
groups:
|
groups:
|
||||||
java-test-dependencies:
|
maven-dependencies:
|
||||||
patterns:
|
|
||||||
- "org.junit.jupiter:*"
|
|
||||||
- "org.mockito:*"
|
|
||||||
- "org.hamcrest:*"
|
|
||||||
- "com.google.jimfs:jimfs"
|
|
||||||
maven-build-plugins:
|
|
||||||
patterns:
|
|
||||||
- "org.apache.maven.plugins:*"
|
|
||||||
- "org.jacoco:jacoco-maven-plugin"
|
|
||||||
- "org.owasp:dependency-check-maven"
|
|
||||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
|
||||||
- "org.codehaus.mojo:license-maven-plugin"
|
|
||||||
javafx:
|
|
||||||
patterns:
|
|
||||||
- "org.openjfx:*"
|
|
||||||
java-production-dependencies:
|
|
||||||
patterns:
|
patterns:
|
||||||
- "*"
|
- "*"
|
||||||
exclude-patterns:
|
|
||||||
- "org.openjfx:*"
|
|
||||||
- "org.apache.maven.plugins:*"
|
|
||||||
- "org.jacoco:jacoco-maven-plugin"
|
|
||||||
- "org.owasp:dependency-check-maven"
|
|
||||||
- "me.fabriciorby:maven-surefire-junit5-tree-reporter"
|
|
||||||
- "org.codehaus.mojo:license-maven-plugin"
|
|
||||||
- "org.junit.jupiter:*"
|
|
||||||
- "org.mockito:*"
|
|
||||||
- "org.hamcrest:*"
|
|
||||||
- "com.google.jimfs:jimfs"
|
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/" # even for `.github/workflows`
|
directory: "/" # even for `.github/workflows`
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<!-- HEADER -->
|
||||||
|
> [!WARN]
|
||||||
|
> 🚧 DO NOT EDIT 🚧
|
||||||
|
>
|
||||||
|
> The [builds are still running](https://github.com/cryptomator/cryptomator/actions/workflows/create-release.yml).
|
||||||
|
> This banner will be replaced after the builds are finished.
|
||||||
|
<!-- /HEADER -->
|
||||||
|
|
||||||
|
<!--REPLACE with auto-generated release notes (see below)
|
||||||
|
### What's New 🎉
|
||||||
|
### Bugfixes 🐛
|
||||||
|
### Other Changes 📎
|
||||||
|
END REPLACE-->
|
||||||
|
|
||||||
|
For a comprehensive view of changes, read the [CHANGELOG](https://github.com/cryptomator/cryptomator/blob/$VERSION/CHANGELOG.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
💾 SHA-256 checksums of release artifacts:
|
||||||
|
```
|
||||||
|
$TARBALL
|
||||||
|
$EXE
|
||||||
|
$MSI
|
||||||
|
$DMG_x64
|
||||||
|
$DMG_arm64
|
||||||
|
$APPIMAGE_x86_64
|
||||||
|
$APPIMAGE_aarch64
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> You can verify the GPG signature of all assets using our public key: [`5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
||||||
|
|
||||||
|
<!-- Auto-Generated Release Notes: -->
|
||||||
|
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
# Cryptomator Release Workflow
|
||||||
|
|
||||||
|
This document describes the automated release pipeline defined in [`draft-release.yml`](draft-release.yml) and [`post-publish.yml`](post-publish.yml).
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The release process has two phases:
|
||||||
|
|
||||||
|
1. **Draft phase** (`draft-release.yml`) -- triggered by pushing a signed git tag. Compiles, tests, builds platform installers, and creates a **draft** GitHub Release.
|
||||||
|
2. **Post-publish phase** (`post-publish.yml`) -- triggered when the draft release is manually **published**. Submits Windows installers for AV whitelisting, notifies the team for DEB build and latest-version update, and triggers downstream updates (website, docs, winget).
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
---
|
||||||
|
config:
|
||||||
|
htmlLabels: false
|
||||||
|
---
|
||||||
|
flowchart TD
|
||||||
|
%% ── Trigger ──────────────────────────────────────────────
|
||||||
|
push_tag([🏷 Signed tag pushed])
|
||||||
|
|
||||||
|
%% ── Draft phase ──────────────────────────────────────────
|
||||||
|
push_tag --> get-version
|
||||||
|
|
||||||
|
subgraph draft["draft-release.yml"]
|
||||||
|
get-version["get-version
|
||||||
|
*parse semver from tag*"]
|
||||||
|
|
||||||
|
get-version --> create-release-draft
|
||||||
|
create-release-draft["create-release-draft
|
||||||
|
*compile & test (Linux)
|
||||||
|
create draft release
|
||||||
|
sign source tarball*"]
|
||||||
|
|
||||||
|
create-release-draft --> build-exe-and-msi
|
||||||
|
create-release-draft --> build-dmg-arm64
|
||||||
|
create-release-draft --> build-dmg-x64
|
||||||
|
create-release-draft --> build-appimages
|
||||||
|
|
||||||
|
build-exe-and-msi["build-exe-and-msi
|
||||||
|
*calls win-exe.yml
|
||||||
|
MSI + EXE (x64)
|
||||||
|
code-signed & GPG-signed*"]
|
||||||
|
build-dmg-arm64["build-dmg-arm64
|
||||||
|
*calls mac-dmg.yml
|
||||||
|
DMG (arm64)
|
||||||
|
notarized & GPG-signed*"]
|
||||||
|
build-dmg-x64["build-dmg-x64
|
||||||
|
*calls mac-dmg-x64.yml
|
||||||
|
DMG (x64)
|
||||||
|
notarized & GPG-signed*"]
|
||||||
|
build-appimages["build-appimages
|
||||||
|
*calls appimage.yml
|
||||||
|
AppImage (x86_64 + aarch64)
|
||||||
|
GPG-signed*"]
|
||||||
|
|
||||||
|
build-exe-and-msi --> update-sha256sums
|
||||||
|
build-dmg-arm64 --> update-sha256sums
|
||||||
|
build-dmg-x64 --> update-sha256sums
|
||||||
|
build-appimages --> update-sha256sums
|
||||||
|
|
||||||
|
update-sha256sums["update-sha256sums
|
||||||
|
*compute checksums
|
||||||
|
update release body*"]
|
||||||
|
end
|
||||||
|
|
||||||
|
update-sha256sums --> manual_review
|
||||||
|
|
||||||
|
%% ── Manual gate ──────────────────────────────────────────
|
||||||
|
manual_review{{Manual review
|
||||||
|
& publish}}
|
||||||
|
|
||||||
|
%% ── Post-publish phase ───────────────────────────────────
|
||||||
|
manual_review --> published([📢 Release published])
|
||||||
|
published --> post-publish
|
||||||
|
|
||||||
|
subgraph post-publish["post-publish.yml"]
|
||||||
|
direction TB
|
||||||
|
|
||||||
|
check-release["check-release
|
||||||
|
*classify release tag
|
||||||
|
stable, alpha, beta, rc, unknown*"]
|
||||||
|
notify["notify
|
||||||
|
*Slack notifications
|
||||||
|
deb build & version check*"]
|
||||||
|
get-asset-urls["get-asset-urls
|
||||||
|
*extract MSI & EXE
|
||||||
|
download URLs*"]
|
||||||
|
|
||||||
|
check-release --> notify-winget
|
||||||
|
check-release --> trigger-website
|
||||||
|
check-release --> trigger-docs
|
||||||
|
|
||||||
|
get-asset-urls --> allowlist-msi
|
||||||
|
allowlist-msi --> allowlist-exe
|
||||||
|
|
||||||
|
allowlist-msi["allowlist-msi-x64
|
||||||
|
*av-whitelist.yml
|
||||||
|
Kaspersky & Avast*"]
|
||||||
|
allowlist-exe["allowlist-exe-x64
|
||||||
|
*av-whitelist.yml
|
||||||
|
Kaspersky & Avast*"]
|
||||||
|
|
||||||
|
notify-winget["notify-winget
|
||||||
|
*Slack: ready for winget
|
||||||
|
stable only*"]
|
||||||
|
trigger-website["trigger-website-update
|
||||||
|
*dispatch to
|
||||||
|
cryptomator.github.io
|
||||||
|
stable only*"]
|
||||||
|
trigger-docs["trigger-docs-update
|
||||||
|
*dispatch to
|
||||||
|
cryptomator/docs
|
||||||
|
stable only, Windows*"]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Phase 1: Draft Release (`draft-release.yml`)
|
||||||
|
|
||||||
|
**Trigger:** push of any tag (`*`)
|
||||||
|
|
||||||
|
### Jobs
|
||||||
|
|
||||||
|
| Job | Runs on | Description |
|
||||||
|
|-----|---------|-------------|
|
||||||
|
| **get-version** | ubuntu | Parses the tag into semver components (`semVerNum`, `semVerSuffix`, `revNum`, `versionType`). The release is aborted if not an alpha, beta, rc or 'stable' release. |
|
||||||
|
| **create-release-draft** | ubuntu | Checks out the repo, verifies the tag is **signed** and lives on a `main` or `release/*` branch. Runs `./mvnw verify` (with `xvfb-run`). Creates a GitHub Release **draft** using the [release body template](../release-body.md.template). Downloads and GPG-signs the source tarball. |
|
||||||
|
| **build-exe-and-msi** | windows | Calls [`win-exe.yml`](win-exe.yml). Builds the MSI and EXE bundle installer for x64 Windows. Code-signed via Azure Trusted Signing, GPG-signed, and uploaded to the draft release. Outputs SHA-256 checksums. |
|
||||||
|
| **build-dmg-arm64** | macos-15 | Calls [`mac-dmg.yml`](mac-dmg.yml). Builds the DMG for Apple Silicon. Code-signed, notarized with Apple, GPG-signed, and uploaded. Outputs SHA-256 checksum. |
|
||||||
|
| **build-dmg-x64** | macos-15-large | Calls [`mac-dmg-x64.yml`](mac-dmg-x64.yml). Same as above but for Intel Macs. Uses macFUSE instead of FUSE-T. |
|
||||||
|
| **build-appimages** | ubuntu | Calls [`appimage.yml`](appimage.yml). Builds AppImages for x86_64 and aarch64 (matrix). GPG-signed and uploaded with `.zsync` delta-update files. Outputs SHA-256 checksums. |
|
||||||
|
| **update-sha256sums** | ubuntu | Runs after all builds complete. Computes the source tarball checksum, collects all artifact checksums, and updates the draft release body via `envsubst`. Replaces the "builds still running" banner with a success notice. |
|
||||||
|
|
||||||
|
### Release Artifacts
|
||||||
|
|
||||||
|
After the draft phase, the GitHub Release contains:
|
||||||
|
|
||||||
|
| Artifact | Platform |
|
||||||
|
|----------|----------|
|
||||||
|
| `cryptomator-<ver>.tar.gz.asc` | Source (GPG signature) |
|
||||||
|
| `Cryptomator-<ver>-x64.msi` + `.asc` | Windows |
|
||||||
|
| `Cryptomator-<ver>-x64.exe` + `.asc` | Windows |
|
||||||
|
| `Cryptomator-<ver>-arm64.dmg` + `.asc` | macOS (Apple Silicon) |
|
||||||
|
| `Cryptomator-<ver>-x64.dmg` + `.asc` | macOS (Intel) |
|
||||||
|
| `cryptomator-<ver>-x86_64.AppImage` + `.zsync` + `.asc` | Linux (x86_64) |
|
||||||
|
| `cryptomator-<ver>-aarch64.AppImage` + `.zsync` + `.asc` | Linux (aarch64) |
|
||||||
|
|
||||||
|
All artifacts are signed with GPG key [`615D449FE6E6A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
||||||
|
|
||||||
|
## Manual Review Gate
|
||||||
|
|
||||||
|
After the draft phase completes, a maintainer reviews the draft release on GitHub. This is the point to:
|
||||||
|
|
||||||
|
- Verify all artifacts are present and checksums look correct.
|
||||||
|
- Edit the auto-generated release notes (What's New, Bugfixes, Other Changes).
|
||||||
|
- **Publish** the release when ready, which triggers phase 2.
|
||||||
|
|
||||||
|
## Phase 2: Post-Publish (`post-publish.yml`)
|
||||||
|
|
||||||
|
**Trigger:** `release: [published]`
|
||||||
|
|
||||||
|
### Jobs
|
||||||
|
|
||||||
|
| Job | Condition | Description |
|
||||||
|
|-----|-----------|-------------|
|
||||||
|
| **notify** | always | Sends Slack notifications to `#cryptomator-desktop`: ready to build `.deb` package, and reminder to update `latest-version.json` on S3. |
|
||||||
|
| **get-asset-urls** | always | Extracts MSI and EXE download URLs from the release assets. |
|
||||||
|
| **check-release** | always | Classifies the published release tag as `stable`, `alpha`, `beta`, `rc`, or `unknown`. Stable-only follow-up jobs depend on this output. Unlike `get-version.yml` workflow, this job does not perform semver validation. |
|
||||||
|
| **allowlist-msi-x64** | Windows release | Calls [`av-whitelist.yml`](av-whitelist.yml). Uploads the MSI to Kaspersky and Avast for whitelisting. |
|
||||||
|
| **allowlist-exe-x64** | Windows release | Same as above for the EXE. Runs sequentially after MSI. |
|
||||||
|
| **notify-winget** | stable + Windows | Sends a Slack notification that the release is ready for [winget submission](winget.yml). |
|
||||||
|
| **trigger-website-update** | stable | Dispatches `desktop-release` event to `cryptomator/cryptomator.github.io`. |
|
||||||
|
| **trigger-docs-update** | stable + Windows | Dispatches `desktop-release` event to `cryptomator/docs`. |
|
||||||
|
|
||||||
|
### Manual Follow-ups
|
||||||
|
|
||||||
|
These steps are triggered by team members after Slack notifications:
|
||||||
|
|
||||||
|
- **Debian package** -- Run the [`debian.yml`](debian.yml) workflow to build `.deb` and optionally upload to the PPA.
|
||||||
|
- **winget** -- Run the [`winget.yml`](winget.yml) workflow to submit to the Windows Package Manager.
|
||||||
|
- **latest-version.json** -- Update the version-check file on S3 (`static.cryptomator.org/desktop/latest-version.json`).
|
||||||
|
|
||||||
|
## Signing & Security
|
||||||
|
|
||||||
|
- **Git tag** must be SSH-signed and reside on `main` or `release/*`.
|
||||||
|
- **Windows** installers are code-signed using Azure Trusted Signing.
|
||||||
|
- **macOS** DMGs are code-signed with an Apple Developer certificate and notarized via `notarytool`.
|
||||||
|
- **All artifacts** receive a detached GPG signature (`.asc`) using key `615D449FE6E6A235`.
|
||||||
|
- **AV whitelisting** is submitted to Kaspersky and Avast after publish (Windows installers only).
|
||||||
|
- The draft release is created using `CRYPTOBOT_RELEASE_TOKEN`, not `GITHUB_TOKEN`, to ensure proper permissions and trigger downstream workflows.
|
||||||
+73
-108
@@ -1,19 +1,44 @@
|
|||||||
name: Build AppImage
|
name: Build AppImage
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 23 20 * *'
|
- cron: '0 23 20 * *'
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
semVerNum:
|
||||||
|
type: string
|
||||||
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
|
required: true
|
||||||
|
revisionNum:
|
||||||
|
type: string
|
||||||
|
description: 'The revision number'
|
||||||
|
required: true
|
||||||
|
semVerSuffix:
|
||||||
|
type: string
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: true
|
||||||
|
upload-to-draft:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
outputs:
|
||||||
|
sha256-appimage-x64:
|
||||||
|
description: "SHA256 sum of the x64 appimage"
|
||||||
|
value: ${{ jobs.collect-sha256sums.outputs.x64-sha256sum}}
|
||||||
|
sha256-appimage-aarch64:
|
||||||
|
description: "SHA256 sum of the aarch64 appimage"
|
||||||
|
value: ${{ jobs.collect-sha256sums.outputs.aarch64-sha256sum}}
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
semVerNum:
|
||||||
description: 'Version'
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
required: false
|
required: false
|
||||||
create-pr:
|
revisionNum:
|
||||||
description: 'Create a PR for aur-bin repo'
|
description: 'The revision number'
|
||||||
type: boolean
|
required: false
|
||||||
default: false
|
semVerSuffix:
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: false
|
||||||
|
default: '-SNAPSHOT'
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
@@ -25,40 +50,34 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
JAVA_VERSION: '26.0.1+8'
|
||||||
|
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
|
||||||
|
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
|
||||||
|
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
|
||||||
uses: ./.github/workflows/get-version.yml
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version }} #okay if not defined
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build AppImage
|
name: Build AppImage
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [get-version]
|
|
||||||
env:
|
|
||||||
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
|
|
||||||
SEMVER_NUM: ${{ needs.get-version.outputs.semVerNum }}
|
|
||||||
REV_NUM: ${{ needs.get-version.outputs.revNum }}
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
arch: x86_64
|
arch: x86_64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-x64_bin-jmods.zip'
|
||||||
openjfx-sha: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
|
openjfx-sha: '47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50'
|
||||||
appimagetool-sha: 'ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0'
|
appimagetool-sha: 'ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0'
|
||||||
- os: ubuntu-24.04-arm
|
- os: ubuntu-24.04-arm
|
||||||
arch: aarch64
|
arch: aarch64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-aarch64_bin-jmods.zip'
|
||||||
openjfx-sha: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
|
openjfx-sha: 'e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f'
|
||||||
appimagetool-sha: 'f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158'
|
appimagetool-sha: 'f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -76,7 +95,7 @@ jobs:
|
|||||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
JMOD_VERSION=${JMOD_VERSION#*@}
|
||||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
JMOD_VERSION=${JMOD_VERSION%%.*}
|
||||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||||
|
|
||||||
@@ -85,9 +104,9 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Set version
|
- name: Set version
|
||||||
run : mvn versions:set -DnewVersion="$SEMVER_STR"
|
run : ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Plinux -DskipTests
|
run: ./mvnw -B clean package -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
@@ -127,13 +146,13 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${SEMVER_NUM}.${REV_NUM}"
|
--app-version "${VERSION_NUM}.${REVISION_NUM}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
|
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
--java-options "-Xmx256m"
|
--java-options "-Xmx256m"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\""
|
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
|
||||||
--java-options "-Dfile.encoding=\"utf-8\""
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\""
|
--java-options "-Dcryptomator.adminConfigPath=\"/etc/cryptomator/config.properties\""
|
||||||
@@ -144,7 +163,7 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/.local/share/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${REV_NUM}\""
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NUM}\""
|
||||||
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
|
--java-options "-Dcryptomator.networking.truststore.p12Path=\"/etc/cryptomator/certs.p12\""
|
||||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
||||||
@@ -183,7 +202,7 @@ jobs:
|
|||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
run: >
|
run: >
|
||||||
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${SEMVER_STR}-${{ matrix.arch }}.AppImage
|
./squashfs-root/AppRun Cryptomator.AppDir cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.AppImage
|
||||||
-u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.arch }}.AppImage.zsync"
|
-u "gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${{ matrix.arch }}.AppImage.zsync"
|
||||||
--sign --sign-key=615D449FE6E6A235
|
--sign --sign-key=615D449FE6E6A235
|
||||||
- name: Create detached GPG signatures
|
- name: Create detached GPG signatures
|
||||||
@@ -191,7 +210,7 @@ jobs:
|
|||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage
|
||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.AppImage.zsync
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: appimage-${{ matrix.arch }}
|
name: appimage-${{ matrix.arch }}
|
||||||
path: |
|
path: |
|
||||||
@@ -200,9 +219,10 @@ jobs:
|
|||||||
cryptomator-*.asc
|
cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish AppImage on GitHub Releases
|
- name: Publish AppImage on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: inputs.upload-to-draft
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
||||||
with:
|
with:
|
||||||
|
draft: true
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
files: |
|
files: |
|
||||||
@@ -210,79 +230,24 @@ jobs:
|
|||||||
cryptomator-*.zsync
|
cryptomator-*.zsync
|
||||||
cryptomator-*.asc
|
cryptomator-*.asc
|
||||||
|
|
||||||
create-aur-bin-pr:
|
collect-sha256sums:
|
||||||
name: Create PR for aur-bin repo
|
name: Collect AppImage checksums
|
||||||
if: github.event_name == 'workflow_dispatch' && inputs.create-pr || github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable'
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build, get-version]
|
needs: [build]
|
||||||
container:
|
if: inputs.upload-to-draft
|
||||||
image: archlinux:base-devel
|
outputs:
|
||||||
env:
|
x64-sha256sum: ${{ steps.sha256sum.outputs.x64-sha256sum }}
|
||||||
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
|
aarch64-sha256sum: ${{ steps.sha256sum.outputs.aarch64-sha256sum }}
|
||||||
PKGDEST: ${{ github.workspace }}/pkgdest
|
|
||||||
SRCDEST: ${{ github.workspace }}/srcdest
|
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare pacman
|
- name: Download AppImage artifacts
|
||||||
run: |
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
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:
|
with:
|
||||||
repository: 'cryptomator/aur-bin'
|
pattern: appimage-*
|
||||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
path: appimage-artifacts
|
||||||
- name: Create build user
|
- name: Compute SHA256 sums
|
||||||
|
id: sha256sum
|
||||||
run: |
|
run: |
|
||||||
useradd -m builder
|
read -ra X64_SUM < <(sha256sum appimage-artifacts/appimage-x86_64/cryptomator-*-x86_64.AppImage)
|
||||||
echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/builder
|
read -ra AARCH64_SUM < <(sha256sum appimage-artifacts/appimage-aarch64/cryptomator-*-aarch64.AppImage)
|
||||||
chown -R builder:builder "$GITHUB_WORKSPACE"
|
echo "x64-sha256sum=${X64_SUM[0]}" >> "$GITHUB_OUTPUT"
|
||||||
install -d -m 0755 -o builder -g builder "$PKGDEST" "$SRCDEST"
|
echo "aarch64-sha256sum=${AARCH64_SUM[0]}" >> "$GITHUB_OUTPUT"
|
||||||
- name: Import Cryptomator release signing key
|
|
||||||
# try first ubuntu. on failure try openpgp keyservers
|
|
||||||
run: >
|
|
||||||
sudo -u builder gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
|
|
||||||
|| sudo -u builder gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
|
|
||||||
- name: Checkout release branch
|
|
||||||
run: |
|
|
||||||
git config --global safe.directory '*'
|
|
||||||
git checkout -b "release/${SEMVER_STR}"
|
|
||||||
- name: Update build file
|
|
||||||
run: |
|
|
||||||
sed -i -e "s|^pkgver=.*$|pkgver=${SEMVER_STR}|" PKGBUILD
|
|
||||||
sed -i -e 's|^pkgrel=.*$|pkgrel=1|' PKGBUILD
|
|
||||||
sudo -u builder updpkgsums
|
|
||||||
sudo -u builder makepkg --printsrcinfo > .SRCINFO
|
|
||||||
- name: Build package with makepkg
|
|
||||||
run: >
|
|
||||||
sudo -u builder
|
|
||||||
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
|
|
||||||
makepkg --syncdeps --cleanbuild --noconfirm --log
|
|
||||||
- name: Commit and push
|
|
||||||
run: |
|
|
||||||
git config user.name "cryptobot"
|
|
||||||
git config user.email "cryptobot@users.noreply.github.com"
|
|
||||||
git config push.autoSetupRemote true
|
|
||||||
git stage PKGBUILD .SRCINFO
|
|
||||||
git commit -m "Prepare release ${SEMVER_STR}"
|
|
||||||
git push
|
|
||||||
- name: Create pull request
|
|
||||||
id: create-pr
|
|
||||||
run: |
|
|
||||||
printf "Created by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" > pr_body.md
|
|
||||||
PR_URL=$(gh pr create --title "Release ${SEMVER_STR}" --body-file pr_body.md)
|
|
||||||
echo "url=$PR_URL" >> "$GITHUB_OUTPUT"
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
|
||||||
SLACK_ICON: false
|
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
|
||||||
SLACK_TITLE: "AUR-bin release PR for ${{ github.event.repository.name }} ${{ needs.get-version.outputs.semVerStr }} created."
|
|
||||||
SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.url }}|PR> on how to proceed."
|
|
||||||
SLACK_FOOTER: false
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
name: PR for aur-bin repo
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
src-tag:
|
||||||
|
description: 'Source or Release tag'
|
||||||
|
required: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
get-version:
|
||||||
|
uses: ./.github/workflows/get-version.yml
|
||||||
|
with:
|
||||||
|
version: ${{ inputs.src-tag }}
|
||||||
|
|
||||||
|
create-aur-bin-pr:
|
||||||
|
name: Create PR for aur-bin repo
|
||||||
|
if: (github.event_name == 'workflow_dispatch') || (github.event_name == 'release' && needs.get-version.outputs.versionType == 'stable')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [get-version]
|
||||||
|
container:
|
||||||
|
image: archlinux:base-devel
|
||||||
|
env:
|
||||||
|
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
|
||||||
|
PKGDEST: ${{ github.workspace }}/pkgdest
|
||||||
|
SRCDEST: ${{ github.workspace }}/srcdest
|
||||||
|
steps:
|
||||||
|
- name: Prepare pacman
|
||||||
|
run: |
|
||||||
|
pacman-key --init
|
||||||
|
pacman-key --populate archlinux
|
||||||
|
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip github-cli curl pacman-contrib
|
||||||
|
- name: Checkout cryptomator/aur-bin
|
||||||
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
with:
|
||||||
|
repository: 'cryptomator/aur-bin'
|
||||||
|
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
|
- name: Create build user
|
||||||
|
run: |
|
||||||
|
useradd -m builder
|
||||||
|
echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers.d/builder
|
||||||
|
chown -R builder:builder "$GITHUB_WORKSPACE"
|
||||||
|
install -d -m 0755 -o builder -g builder "$PKGDEST" "$SRCDEST"
|
||||||
|
- name: Import Cryptomator release signing key
|
||||||
|
# try first ubuntu. on failure try openpgp keyservers
|
||||||
|
run: >
|
||||||
|
sudo -u builder gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
|
||||||
|
|| sudo -u builder gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys 58117AFA1F85B3EEC154677D615D449FE6E6A235
|
||||||
|
- name: Checkout release branch
|
||||||
|
run: |
|
||||||
|
git config --global safe.directory '*'
|
||||||
|
git checkout -b "release/${SEMVER_STR}"
|
||||||
|
- name: Determine pkgrel
|
||||||
|
id: pkgrel
|
||||||
|
run: |
|
||||||
|
CURRENT_VERSION="$(sed -nE 's/^pkgver=(.*)$/\1/p' PKGBUILD | head -n1)"
|
||||||
|
CURRENT_REL="$(sed -nE 's/^pkgrel=([0-9]+).*$/\1/p' PKGBUILD | head -n1)"
|
||||||
|
|
||||||
|
if [[ "$CURRENT_VERSION" == "$TARGET_VERSION" && "$CURRENT_REL" =~ ^[0-9]+$ ]]; then
|
||||||
|
NEXT_REL=$((CURRENT_REL + 1))
|
||||||
|
else
|
||||||
|
NEXT_REL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "value=${NEXT_REL}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "dist-version=${TARGET_VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT"
|
||||||
|
env:
|
||||||
|
TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }}
|
||||||
|
- name: Update build file
|
||||||
|
run: |
|
||||||
|
sed -i -e "s|^pkgver=.*$|pkgver=${PKG_VERSION}|" PKGBUILD
|
||||||
|
sed -i -e "s|^pkgrel=.*$|pkgrel=${PKG_RELEASE}|" PKGBUILD
|
||||||
|
sudo -u builder updpkgsums
|
||||||
|
sudo -u builder makepkg --printsrcinfo > .SRCINFO
|
||||||
|
env:
|
||||||
|
PKG_VERSION: ${{ needs.get-version.outputs.semVerNum }}
|
||||||
|
PKG_RELEASE: ${{ steps.pkgrel.outputs.value }}
|
||||||
|
- name: Build package with makepkg
|
||||||
|
run: >
|
||||||
|
sudo -u builder
|
||||||
|
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
|
||||||
|
makepkg --syncdeps --cleanbuild --noconfirm --log
|
||||||
|
- name: Commit and push
|
||||||
|
run: |
|
||||||
|
git config user.name "cryptobot"
|
||||||
|
git config user.email "cryptobot@users.noreply.github.com"
|
||||||
|
git config push.autoSetupRemote true
|
||||||
|
git stage PKGBUILD .SRCINFO
|
||||||
|
git commit -m "Prepare release ${DIST_VERSION}"
|
||||||
|
git push
|
||||||
|
env:
|
||||||
|
DIST_VERSION: ${{ steps.pkgrel.outputs.dist-version }}
|
||||||
|
- name: Create pull request
|
||||||
|
id: create-pr
|
||||||
|
run: |
|
||||||
|
printf "Created by $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" > pr_body.md
|
||||||
|
PR_URL=$(gh pr create --title "Release ${DIST_VERSION}" --body-file pr_body.md)
|
||||||
|
echo "url=$PR_URL" >> "$GITHUB_OUTPUT"
|
||||||
|
env:
|
||||||
|
DIST_VERSION: ${{ steps.pkgrel.outputs.dist-version }}
|
||||||
|
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
|
- name: Slack Notification
|
||||||
|
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||||
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
|
SLACK_ICON: ''
|
||||||
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
|
SLACK_TITLE: "AUR-bin release PR for ${{ github.event.repository.name }} ${{ needs.get-version.outputs.semVerStr }} created."
|
||||||
|
SLACK_MESSAGE: "See <${{ steps.create-pr.outputs.url }}|PR> on how to proceed."
|
||||||
|
SLACK_FOOTER: ''
|
||||||
|
MSG_MINIMAL: true
|
||||||
@@ -37,7 +37,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
download-file:
|
download-file:
|
||||||
name: Downloads the file into the VM
|
name: Downloads the file into the VM
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
outputs:
|
outputs:
|
||||||
fileName: ${{ steps.extractName.outputs.fileName}}
|
fileName: ${{ steps.extractName.outputs.fileName}}
|
||||||
env:
|
env:
|
||||||
@@ -51,24 +51,24 @@ jobs:
|
|||||||
- name: Download file
|
- name: Download file
|
||||||
run: curl --silent --fail-with-body --proto "=https" -L "${INPUT_URL}" -o "${{steps.extractName.outputs.fileName}}"
|
run: curl --silent --fail-with-body --proto "=https" -L "${INPUT_URL}" -o "${{steps.extractName.outputs.fileName}}"
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.extractName.outputs.fileName }}
|
name: ${{ steps.extractName.outputs.fileName }}
|
||||||
path: ${{ steps.extractName.outputs.fileName }}
|
path: ${{ steps.extractName.outputs.fileName }}
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
allowlist-kaspersky:
|
allowlist-kaspersky:
|
||||||
name: Anti Virus Allowlisting Kaspersky
|
name: Anti Virus Allowlisting Kaspersky
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
needs: download-file
|
needs: download-file
|
||||||
if: inputs.kaspersky
|
if: inputs.kaspersky
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: ${{ needs.download-file.outputs.fileName }}
|
name: ${{ needs.download-file.outputs.fileName }}
|
||||||
path: upload
|
path: upload
|
||||||
- name: Upload to Kaspersky
|
- name: Upload to Kaspersky
|
||||||
uses: SamKirkland/FTP-Deploy-Action@a51268f67f6605236975928ae28b0f7e9971d50a # v4.6.3
|
uses: SamKirkland/FTP-Deploy-Action@110f9186c050f71550953127052e77650219c287 # v4.6.3
|
||||||
with:
|
with:
|
||||||
protocol: ftps
|
protocol: ftps
|
||||||
server: allowlist.kaspersky-labs.com
|
server: allowlist.kaspersky-labs.com
|
||||||
@@ -83,7 +83,7 @@ jobs:
|
|||||||
if: inputs.avast
|
if: inputs.avast
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifact
|
- name: Download artifact
|
||||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: ${{ needs.download-file.outputs.fileName }}
|
name: ${{ needs.download-file.outputs.fileName }}
|
||||||
path: upload
|
path: upload
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: 25
|
JAVA_VERSION: 26
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -22,14 +22,14 @@ jobs:
|
|||||||
name: Compile and Test
|
name: Compile and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Cache SonarCloud packages
|
- name: Cache SonarCloud packages
|
||||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: ~/.sonar/cache
|
path: ~/.sonar/cache
|
||||||
key: ${{ runner.os }}-sonar
|
key: ${{ runner.os }}-sonar
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
- name: Build and Test
|
- name: Build and Test
|
||||||
run: >
|
run: >
|
||||||
xvfb-run
|
xvfb-run
|
||||||
mvn -B verify
|
./mvnw -B verify
|
||||||
jacoco:report
|
jacoco:report
|
||||||
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||||
-Pcoverage
|
-Pcoverage
|
||||||
@@ -47,40 +47,3 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
- name: Draft a release
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
|
||||||
with:
|
|
||||||
draft: true
|
|
||||||
discussion_category_name: releases
|
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
|
||||||
generate_release_notes: true
|
|
||||||
body: |-
|
|
||||||
> [!NOTE]
|
|
||||||
> 🚧 Work in Progress 🚧
|
|
||||||
>
|
|
||||||
> Please be patient, the [builds are still running](https://github.com/cryptomator/cryptomator/actions). Binary packages can be found here in a few moments.
|
|
||||||
|
|
||||||
<!--REPLACE with auto-generated release notes (see below)
|
|
||||||
### What's New 🎉
|
|
||||||
### Bugfixes 🐛
|
|
||||||
### Other Changes 📎
|
|
||||||
END REPLACE-->
|
|
||||||
|
|
||||||
For a comprehensive view of changes, read the [CHANGELOG](https://github.com/cryptomator/cryptomator/blob/develop/CHANGELOG.md).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Don't forget to include the
|
|
||||||
💾 SHA-256 checksums of release artifacts:
|
|
||||||
```
|
|
||||||
```
|
|
||||||
-->
|
|
||||||
|
|
||||||
> [!TIP]
|
|
||||||
> You can verify the GPG signature of all assets using our public key: [`5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Auto-Generated Release Notes: -->
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,2) >> "$env:GITHUB_ENV"
|
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,2) >> "$env:GITHUB_ENV"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }}
|
- name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }}
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
java-version: ${{ env.JDK_MAJOR_VERSION}}
|
java-version: ${{ env.JDK_MAJOR_VERSION}}
|
||||||
distribution: ${{ env.JDK_VENDOR }}
|
distribution: ${{ env.JDK_VENDOR }}
|
||||||
@@ -70,14 +70,14 @@ jobs:
|
|||||||
}
|
}
|
||||||
- name: Notify
|
- name: Notify
|
||||||
if: steps.determine.outputs.UPDATE_AVAILABLE == 'true'
|
if: steps.determine.outputs.UPDATE_AVAILABLE == 'true'
|
||||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
|
||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
SLACK_ICON: false
|
SLACK_ICON: ''
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
SLACK_TITLE: "JDK update available"
|
SLACK_TITLE: "JDK update available"
|
||||||
SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ steps.determine.outputs.LATEST_JDK_VERSION }}. Check the Nextcloud collective for instructions."
|
SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ steps.determine.outputs.LATEST_JDK_VERSION }}. Check the Nextcloud collective for instructions."
|
||||||
SLACK_FOOTER: false
|
SLACK_FOOTER: ''
|
||||||
MSG_MINIMAL: true
|
MSG_MINIMAL: true
|
||||||
@@ -25,12 +25,12 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
JAVA_VERSION: '26.0.1+8'
|
||||||
DEB_BUILD_DEPENDS: 'debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1'
|
DEB_BUILD_DEPENDS: 'debhelper (>=10), coffeelibs-jdk-26 (>= 26.0.1+8-0ppa1), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1'
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-x64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AMD64_HASH: 'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
|
OPENJFX_JMODS_AMD64_HASH: '47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50'
|
||||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip'
|
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-aarch64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AARCH64_HASH: 'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
|
OPENJFX_JMODS_AARCH64_HASH: 'e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
INPUT_PPAVER: ${{ inputs.ppaver }}
|
INPUT_PPAVER: ${{ inputs.ppaver }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- id: deb-version
|
- id: deb-version
|
||||||
name: Determine deb-version
|
name: Determine deb-version
|
||||||
run: |
|
run: |
|
||||||
@@ -56,20 +56,21 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
- name: Install build tools
|
- name: Install build tools
|
||||||
run: |
|
run: |
|
||||||
|
sudo add-apt-repository -y ppa:coffeelibs/openjdk
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install devscripts dput
|
sudo apt-get install devscripts dput
|
||||||
sudo apt-get satisfy "${DEB_BUILD_DEPENDS}"
|
sudo apt-get satisfy "${DEB_BUILD_DEPENDS}"
|
||||||
env:
|
env:
|
||||||
DEB_BUILD_DEPENDS: ${{ env.DEB_BUILD_DEPENDS }}
|
DEB_BUILD_DEPENDS: ${{ env.DEB_BUILD_DEPENDS }}
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Plinux -DskipTests
|
run: ./mvnw -B clean package -DskipTests
|
||||||
- name: Download OpenJFX jmods
|
- name: Download OpenJFX jmods
|
||||||
id: download-jmods
|
id: download-jmods
|
||||||
run: |
|
run: |
|
||||||
@@ -89,7 +90,7 @@ jobs:
|
|||||||
JMOD_VERSION_AARCH64=$(jmod describe jmods/aarch64/javafx.base.jmod | head -1)
|
JMOD_VERSION_AARCH64=$(jmod describe jmods/aarch64/javafx.base.jmod | head -1)
|
||||||
JMOD_VERSION_AARCH64=${JMOD_VERSION_AARCH64#*@}
|
JMOD_VERSION_AARCH64=${JMOD_VERSION_AARCH64#*@}
|
||||||
JMOD_VERSION_AARCH64=${JMOD_VERSION_AARCH64%%.*}
|
JMOD_VERSION_AARCH64=${JMOD_VERSION_AARCH64%%.*}
|
||||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||||
|
|
||||||
@@ -145,7 +146,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
|
gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator_*_amd64.deb
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: linux-deb-package
|
name: linux-deb-package
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-dependencies:
|
check-dependencies:
|
||||||
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@957d3c2c08c56855fdac41e5afb9a7aca8c30dd9 # v3.0.3
|
uses: skymatic/workflows/.github/workflows/run-dependency-check.yml@8356563bf7b8d1c8d693f75ca487e8f57573cec9 # v3.1.0
|
||||||
with:
|
with:
|
||||||
runner-os: 'ubuntu-latest'
|
runner-os: 'ubuntu-latest'
|
||||||
java-distribution: 'temurin'
|
java-distribution: 'temurin'
|
||||||
java-version: 25
|
java-version: 26
|
||||||
secrets:
|
secrets:
|
||||||
nvd-api-key: ${{ secrets.NVD_API_KEY }}
|
nvd-api-key: ${{ secrets.NVD_API_KEY }}
|
||||||
ossindex-username: ${{ secrets.OSSINDEX_USERNAME }}
|
ossindex-username: ${{ secrets.OSSINDEX_USERNAME }}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Get download count of latest releases
|
- name: Get download count of latest releases
|
||||||
id: get-stats
|
id: get-stats
|
||||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const query = `query($owner:String!, $name:String!) {
|
const query = `query($owner:String!, $name:String!) {
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
name: Draft a Cryptomator Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
JAVA_DIST: 'temurin'
|
||||||
|
JAVA_VERSION: '26.0.1+8'
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
get-version:
|
||||||
|
uses: ./.github/workflows/get-version.yml
|
||||||
|
with:
|
||||||
|
version: ''
|
||||||
|
|
||||||
|
create-release-draft:
|
||||||
|
name: Compile and Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: get-version
|
||||||
|
if: needs.get-version.outputs.versionType != 'unknown'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Check the git tag is signed
|
||||||
|
run: git cat-file -p "${GITHUB_REF_NAME}" | grep "BEGIN SSH SIGNATURE"
|
||||||
|
- name: Check the git tag is on release or main branch
|
||||||
|
run: git branch -r --contains "${GITHUB_REF_NAME}" | grep -E '^\s*origin/(main|release/.*)\s*$'
|
||||||
|
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
|
with:
|
||||||
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
|
cache: 'maven'
|
||||||
|
- name: Build and Test
|
||||||
|
run: xvfb-run ./mvnw -B verify
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
|
- name: Draft a release
|
||||||
|
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
discussion_category_name: releases
|
||||||
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
generate_release_notes: true
|
||||||
|
body_path: .github/release-body.md.template
|
||||||
|
- name: Download source tarball
|
||||||
|
run: |
|
||||||
|
curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/${{ github.ref }}.tar.gz --output cryptomator-${{ github.ref_name }}.tar.gz
|
||||||
|
- name: Sign source tarball with key 615D449FE6E6A235
|
||||||
|
run: |
|
||||||
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
|
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
|
||||||
|
env:
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
|
- name: Publish asc on GitHub Releases
|
||||||
|
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
||||||
|
with:
|
||||||
|
draft: true
|
||||||
|
fail_on_unmatched_files: true
|
||||||
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
files: |
|
||||||
|
cryptomator-*.tar.gz.asc
|
||||||
|
|
||||||
|
build-exe-and-msi:
|
||||||
|
needs: [get-version, create-release-draft]
|
||||||
|
uses: ./.github/workflows/win-exe.yml
|
||||||
|
with:
|
||||||
|
semVerNum: ${{needs.get-version.outputs.semVerNum}}
|
||||||
|
revisionNum: ${{needs.get-version.outputs.revNum}}
|
||||||
|
semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
build-dmg-arm64:
|
||||||
|
needs: [get-version, create-release-draft]
|
||||||
|
uses: ./.github/workflows/mac-dmg.yml
|
||||||
|
with:
|
||||||
|
semVerNum: ${{needs.get-version.outputs.semVerNum}}
|
||||||
|
revisionNum: ${{needs.get-version.outputs.revNum}}
|
||||||
|
semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
build-dmg-x64:
|
||||||
|
needs: [get-version, create-release-draft]
|
||||||
|
uses: ./.github/workflows/mac-dmg-x64.yml
|
||||||
|
with:
|
||||||
|
semVerNum: ${{needs.get-version.outputs.semVerNum}}
|
||||||
|
revisionNum: ${{needs.get-version.outputs.revNum}}
|
||||||
|
semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
build-appimages:
|
||||||
|
needs: [get-version, create-release-draft]
|
||||||
|
uses: ./.github/workflows/appimage.yml
|
||||||
|
with:
|
||||||
|
semVerNum: ${{needs.get-version.outputs.semVerNum}}
|
||||||
|
revisionNum: ${{needs.get-version.outputs.revNum}}
|
||||||
|
semVerSuffix: ${{needs.get-version.outputs.semVerSuffix}}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
update-sha256sums:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [get-version, build-exe-and-msi, build-dmg-arm64, build-dmg-x64, build-appimages]
|
||||||
|
env:
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
SEMVER: ${{ needs.get-version.outputs.semVerStr }}
|
||||||
|
GH_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
- name: Compute source tarball SHA256
|
||||||
|
id: src-sha256
|
||||||
|
run: |
|
||||||
|
curl --silent --fail-with-body --proto "=https" -L \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
"https://github.com/cryptomator/cryptomator/archive/refs/tags/${TAG}.tar.gz" \
|
||||||
|
--output "cryptomator-${SEMVER}.tar.gz"
|
||||||
|
read -ra CMD_OUTPUT < <(sha256sum "cryptomator-${SEMVER}.tar.gz")
|
||||||
|
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||||
|
- name: Update release body with checksums
|
||||||
|
run: |
|
||||||
|
CURRENT_BODY=$(gh release view "${TAG}" --json body --jq .body)
|
||||||
|
RELEASE_BODY=$(printf '%s\n' "${CURRENT_BODY}" | sed '/<!-- 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 }}
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
- name: Query Discussion Data
|
- name: Query Discussion Data
|
||||||
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
|
if: github.event_name == 'discussion_comment' || github.event_name == 'discussion' && github.event.action != 'deleted'
|
||||||
id: query-data
|
id: query-data
|
||||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
|
const query = `query ($owner: String!, $name: String!, $discussionNumber: Int!) {
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ on:
|
|||||||
semVerNum:
|
semVerNum:
|
||||||
description: "The numerical part of the version string"
|
description: "The numerical part of the version string"
|
||||||
value: ${{ jobs.determine-version.outputs.semVerNum}}
|
value: ${{ jobs.determine-version.outputs.semVerNum}}
|
||||||
|
semVerSuffix:
|
||||||
|
description: "The suffix of the version string"
|
||||||
|
value: ${{ jobs.determine-version.outputs.semVerSuffix}}
|
||||||
revNum:
|
revNum:
|
||||||
description: "The revision number"
|
description: "The revision number"
|
||||||
value: ${{ jobs.determine-version.outputs.revNum}}
|
value: ${{ jobs.determine-version.outputs.revNum}}
|
||||||
@@ -23,7 +26,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: 25
|
JAVA_VERSION: 26
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
determine-version:
|
determine-version:
|
||||||
@@ -32,14 +35,15 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
semVerNum: ${{ steps.versions.outputs.semVerNum }}
|
semVerNum: ${{ steps.versions.outputs.semVerNum }}
|
||||||
semVerStr: ${{ steps.versions.outputs.semVerStr }}
|
semVerStr: ${{ steps.versions.outputs.semVerStr }}
|
||||||
|
semVerSuffix: ${{ steps.versions.outputs.semVerSuffix }}
|
||||||
revNum: ${{ steps.versions.outputs.revNum }}
|
revNum: ${{ steps.versions.outputs.revNum }}
|
||||||
type: ${{ steps.versions.outputs.type}}
|
type: ${{ steps.versions.outputs.type}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -52,27 +56,32 @@ jobs:
|
|||||||
elif [[ "${VERSION_STRING}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
elif [[ "${VERSION_STRING}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||||
SEM_VER_STR="${VERSION_STRING}"
|
SEM_VER_STR="${VERSION_STRING}"
|
||||||
else
|
else
|
||||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
SEM_VER_STR=`./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||||
fi
|
fi
|
||||||
SEM_VER_NUM=`echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/'`
|
|
||||||
|
SEM_VER_NUM=$(echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
|
||||||
|
SEM_VER_SUFFIX="${SEM_VER_STR#"$SEM_VER_NUM"}"
|
||||||
REVCOUNT=`git rev-list --count HEAD`
|
REVCOUNT=`git rev-list --count HEAD`
|
||||||
|
|
||||||
TYPE="unknown"
|
TYPE="unknown"
|
||||||
if [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
if [[ -z $SEM_VER_SUFFIX ]]; then
|
||||||
TYPE="stable"
|
TYPE="stable"
|
||||||
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-alpha[1-9]+$ ]]; then
|
elif [[ $SEM_VER_SUFFIX =~ -alpha[1-9]+$ ]]; then
|
||||||
TYPE="alpha"
|
TYPE="alpha"
|
||||||
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-beta[1-9]+$ ]]; then
|
elif [[ $SEM_VER_SUFFIX =~ -beta[1-9]+$ ]]; then
|
||||||
TYPE="beta"
|
TYPE="beta"
|
||||||
elif [[ $SEM_VER_STR =~ [0-9]+\.[0-9]+\.[0-9]+-rc[1-9]$ ]]; then
|
elif [[ $SEM_VER_SUFFIX =~ -rc[1-9]+$ ]]; then
|
||||||
TYPE="rc"
|
TYPE="rc"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
|
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
|
||||||
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
|
echo "semVerNum=${SEM_VER_NUM}" >> $GITHUB_OUTPUT
|
||||||
|
echo "semVerSuffix=${SEM_VER_SUFFIX}" >> $GITHUB_OUTPUT
|
||||||
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
|
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
|
||||||
echo "type=${TYPE}" >> $GITHUB_OUTPUT
|
echo "type=${TYPE}" >> $GITHUB_OUTPUT
|
||||||
env:
|
env:
|
||||||
VERSION_STRING: ${{ inputs.version }}
|
VERSION_STRING: ${{ inputs.version }}
|
||||||
- name: Validate Version
|
- name: Validate Version
|
||||||
uses: skymatic/semver-validation-action@7a6ae1c9e121540d11c9c7e4e667c83d583aa153 # v3.0.0
|
uses: skymatic/semver-validation-action@7c80b6b03a18b42884761daa9862ff5683ec8c8a # v4.0.0
|
||||||
with:
|
with:
|
||||||
version: ${{ steps.versions.outputs.semVerStr }}
|
version: ${{ steps.versions.outputs.semVerStr }}
|
||||||
@@ -49,16 +49,16 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SRC_GIT_SHA: ${{ inputs.src-tag || github.sha}}
|
SRC_GIT_SHA: ${{ inputs.src-tag || github.sha}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
repository: flathub/org.cryptomator.Cryptomator
|
repository: flathub/org.cryptomator.Cryptomator
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Checkout build script
|
- name: Checkout build script
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
path: build-scripts
|
path: build-scripts
|
||||||
- name: Checkout app source
|
- name: Checkout app source
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
path: cryptomator
|
path: cryptomator
|
||||||
ref: ${{ env.SRC_GIT_SHA }}
|
ref: ${{ env.SRC_GIT_SHA }}
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
commit: ${{ env.SRC_GIT_SHA }}
|
commit: ${{ env.SRC_GIT_SHA }}
|
||||||
- name: Copy build script for upload
|
- name: Copy build script for upload
|
||||||
run: cp org.cryptomator.Cryptomator.yaml org.cryptomator.Cryptomator.${{matrix.variant.arch}}.yaml
|
run: cp org.cryptomator.Cryptomator.yaml org.cryptomator.Cryptomator.${{matrix.variant.arch}}.yaml
|
||||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
archive: false
|
archive: false
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
@@ -129,7 +129,7 @@ jobs:
|
|||||||
EOF
|
EOF
|
||||||
done < maven-dependency-files-javafx.txt
|
done < maven-dependency-files-javafx.txt
|
||||||
) > javafx-maven-dependencies-${{ matrix.variant.arch }}.yaml
|
) > javafx-maven-dependencies-${{ matrix.variant.arch }}.yaml
|
||||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: maven-sources-${{ matrix.variant.arch }}
|
name: maven-sources-${{ matrix.variant.arch }}
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
@@ -145,12 +145,12 @@ jobs:
|
|||||||
contents: none
|
contents: none
|
||||||
steps:
|
steps:
|
||||||
- name: Download updated maven aarch64 dependencies
|
- name: Download updated maven aarch64 dependencies
|
||||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: maven-sources-aarch64
|
name: maven-sources-aarch64
|
||||||
path: mvn-src-aarch64
|
path: mvn-src-aarch64
|
||||||
- name: Download updated maven x86_64 dependencies
|
- name: Download updated maven x86_64 dependencies
|
||||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: maven-sources-x86_64
|
name: maven-sources-x86_64
|
||||||
path: mvn-src-x64
|
path: mvn-src-x64
|
||||||
@@ -176,7 +176,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
env:
|
env:
|
||||||
SRC_TAG: ${{ inputs.src-tag }}
|
SRC_TAG: ${{ inputs.src-tag }}
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
repository: flathub/org.cryptomator.Cryptomator
|
repository: flathub/org.cryptomator.Cryptomator
|
||||||
submodules: true #TODO: Update submodule!
|
submodules: true #TODO: Update submodule!
|
||||||
@@ -184,7 +184,7 @@ jobs:
|
|||||||
- name: Checkout release branch
|
- name: Checkout release branch
|
||||||
run: |
|
run: |
|
||||||
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
|
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
path: cryptomator
|
path: cryptomator
|
||||||
- name: Download source tarball and compute checksum
|
- name: Download source tarball and compute checksum
|
||||||
@@ -194,12 +194,12 @@ jobs:
|
|||||||
TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1)
|
TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1)
|
||||||
echo "value=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT"
|
echo "value=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT"
|
||||||
- name: Download updated maven aarch64 dependencies
|
- name: Download updated maven aarch64 dependencies
|
||||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: maven-sources-aarch64
|
name: maven-sources-aarch64
|
||||||
path: mvn-src-aarch64
|
path: mvn-src-aarch64
|
||||||
- name: Download updated maven x86_64 dependencies
|
- name: Download updated maven x86_64 dependencies
|
||||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: maven-sources-x86_64
|
name: maven-sources-x86_64
|
||||||
path: mvn-src-x64
|
path: mvn-src-x64
|
||||||
@@ -250,15 +250,15 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
- name: Slack Notification
|
- name: Slack Notification
|
||||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
SLACK_ICON: false
|
SLACK_ICON: ''
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
SLACK_TITLE: "Flathub release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created."
|
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_MESSAGE: "See <${{ steps.create-pr.outputs.FLATHUB_PR_URL }}|PR> on how to proceed."
|
||||||
SLACK_FOOTER: false
|
SLACK_FOOTER: ''
|
||||||
MSG_MINIMAL: true
|
MSG_MINIMAL: true
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
pacman-key --init
|
pacman-key --init
|
||||||
pacman-key --populate archlinux
|
pacman-key --populate archlinux
|
||||||
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip
|
pacman -Syu --noconfirm --needed git base-devel sudo gnupg maven unzip
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
path: cryptomator
|
path: cryptomator
|
||||||
- name: Create build user
|
- name: Create build user
|
||||||
@@ -69,13 +69,13 @@ jobs:
|
|||||||
sudo -u builder
|
sudo -u builder
|
||||||
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
|
env PKGDEST="$PKGDEST" SRCDEST="$SRCDEST"
|
||||||
makepkg --syncdeps --cleanbuild --noconfirm --log
|
makepkg --syncdeps --cleanbuild --noconfirm --log
|
||||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: arch-package
|
name: arch-package
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
path: |
|
path: |
|
||||||
${{ env.PKGDEST }}/*.pkg.tar.zst
|
${{ env.PKGDEST }}/*.pkg.tar.zst
|
||||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: pkgbuild-file
|
name: pkgbuild-file
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
@@ -108,7 +108,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
TAG: ${{ needs.get-version.outputs.semVerStr || github.event.release.tag_name }}
|
TAG: ${{ needs.get-version.outputs.semVerStr || github.event.release.tag_name }}
|
||||||
- name: Checkout cryptomator/aur repo
|
- name: Checkout cryptomator/aur repo
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
with:
|
with:
|
||||||
repository: 'cryptomator/aur'
|
repository: 'cryptomator/aur'
|
||||||
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
@@ -132,7 +132,6 @@ jobs:
|
|||||||
- name: Determine pkgrel
|
- name: Determine pkgrel
|
||||||
id: pkgrel
|
id: pkgrel
|
||||||
run: |
|
run: |
|
||||||
TARGET_VERSION='${{ needs.get-version.outputs.semVerStr }}'
|
|
||||||
CURRENT_VERSION="$(sed -nE 's/^pkgver=(.*)$/\1/p' PKGBUILD | head -n1)"
|
CURRENT_VERSION="$(sed -nE 's/^pkgver=(.*)$/\1/p' PKGBUILD | head -n1)"
|
||||||
CURRENT_REL="$(sed -nE 's/^pkgrel=([0-9]+).*$/\1/p' PKGBUILD | head -n1)"
|
CURRENT_REL="$(sed -nE 's/^pkgrel=([0-9]+).*$/\1/p' PKGBUILD | head -n1)"
|
||||||
|
|
||||||
@@ -143,11 +142,11 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "value=${NEXT_REL}" >> "$GITHUB_OUTPUT"
|
echo "value=${NEXT_REL}" >> "$GITHUB_OUTPUT"
|
||||||
echo "dist-version=${VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT"
|
echo "dist-version=${TARGET_VERSION}-${NEXT_REL}" >> "$GITHUB_OUTPUT"
|
||||||
env:
|
env:
|
||||||
VERSION: ${{ needs.get-version.outputs.semVerStr }}
|
TARGET_VERSION: ${{ needs.get-version.outputs.semVerStr }}
|
||||||
- name: Download PKGBUILD template
|
- name: Download PKGBUILD template
|
||||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: pkgbuild-file
|
name: pkgbuild-file
|
||||||
- name: Prepare PKGBUILD
|
- name: Prepare PKGBUILD
|
||||||
@@ -189,14 +188,14 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
- name: Slack Notification
|
- name: Slack Notification
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
|
||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
SLACK_ICON: false
|
SLACK_ICON: ''
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
SLACK_TITLE: "AUR release PR created for ${{ github.event.repository.name }} ${{ steps.pkgrel.outputs.dist-version }} ."
|
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_MESSAGE: "See <${{ steps.create-pr.outputs.url }}|PR> on how to proceed."
|
||||||
SLACK_FOOTER: false
|
SLACK_FOOTER: ''
|
||||||
MSG_MINIMAL: true
|
MSG_MINIMAL: true
|
||||||
|
|||||||
@@ -9,15 +9,45 @@ name: Build macOS .dmg for x64
|
|||||||
#######################################
|
#######################################
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 20 20 * *'
|
- cron: '0 20 20 * *'
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
semVerNum:
|
||||||
|
type: string
|
||||||
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
|
required: true
|
||||||
|
revisionNum:
|
||||||
|
type: string
|
||||||
|
description: 'The revision number'
|
||||||
|
required: true
|
||||||
|
semVerSuffix:
|
||||||
|
type: string
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: true
|
||||||
|
notarize:
|
||||||
|
description: 'Notarize'
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
upload-to-draft:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
outputs:
|
||||||
|
sha256-dmg:
|
||||||
|
description: "SHA256 sum of the x64 dmg"
|
||||||
|
value: ${{ jobs.build.outputs.sha256sum}}
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
semVerNum:
|
||||||
description: 'Version'
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
required: false
|
required: false
|
||||||
|
revisionNum:
|
||||||
|
description: 'The revision number'
|
||||||
|
required: false
|
||||||
|
semVerSuffix:
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: false
|
||||||
|
default: '-SNAPSHOT'
|
||||||
notarize:
|
notarize:
|
||||||
description: 'Notarize'
|
description: 'Notarize'
|
||||||
required: true
|
required: true
|
||||||
@@ -26,18 +56,18 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
JAVA_VERSION: '26.0.1+8'
|
||||||
|
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
|
||||||
|
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
|
||||||
|
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
build:
|
||||||
uses: ./.github/workflows/get-version.yml
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version }}
|
|
||||||
|
|
||||||
build-arm:
|
|
||||||
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [get-version]
|
outputs:
|
||||||
|
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -46,12 +76,12 @@ jobs:
|
|||||||
architecture: x64
|
architecture: x64
|
||||||
output-suffix: x64
|
output-suffix: x64
|
||||||
fuse-lib: macFUSE
|
fuse-lib: macFUSE
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_osx-x64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_osx-x64_bin-jmods.zip'
|
||||||
openjfx-sha: '0b4d8463f03901b7425d94628e4116b7078abb8dd540fbec415266fac20bda5c'
|
openjfx-sha: '3512fabe43aee467538d329cfbbaab3c53dff2a810f0d54e381f461d5e0fac43'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -70,7 +100,7 @@ jobs:
|
|||||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
JMOD_VERSION=${JMOD_VERSION#*@}
|
||||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
JMOD_VERSION=${JMOD_VERSION%%.*}
|
||||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||||
|
|
||||||
@@ -79,9 +109,9 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Set version
|
- name: Set version
|
||||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
run : ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Pmac -DskipTests
|
run: ./mvnw -B clean package -Pmac -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
@@ -119,16 +149,17 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
--app-version "${VERSION_NUM}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
--java-options "-Xmx256m"
|
--java-options "-Xmx256m"
|
||||||
--java-options "-Dfile.encoding=\"utf-8\""
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
|
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||||
--java-options "-Dsun.java2d.metal=true"
|
--java-options "-Dsun.java2d.metal=true"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
|
||||||
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||||
@@ -138,7 +169,7 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
||||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NUM}\""
|
||||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||||
--mac-package-identifier org.cryptomator
|
--mac-package-identifier org.cryptomator
|
||||||
--resource-dir dist/mac/resources
|
--resource-dir dist/mac/resources
|
||||||
@@ -147,16 +178,14 @@ jobs:
|
|||||||
mv appdir/Cryptomator.app Cryptomator.app
|
mv appdir/Cryptomator.app Cryptomator.app
|
||||||
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
||||||
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
|
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
|
||||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NUM}|g" Cryptomator.app/Contents/Info.plist
|
||||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist
|
||||||
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
||||||
env:
|
env:
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
|
||||||
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
|
||||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||||
- name: Generate license for dmg
|
- name: Generate license for dmg
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
./mvnw -B license:add-third-party
|
||||||
-Dlicense.thirdPartyFilename=license.rtf
|
-Dlicense.thirdPartyFilename=license.rtf
|
||||||
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
||||||
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
||||||
@@ -241,16 +270,14 @@ jobs:
|
|||||||
--eula "dist/mac/dmg/resources/license.rtf"
|
--eula "dist/mac/dmg/resources/license.rtf"
|
||||||
--icon ".background" 128 758
|
--icon ".background" 128 758
|
||||||
--icon ".VolumeIcon.icns" 512 758
|
--icon ".VolumeIcon.icns" 512 758
|
||||||
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
|
||||||
env:
|
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
|
||||||
- name: Codesign .dmg
|
- name: Codesign .dmg
|
||||||
run: |
|
run: |
|
||||||
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
||||||
env:
|
env:
|
||||||
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||||
- name: Notarize .dmg
|
- name: Notarize .dmg
|
||||||
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize || github.event_name == 'schedule'
|
if: inputs.notarize || github.event_name == 'schedule'
|
||||||
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
||||||
with:
|
with:
|
||||||
app-path: 'Cryptomator-*.dmg'
|
app-path: 'Cryptomator-*.dmg'
|
||||||
@@ -258,8 +285,12 @@ jobs:
|
|||||||
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||||
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||||
xcode-path: '/Applications/Xcode_16.app'
|
xcode-path: '/Applications/Xcode_16.app'
|
||||||
|
- id: sha256sum
|
||||||
|
run: |
|
||||||
|
read -ra CMD_OUTPUT < <(shasum -a256 Cryptomator-*.dmg)
|
||||||
|
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||||
- name: Add possible alpha/beta tags to installer name
|
- name: Add possible alpha/beta tags to installer name
|
||||||
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
|
run: mv Cryptomator-*.dmg "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg"
|
||||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
run: |
|
run: |
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
@@ -272,7 +303,7 @@ jobs:
|
|||||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: dmg-${{ matrix.output-suffix }}
|
name: dmg-${{ matrix.output-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -280,9 +311,10 @@ jobs:
|
|||||||
Cryptomator-*.asc
|
Cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish dmg on GitHub Releases
|
- name: Publish dmg on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: inputs.upload-to-draft
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
||||||
with:
|
with:
|
||||||
|
draft: true
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
@@ -1,15 +1,45 @@
|
|||||||
name: Build macOS .dmg for arm64
|
name: Build macOS .dmg for arm64
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 20 20 * *'
|
- cron: '0 20 20 * *'
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
semVerNum:
|
||||||
|
type: string
|
||||||
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
|
required: true
|
||||||
|
revisionNum:
|
||||||
|
type: string
|
||||||
|
description: 'The revision number'
|
||||||
|
required: true
|
||||||
|
semVerSuffix:
|
||||||
|
type: string
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: true
|
||||||
|
notarize:
|
||||||
|
description: 'Notarize'
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
upload-to-draft:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
outputs:
|
||||||
|
sha256-dmg:
|
||||||
|
description: "SHA256 sum of the arm64 dmg"
|
||||||
|
value: ${{ jobs.build.outputs.sha256sum}}
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
semVerNum:
|
||||||
description: 'Version'
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
required: false
|
required: false
|
||||||
|
revisionNum:
|
||||||
|
description: 'The revision number'
|
||||||
|
required: false
|
||||||
|
semVerSuffix:
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: false
|
||||||
|
default: '-SNAPSHOT'
|
||||||
notarize:
|
notarize:
|
||||||
description: 'Notarize'
|
description: 'Notarize'
|
||||||
required: true
|
required: true
|
||||||
@@ -24,18 +54,18 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: '25.0.2+10.0.LTS'
|
JAVA_VERSION: '26.0.1+8'
|
||||||
|
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
|
||||||
|
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
|
||||||
|
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
|
||||||
uses: ./.github/workflows/get-version.yml
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version }}
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [get-version]
|
outputs:
|
||||||
|
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -44,12 +74,12 @@ jobs:
|
|||||||
architecture: aarch64
|
architecture: aarch64
|
||||||
output-suffix: arm64
|
output-suffix: arm64
|
||||||
fuse-lib: FUSE-T
|
fuse-lib: FUSE-T
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_osx-aarch64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_osx-aarch64_bin-jmods.zip'
|
||||||
openjfx-sha: '4cd258001c75af7047005c5c891e2400ed11d24fbb09412324c0cbaf8b503c5a'
|
openjfx-sha: 'a52014d625b8b04e57fd71650f881c1397542b4018e1b04f1b4e66c8800a1f34'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -68,7 +98,7 @@ jobs:
|
|||||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
JMOD_VERSION=${JMOD_VERSION#*@}
|
||||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
JMOD_VERSION=${JMOD_VERSION%%.*}
|
||||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||||
|
|
||||||
@@ -77,9 +107,9 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Set version
|
- name: Set version
|
||||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
run : ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Pmac -DskipTests
|
run: ./mvnw -B clean package -Pmac -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
@@ -117,16 +147,17 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
--app-version "${VERSION_NUM}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.mac"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
--java-options "-Xmx256m"
|
--java-options "-Xmx256m"
|
||||||
--java-options "-Dfile.encoding=\"utf-8\""
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
|
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||||
--java-options "-Dsun.java2d.metal=true"
|
--java-options "-Dsun.java2d.metal=true"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
|
||||||
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\""
|
||||||
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||||
@@ -136,7 +167,7 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism"
|
||||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NUM}\""
|
||||||
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
--java-options "-XX:ErrorFile=/cryptomator/cryptomator_crash.log"
|
||||||
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
--java-options "-Dcryptomator.hub.enableTrustOnFirstUse=true"
|
||||||
--mac-package-identifier org.cryptomator
|
--mac-package-identifier org.cryptomator
|
||||||
@@ -146,16 +177,14 @@ jobs:
|
|||||||
mv appdir/Cryptomator.app Cryptomator.app
|
mv appdir/Cryptomator.app Cryptomator.app
|
||||||
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
||||||
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
|
cp dist/mac/resources/Assets.car Cryptomator.app/Contents/Resources/
|
||||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NUM}|g" Cryptomator.app/Contents/Info.plist
|
||||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NUM}|g" Cryptomator.app/Contents/Info.plist
|
||||||
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
||||||
env:
|
env:
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
|
||||||
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
|
||||||
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||||
- name: Generate license for dmg
|
- name: Generate license for dmg
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
./mvnw -B license:add-third-party
|
||||||
-Dlicense.thirdPartyFilename=license.rtf
|
-Dlicense.thirdPartyFilename=license.rtf
|
||||||
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
||||||
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
||||||
@@ -240,16 +269,14 @@ jobs:
|
|||||||
--eula "dist/mac/dmg/resources/license.rtf"
|
--eula "dist/mac/dmg/resources/license.rtf"
|
||||||
--icon ".background" 128 758
|
--icon ".background" 128 758
|
||||||
--icon ".VolumeIcon.icns" 512 758
|
--icon ".VolumeIcon.icns" 512 758
|
||||||
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
Cryptomator-${VERSION_NUM}-${{ matrix.output-suffix }}.dmg dmg
|
||||||
env:
|
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
|
||||||
- name: Codesign .dmg
|
- name: Codesign .dmg
|
||||||
run: |
|
run: |
|
||||||
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
codesign -s ${CODESIGN_IDENTITY} --timestamp Cryptomator-*.dmg
|
||||||
env:
|
env:
|
||||||
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||||
- name: Notarize .dmg
|
- name: Notarize .dmg
|
||||||
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize || github.event_name == 'schedule'
|
if: inputs.notarize || github.event_name == 'schedule'
|
||||||
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
uses: cocoalibs/xcode-notarization-action@5cf433d494b6fa26504b574c591f4dd120388846 # v1.0.3
|
||||||
with:
|
with:
|
||||||
app-path: 'Cryptomator-*.dmg'
|
app-path: 'Cryptomator-*.dmg'
|
||||||
@@ -257,8 +284,12 @@ jobs:
|
|||||||
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||||
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||||
xcode-path: '/Applications/Xcode_16.app'
|
xcode-path: '/Applications/Xcode_16.app'
|
||||||
|
- id: sha256sum
|
||||||
|
run: |
|
||||||
|
read -ra CMD_OUTPUT < <(shasum -a256 Cryptomator-*.dmg)
|
||||||
|
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||||
- name: Add possible alpha/beta tags to installer name
|
- name: Add possible alpha/beta tags to installer name
|
||||||
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
|
run: mv Cryptomator-*.dmg "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.output-suffix }}.dmg"
|
||||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
run: |
|
run: |
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
@@ -271,7 +302,7 @@ jobs:
|
|||||||
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: dmg-${{ matrix.output-suffix }}
|
name: dmg-${{ matrix.output-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -279,9 +310,10 @@ jobs:
|
|||||||
Cryptomator-*.asc
|
Cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish dmg on GitHub Releases
|
- name: Publish dmg on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: inputs.upload-to-draft
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
||||||
with:
|
with:
|
||||||
|
draft: true
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
no-response:
|
no-response:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
||||||
with:
|
with:
|
||||||
days-before-stale: 14
|
days-before-stale: 14
|
||||||
days-before-close: 0
|
days-before-close: 0
|
||||||
|
|||||||
@@ -5,35 +5,141 @@ on:
|
|||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
notify:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Download source tarball
|
- name: Notify about DEB build
|
||||||
run: |
|
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
|
||||||
curl --silent --fail-with-body --proto "=https" -L -H "Accept: application/vnd.github+json" https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz --output cryptomator-${{ github.event.release.tag_name }}.tar.gz
|
|
||||||
- name: Sign source tarball with key 615D449FE6E6A235
|
|
||||||
run: |
|
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
|
||||||
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
|
|
||||||
env:
|
|
||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
|
||||||
- name: Publish asc on GitHub Releases
|
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
|
||||||
with:
|
|
||||||
fail_on_unmatched_files: true
|
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
|
||||||
files: |
|
|
||||||
cryptomator-*.tar.gz.asc
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
|
||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
SLACK_ICON: false
|
SLACK_ICON: ''
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
|
SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
|
||||||
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/debian.yml|build deb Package>."
|
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/debian.yml|build deb Package>."
|
||||||
SLACK_FOOTER: false
|
SLACK_FOOTER: ''
|
||||||
MSG_MINIMAL: true
|
MSG_MINIMAL: true
|
||||||
|
- name: Notify about latest-version update
|
||||||
|
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||||
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
|
SLACK_ICON: ''
|
||||||
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
|
SLACK_TITLE: "Requiring version check source update for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}."
|
||||||
|
SLACK_MESSAGE: 'Check S3 bucket for <https://static.cryptomator.org/desktop/latest-version.json|latest-version.json>.'
|
||||||
|
SLACK_FOOTER: ''
|
||||||
|
MSG_MINIMAL: true
|
||||||
|
|
||||||
|
get-asset-urls:
|
||||||
|
name: Get release asset URLs
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
outputs:
|
||||||
|
is-windows-release: ${{ steps.urls.outputs.urls-present }}
|
||||||
|
msi-url: ${{ steps.urls.outputs.msi }}
|
||||||
|
exe-url: ${{ steps.urls.outputs.exe }}
|
||||||
|
steps:
|
||||||
|
- name: Extract MSI and EXE download URLs
|
||||||
|
id: urls
|
||||||
|
run: |
|
||||||
|
MSI_URL=$(jq -r '[.[] | select(.name | endswith("-x64.msi"))][0].browser_download_url // "null"' <<< "$RELEASE_ASSETS")
|
||||||
|
EXE_URL=$(jq -r '[.[] | select(.name | endswith("-x64.exe"))][0].browser_download_url // "null"' <<< "$RELEASE_ASSETS")
|
||||||
|
if [[ "$MSI_URL" == "null" || -z "$MSI_URL" || "$EXE_URL" == "null" || -z "$EXE_URL" ]]; then
|
||||||
|
echo "urls-present=false" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "urls-present=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "msi=${MSI_URL}" >> $GITHUB_OUTPUT
|
||||||
|
echo "exe=${EXE_URL}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
RELEASE_ASSETS: ${{ toJson(github.event.release.assets) }}
|
||||||
|
|
||||||
|
allowlist-msi-x64:
|
||||||
|
needs: [get-asset-urls]
|
||||||
|
if: needs.get-asset-urls.outputs.is-windows-release == 'true'
|
||||||
|
uses: ./.github/workflows/av-whitelist.yml
|
||||||
|
with:
|
||||||
|
url: ${{ needs.get-asset-urls.outputs.msi-url }}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
allowlist-exe-x64:
|
||||||
|
needs: [get-asset-urls, allowlist-msi-x64]
|
||||||
|
if: needs.get-asset-urls.outputs.is-windows-release == 'true'
|
||||||
|
uses: ./.github/workflows/av-whitelist.yml
|
||||||
|
with:
|
||||||
|
url: ${{ needs.get-asset-urls.outputs.exe-url }}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
check-release:
|
||||||
|
name: Analyzes the release for certain properties
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
outputs:
|
||||||
|
release-kind: ${{steps.determine-kind.outputs.value}} # Possible values are [alpha, beta, rc, stable, unknown]
|
||||||
|
steps:
|
||||||
|
- id: determine-kind
|
||||||
|
run: |
|
||||||
|
SEM_VER_NUM=$(echo ${SEM_VER_STR} | sed -E 's/([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
|
||||||
|
SEM_VER_SUFFIX="${SEM_VER_STR#"$SEM_VER_NUM"}"
|
||||||
|
|
||||||
|
TYPE="unknown"
|
||||||
|
if [[ -z $SEM_VER_SUFFIX ]]; then
|
||||||
|
TYPE="stable"
|
||||||
|
elif [[ $SEM_VER_SUFFIX =~ -alpha[1-9]+$ ]]; then
|
||||||
|
TYPE="alpha"
|
||||||
|
elif [[ $SEM_VER_SUFFIX =~ -beta[1-9]+$ ]]; then
|
||||||
|
TYPE="beta"
|
||||||
|
elif [[ $SEM_VER_SUFFIX =~ -rc[1-9]+$ ]]; then
|
||||||
|
TYPE="rc"
|
||||||
|
fi
|
||||||
|
echo "value=${TYPE}" >> $GITHUB_OUTPUT
|
||||||
|
env:
|
||||||
|
SEM_VER_STR: ${{ github.event.release.tag_name }}
|
||||||
|
|
||||||
|
|
||||||
|
notify-winget:
|
||||||
|
name: Notify for winget-release
|
||||||
|
if: needs.get-asset-urls.outputs.is-windows-release == 'true' && needs.check-release.outputs.release-kind == 'stable'
|
||||||
|
needs: [check-release, get-asset-urls]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Slack Notification
|
||||||
|
uses: rtCamp/action-slack-notify@33ca3be66c6f378fe1610fd1d5258632dbed5e58 # v2.4.0
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
||||||
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
|
SLACK_ICON: ''
|
||||||
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
|
SLACK_TITLE: "Release ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
|
||||||
|
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/winget.yml|release to winget>."
|
||||||
|
SLACK_FOOTER: ''
|
||||||
|
MSG_MINIMAL: true
|
||||||
|
|
||||||
|
trigger-website-update:
|
||||||
|
needs: [check-release]
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
if: needs.check-release.outputs.release-kind == 'stable'
|
||||||
|
steps:
|
||||||
|
- name: Start website update workflow
|
||||||
|
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||||
|
with:
|
||||||
|
event-type: desktop-release
|
||||||
|
token: ${{ secrets.CRYPTOBOT_WORKFLOW_DISPATCH_TOKEN }}
|
||||||
|
repository: cryptomator/cryptomator.github.io
|
||||||
|
client-payload: '{ "version": "${{ github.event.release.tag_name }}", "release": ${{ toJson(github.event.release.assets) }} }'
|
||||||
|
|
||||||
|
trigger-docs-update:
|
||||||
|
needs: [check-release, get-asset-urls]
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
if: needs.get-asset-urls.outputs.is-windows-release == 'true' && needs.check-release.outputs.release-kind == 'stable'
|
||||||
|
steps:
|
||||||
|
- name: Start docs update workflow
|
||||||
|
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||||
|
with:
|
||||||
|
event-type: desktop-release
|
||||||
|
token: ${{ secrets.CRYPTOBOT_WORKFLOW_DISPATCH_TOKEN }}
|
||||||
|
repository: cryptomator/docs
|
||||||
|
client-payload: '{ "version": "${{ github.event.release.tag_name }}", "release": ${{ toJson(github.event.release.assets) }} }'
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: 25
|
JAVA_VERSION: 26
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -16,11 +16,11 @@ jobs:
|
|||||||
name: Compile and Test
|
name: Compile and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Build and Test
|
- name: Build and Test
|
||||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage
|
run: xvfb-run ./mvnw -B clean install jacoco:report -Pcoverage
|
||||||
@@ -12,16 +12,16 @@ defaults:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'temurin'
|
JAVA_DIST: 'temurin'
|
||||||
JAVA_VERSION: 25
|
JAVA_VERSION: 26
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-preconditions:
|
check-preconditions:
|
||||||
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
|
name: Validate commits pushed to release/hotfix branch to fulfill release requirements
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ env.JAVA_DIST }}
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${SEM_VER_STR} == `mvn help:evaluate -Dexpression=project.version -q -DforceStdout` ]]; then
|
if [[ ${SEM_VER_STR} == `./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout` ]]; then
|
||||||
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
|
echo "semVerStr=${SEM_VER_STR}" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "Version not set in POM"
|
echo "Version not set in POM"
|
||||||
@@ -48,19 +48,4 @@ jobs:
|
|||||||
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\">" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
|
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\">" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
|
||||||
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
|
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Cache NVD DB
|
|
||||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
|
||||||
with:
|
|
||||||
path: ~/.m2/repository/org/owasp/dependency-check-data/
|
|
||||||
key: dependency-check-${{ github.run_id }}
|
|
||||||
restore-keys: |
|
|
||||||
dependency-check
|
|
||||||
env:
|
|
||||||
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5
|
|
||||||
- name: Run org.owasp:dependency-check plugin
|
|
||||||
id: dependency-check
|
|
||||||
continue-on-error: true
|
|
||||||
run: mvn -B verify -Pdependency-check -DskipTests
|
|
||||||
env:
|
|
||||||
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
|
|
||||||
@@ -7,12 +7,12 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
||||||
with:
|
with:
|
||||||
days-before-stale: 365
|
days-before-stale: 365
|
||||||
days-before-close: 90
|
days-before-close: 90
|
||||||
|
|||||||
+129
-99
@@ -1,15 +1,48 @@
|
|||||||
name: Build Windows Installer
|
name: Build Windows Installer
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 19 20 * *'
|
- cron: '0 19 20 * *'
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
semVerNum:
|
||||||
|
type: string
|
||||||
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
|
required: true
|
||||||
|
revisionNum:
|
||||||
|
type: string
|
||||||
|
description: 'The revision number'
|
||||||
|
required: true
|
||||||
|
semVerSuffix:
|
||||||
|
type: string
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: true
|
||||||
|
sign:
|
||||||
|
description: 'Sign binaries'
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
upload-to-draft:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
outputs:
|
||||||
|
sha256-msi:
|
||||||
|
description: "SHA256 sum of the x64 msi"
|
||||||
|
value: ${{ jobs.build-msi.outputs.sha256sum}}
|
||||||
|
sha256-exe:
|
||||||
|
description: "SHA256 sum of the x64 exe"
|
||||||
|
value: ${{ jobs.build-exe.outputs.sha256sum}}
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
semVerNum:
|
||||||
description: 'Version'
|
description: 'The Major.Minor.Patch part of the version'
|
||||||
required: false
|
required: false
|
||||||
|
revisionNum:
|
||||||
|
description: 'The revision number'
|
||||||
|
required: false
|
||||||
|
semVerSuffix:
|
||||||
|
description: 'The suffix of the version, including dash'
|
||||||
|
required: false
|
||||||
|
default: '-SNAPSHOT'
|
||||||
sign:
|
sign:
|
||||||
description: 'Sign binaries'
|
description: 'Sign binaries'
|
||||||
required: false
|
required: false
|
||||||
@@ -24,8 +57,11 @@ on:
|
|||||||
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_windows-x64_bin-jmods.zip'
|
VERSION_NUM: ${{ inputs.semVerNum || '99.99.99'}}
|
||||||
OPENJFX_JMODS_AMD64_HASH: '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3'
|
REVISION_NUM: ${{ inputs.revisionNum || '0' }}
|
||||||
|
VERSION_SUFFIX: ${{ inputs.semVerSuffix || ''}}
|
||||||
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_windows-x64_bin-jmods.zip'
|
||||||
|
OPENJFX_JMODS_AMD64_HASH: '0bf9b83260b85607a9ba200124debabd9cdb013cbc0d659e62a20192a7137907'
|
||||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
||||||
WINFSP_MSI_HASH: '073a70e00f77423e34bed98b86e600def93393ba5822204fac57a29324db9f7a'
|
WINFSP_MSI_HASH: '073a70e00f77423e34bed98b86e600def93393ba5822204fac57a29324db9f7a'
|
||||||
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
||||||
@@ -36,27 +72,23 @@ defaults:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
|
||||||
uses: ./.github/workflows/get-version.yml
|
|
||||||
with:
|
|
||||||
version: ${{ inputs.version }}
|
|
||||||
|
|
||||||
build-msi:
|
build-msi:
|
||||||
name: Build .msi Installer
|
name: Build .msi Installer
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [ get-version ]
|
outputs:
|
||||||
|
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- arch: x64
|
- arch: x64
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
java-dist: 'zulu' #cannot use temurin, see https://github.com/cryptomator/cryptomator/issues/3824#issuecomment-2829827427
|
java-dist: 'temurin'
|
||||||
java-version: '25.0.1+8'
|
java-version: '26.0.1+8'
|
||||||
java-package: 'jdk'
|
java-package: 'jdk'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ matrix.java-dist }}
|
distribution: ${{ matrix.java-dist }}
|
||||||
java-version: ${{ matrix.java-version }}
|
java-version: ${{ matrix.java-version }}
|
||||||
@@ -87,7 +119,7 @@ jobs:
|
|||||||
JMOD_VERSION_AMD64=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
JMOD_VERSION_AMD64=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||||
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@}
|
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64#*@}
|
||||||
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*}
|
JMOD_VERSION_AMD64=${JMOD_VERSION_AMD64%%.*}
|
||||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
POM_JFX_VERSION=$(./mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||||
|
|
||||||
@@ -96,9 +128,9 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Set version
|
- name: Set version
|
||||||
run: mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
run: ./mvnw versions:set -DnewVersion="${VERSION_NUM}${VERSION_SUFFIX}"
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Pwin -DskipTests
|
run: ./mvnw -B clean package -Pwin -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
@@ -136,13 +168,13 @@ jobs:
|
|||||||
--dest appdir
|
--dest appdir
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
--app-version "${VERSION_NUM}.${REVISION_NUM}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
|
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
--java-options "-Xmx256m"
|
--java-options "-Xmx256m"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${VERSION_NUM}${VERSION_SUFFIX}\""
|
||||||
--java-options "-Dfile.encoding=\"utf-8\""
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
--java-options "-Djava.net.useSystemProxies=true"
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
|
--java-options "-Dcryptomator.adminConfigPath=\"C:/ProgramData/Cryptomator/config.properties\""
|
||||||
@@ -153,7 +185,7 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Cryptomator\""
|
||||||
--java-options "-Dcryptomator.loopbackAlias=\"cryptomator-vault\""
|
--java-options "-Dcryptomator.loopbackAlias=\"cryptomator-vault\""
|
||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"msi-${REVISION_NUM}\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
|
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json\""
|
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json\""
|
||||||
@@ -188,22 +220,32 @@ jobs:
|
|||||||
}
|
}
|
||||||
$jar.Dispose()
|
$jar.Dispose()
|
||||||
}
|
}
|
||||||
- name: Extract wixhelper.dll for Codesigning #see https://github.com/cryptomator/cryptomator/issues/3130
|
- name: Get msi helper dll for code signing
|
||||||
shell: pwsh
|
|
||||||
run: |
|
run: |
|
||||||
New-Item -Path appdir/jpackage-jmod -ItemType Directory
|
${JAVA_HOME}/bin/jpackage --type msi --win-upgrade-uuid bda45523-42b1-4cae-9354-a45475ed4775 --app-image appdir/Cryptomator --dest /tmp/ --name Test --vendor Test --copyright "None" --app-version "1.0" --temp msi-helper
|
||||||
& $env:JAVA_HOME\bin\jmod.exe extract --dir jpackage-jmod "${env:JAVA_HOME}\jmods\jdk.jpackage.jmod"
|
find ./msi-helper/ -type f -name msica.dll -exec mv {} ./appdir \;
|
||||||
Get-ChildItem -Recurse -Path "jpackage-jmod" -File wixhelper.dll | Select-Object -Last 1 | Copy-Item -Destination "appdir"
|
|
||||||
- name: Sign DLLs with Azure Trusted Signing
|
- name: Sign DLLs with Azure Trusted Signing
|
||||||
if: inputs.sign || github.event_name == 'release' || github.event_name == 'schedule'
|
if: inputs.sign || github.event_name == 'schedule'
|
||||||
uses: ./.github/actions/win-sign-action
|
uses: ./.github/actions/win-sign-action
|
||||||
with:
|
with:
|
||||||
base-dir: ${{ github.workspace }}\appdir
|
base-dir: ${{ github.workspace }}\appdir
|
||||||
|
file-extensions: 'exe,dll'
|
||||||
recursive: true
|
recursive: true
|
||||||
append-signature: true
|
append-signature: true
|
||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||||
|
- name: Sign Scripts with Azure Trusted Signing
|
||||||
|
if: inputs.sign || github.event_name == 'schedule'
|
||||||
|
uses: ./.github/actions/win-sign-action
|
||||||
|
with:
|
||||||
|
base-dir: ${{ github.workspace }}\appdir\Cryptomator
|
||||||
|
file-extensions: 'ps1'
|
||||||
|
recursive: false
|
||||||
|
append-signature: false # Powershell scripts cannot be signed in append mode, see #4260
|
||||||
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
|
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||||
- name: Replace DLLs inside jars with signed ones
|
- name: Replace DLLs inside jars with signed ones
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
@@ -220,7 +262,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
- name: Generate license for MSI
|
- name: Generate license for MSI
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
./mvnw -B license:add-third-party
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf"
|
"-Dlicense.thirdPartyFilename=license.rtf"
|
||||||
"-Dlicense.outputDirectory=dist/win/resources"
|
"-Dlicense.outputDirectory=dist/win/resources"
|
||||||
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
|
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
|
||||||
@@ -229,6 +271,16 @@ jobs:
|
|||||||
"-Dlicense.failOnMissing=true"
|
"-Dlicense.failOnMissing=true"
|
||||||
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
|
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
- name: Create file association file from template
|
||||||
|
working-directory: dist/win
|
||||||
|
run: |
|
||||||
|
$Env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT = "${Env:JP_WIXWIZARD_RESOURCES}".Replace('\', '\\');
|
||||||
|
Get-Content .\resources\FAvaultFile.template.properties `
|
||||||
|
| ForEach-Object { $ExecutionContext.InvokeCommand.ExpandString($_) } `
|
||||||
|
| Out-File -FilePath .\resources\FAvaultFile.properties
|
||||||
|
env:
|
||||||
|
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources/ # requires abs path, used in resources/main.wxs
|
||||||
|
shell: pwsh
|
||||||
- name: Create MSI
|
- name: Create MSI
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jpackage
|
${JAVA_HOME}/bin/jpackage
|
||||||
@@ -239,21 +291,21 @@ jobs:
|
|||||||
--dest installer
|
--dest installer
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor "Skymatic GmbH"
|
--vendor "Skymatic GmbH"
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH"
|
--copyright "(C) 2016 - 2026 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
--app-version "${VERSION_NUM}.${REVISION_NUM}"
|
||||||
--win-menu
|
--win-menu
|
||||||
--win-dir-chooser
|
--win-dir-chooser
|
||||||
--win-shortcut-prompt
|
--win-shortcut-prompt
|
||||||
--win-update-url "https:\\cryptomator.org\downloads"
|
--win-update-url "https://cryptomator.org/downloads"
|
||||||
--win-menu-group Cryptomator
|
--win-menu-group Cryptomator
|
||||||
--resource-dir dist/win/resources
|
--resource-dir dist/win/resources
|
||||||
--license-file dist/win/resources/license.rtf
|
--license-file dist/win/resources/license.rtf
|
||||||
--file-associations dist/win/resources/FAvaultFile.properties
|
--file-associations dist/win/resources/FAvaultFile.properties
|
||||||
env:
|
env:
|
||||||
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources # requires abs path, used in resources/main.wxs
|
JP_WIXWIZARD_RESOURCES: ${{ github.workspace }}/dist/win/resources/ # requires abs path, used in resources/main.wxs
|
||||||
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir
|
JP_WIXHELPER_DIR: ${{ github.workspace }}\appdir\
|
||||||
- name: Sign MSI with Azure Trusted Signing
|
- name: Sign MSI with Azure Trusted Signing
|
||||||
if: inputs.sign || github.event_name == 'release' || github.event_name == 'schedule'
|
if: inputs.sign || github.event_name == 'schedule'
|
||||||
uses: ./.github/actions/win-sign-action
|
uses: ./.github/actions/win-sign-action
|
||||||
with:
|
with:
|
||||||
base-dir: ${{ github.workspace }}\installer
|
base-dir: ${{ github.workspace }}\installer
|
||||||
@@ -262,8 +314,12 @@ jobs:
|
|||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||||
|
- id: sha256sum
|
||||||
|
run: |
|
||||||
|
read -ra CMD_OUTPUT < <(sha256sum installer/Cryptomator-*.msi)
|
||||||
|
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||||
- name: Add possible alpha/beta tags and architecture to installer name
|
- name: Add possible alpha/beta tags and architecture to installer name
|
||||||
run: mv installer/Cryptomator-*.msi Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.arch }}.msi
|
run: mv installer/Cryptomator-*.msi "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.arch }}.msi"
|
||||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
run: |
|
run: |
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
@@ -272,7 +328,7 @@ jobs:
|
|||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: msi-${{ matrix.arch }}
|
name: msi-${{ matrix.arch }}
|
||||||
path: |
|
path: |
|
||||||
@@ -283,18 +339,20 @@ jobs:
|
|||||||
build-exe:
|
build-exe:
|
||||||
name: Build .exe installer
|
name: Build .exe installer
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
needs: [ get-version, build-msi ]
|
needs: [ build-msi ]
|
||||||
|
outputs:
|
||||||
|
sha256sum: ${{ steps.sha256sum.outputs.value }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- arch: x64
|
- arch: x64
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
executable-suffix: x64
|
executable-suffix: x64
|
||||||
java-dist: 'zulu'
|
java-dist: 'temurin'
|
||||||
java-version: '24.0.1+9'
|
java-version: '26.0.1+8'
|
||||||
java-package: 'jdk'
|
java-package: 'jdk'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Install wix and extensions
|
- name: Install wix and extensions
|
||||||
run: |
|
run: |
|
||||||
dotnet tool install --global wix --version ${WIX_VERSION}
|
dotnet tool install --global wix --version ${WIX_VERSION}
|
||||||
@@ -303,14 +361,14 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
WIX_VERSION: ${{ env.WIX_VERSION }}
|
WIX_VERSION: ${{ env.WIX_VERSION }}
|
||||||
- name: Download .msi
|
- name: Download .msi
|
||||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: msi-${{ matrix.arch }}
|
name: msi-${{ matrix.arch }}
|
||||||
path: dist/win/bundle/resources
|
path: dist/win/bundle/resources
|
||||||
- name: Strip version info from msi file name
|
- name: Strip version info from msi file name
|
||||||
run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
|
run: mv dist/win/bundle/resources/Cryptomator*.msi dist/win/bundle/resources/Cryptomator.msi
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||||
with:
|
with:
|
||||||
distribution: ${{ matrix.java-dist }}
|
distribution: ${{ matrix.java-dist }}
|
||||||
java-version: ${{ matrix.java-version }}
|
java-version: ${{ matrix.java-version }}
|
||||||
@@ -319,7 +377,7 @@ jobs:
|
|||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Generate license for exe
|
- name: Generate license for exe
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
./mvnw -B license:add-third-party
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf"
|
"-Dlicense.thirdPartyFilename=license.rtf"
|
||||||
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
|
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
|
||||||
"-Dlicense.outputDirectory=dist/win/bundle/resources"
|
"-Dlicense.outputDirectory=dist/win/bundle/resources"
|
||||||
@@ -330,10 +388,10 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
- name: Download WinFsp
|
- name: Download WinFsp
|
||||||
run: |
|
run: |
|
||||||
curl --silent --fail-with-body --proto "=https" -L ${{ env.WINFSP_MSI }} --output $env:WINFSP_PATH
|
curl --silent --fail-with-body --proto "=https" -L "$env:WINFSP_MSI" --output $env:WINFSP_PATH
|
||||||
$computedHash = (Get-FileHash -Path $env:WINFSP_PATH -Algorithm SHA256).Hash.ToLower()
|
$computedHash = (Get-FileHash -Path "$env:WINFSP_PATH" -Algorithm SHA256).Hash.ToLower()
|
||||||
if ($computedHash -ne "${{ env.WINFSP_MSI_HASH }}") {
|
if ($computedHash -ne "$env:WINFSP_MSI_HASH") {
|
||||||
throw "Checksum mismatch for $env:WINFSP_PATH (expected ${{ env.WINFSP_MSI_HASH }}, got $computedHash)."
|
throw "Checksum mismatch for ${env:WINFSP_PATH} (expected ${env:WINFSP_MSI_HASH}, got $computedHash)."
|
||||||
}
|
}
|
||||||
env:
|
env:
|
||||||
WINFSP_PATH: 'dist/win/bundle/resources/winfsp.msi'
|
WINFSP_PATH: 'dist/win/bundle/resources/winfsp.msi'
|
||||||
@@ -347,21 +405,22 @@ jobs:
|
|||||||
run: >
|
run: >
|
||||||
wix build
|
wix build
|
||||||
-define BundleName="Cryptomator"
|
-define BundleName="Cryptomator"
|
||||||
-define BundleVersion="${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum}}"
|
-define BundleVersion="${VERSION_NUM}.${REVISION_NUM}"
|
||||||
-define BundleVendor="Skymatic GmbH"
|
-define BundleVendor="Skymatic GmbH"
|
||||||
-define BundleCopyright="(C) 2016 - 2025 Skymatic GmbH"
|
-define BundleCopyright="(C) 2016 - 2026 Skymatic GmbH"
|
||||||
-define AboutUrl="https://cryptomator.org"
|
-define AboutUrl="https://cryptomator.org"
|
||||||
-define HelpUrl="https://cryptomator.org/contact"
|
-define HelpUrl="https://cryptomator.org/contact"
|
||||||
-define UpdateUrl="https://cryptomator.org/downloads/"
|
-define UpdateUrl="https://cryptomator.org/downloads/"
|
||||||
-ext "WixToolset.Util.wixext"
|
-ext "WixToolset.Util.wixext"
|
||||||
-ext "WixToolset.BootstrapperApplications.wixext"
|
-ext "WixToolset.BootstrapperApplications.wixext"
|
||||||
./bundle/bundleWithWinfsp.wxs
|
./bundle/bundleWithWinfsp.wxs
|
||||||
-out "../../installer/unsigned/Cryptomator-Installer.exe"
|
-out "../../installer/Cryptomator-Installer.exe"
|
||||||
- name: Detach burn engine in preparation to sign
|
- name: Detach burn engine in preparation to sign
|
||||||
|
if: inputs.sign || github.event_name == 'schedule'
|
||||||
run: >
|
run: >
|
||||||
wix burn detach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe
|
wix burn detach installer/Cryptomator-Installer.exe -engine tmp/engine.exe
|
||||||
- name: Sign WiX burn engine with Azure Trusted Signing
|
- name: Sign WiX burn engine with Azure Trusted Signing
|
||||||
if: inputs.sign || github.event_name == 'release' || github.event_name == 'schedule'
|
if: inputs.sign || github.event_name == 'schedule'
|
||||||
uses: ./.github/actions/win-sign-action
|
uses: ./.github/actions/win-sign-action
|
||||||
with:
|
with:
|
||||||
base-dir: ${{ github.workspace }}\tmp
|
base-dir: ${{ github.workspace }}\tmp
|
||||||
@@ -372,10 +431,13 @@ jobs:
|
|||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||||
- name: Reattach signed burn engine to installer
|
- name: Reattach signed burn engine to installer
|
||||||
run: >
|
if: inputs.sign || github.event_name == 'schedule'
|
||||||
wix burn reattach installer/unsigned/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Move-Item -Path installer/Cryptomator-Installer.exe -Destination tmp/Cryptomator-Installer.exe
|
||||||
|
wix burn reattach tmp/Cryptomator-Installer.exe -engine tmp/engine.exe -o installer/Cryptomator-Installer.exe
|
||||||
- name: Sign EXE installer with Azure Trusted Signing
|
- name: Sign EXE installer with Azure Trusted Signing
|
||||||
if: inputs.sign || github.event_name == 'release' || github.event_name == 'schedule'
|
if: inputs.sign || github.event_name == 'schedule'
|
||||||
uses: ./.github/actions/win-sign-action
|
uses: ./.github/actions/win-sign-action
|
||||||
with:
|
with:
|
||||||
base-dir: ${{ github.workspace }}\installer
|
base-dir: ${{ github.workspace }}\installer
|
||||||
@@ -385,8 +447,12 @@ jobs:
|
|||||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||||
|
- id: sha256sum
|
||||||
|
run: |
|
||||||
|
read -ra CMD_OUTPUT < <(sha256sum installer/Cryptomator-*.exe)
|
||||||
|
echo "value=${CMD_OUTPUT[0]}" >> $GITHUB_OUTPUT
|
||||||
- name: Add possible alpha/beta tags to installer name
|
- name: Add possible alpha/beta tags to installer name
|
||||||
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.executable-suffix }}.exe
|
run: mv installer/Cryptomator-Installer.exe "Cryptomator-${VERSION_NUM}${VERSION_SUFFIX}-${{ matrix.executable-suffix }}.exe"
|
||||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
run: |
|
run: |
|
||||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||||
@@ -395,7 +461,7 @@ jobs:
|
|||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: exe-${{ matrix.executable-suffix }}
|
name: exe-${{ matrix.executable-suffix }}
|
||||||
path: |
|
path: |
|
||||||
@@ -405,58 +471,22 @@ jobs:
|
|||||||
|
|
||||||
publish:
|
publish:
|
||||||
name: Publish installers to the github release
|
name: Publish installers to the github release
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
if: inputs.upload-to-draft
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ build-msi, build-exe ]
|
needs: [ build-msi, build-exe ]
|
||||||
outputs:
|
|
||||||
download-url-msi-x64: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }}
|
|
||||||
download-url-exe-x64: ${{ fromJSON(steps.publish.outputs.assets)[2].browser_download_url }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download installers
|
- name: Download installers
|
||||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
- name: Publish installers on GitHub Releases
|
- name: Publish installers on GitHub Releases
|
||||||
id: publish
|
id: publish
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
||||||
with:
|
with:
|
||||||
|
draft: true
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
# do not change ordering of filelist, required for correct job output
|
|
||||||
files: |
|
files: |
|
||||||
*x64.msi
|
*x64.msi
|
||||||
*x64.exe
|
*x64.exe
|
||||||
*.asc
|
*.asc
|
||||||
|
|
||||||
allowlist-msi-x64:
|
|
||||||
uses: ./.github/workflows/av-whitelist.yml
|
|
||||||
needs: [ publish ]
|
|
||||||
with:
|
|
||||||
url: ${{ needs.publish.outputs.download-url-msi-x64 }}
|
|
||||||
secrets: inherit
|
|
||||||
|
|
||||||
allowlist-exe-x64:
|
|
||||||
uses: ./.github/workflows/av-whitelist.yml
|
|
||||||
needs: [ publish, allowlist-msi-x64 ]
|
|
||||||
with:
|
|
||||||
url: ${{ needs.publish.outputs.download-url-exe-x64 }}
|
|
||||||
secrets: inherit
|
|
||||||
|
|
||||||
notify-winget:
|
|
||||||
name: Notify for winget-release
|
|
||||||
if: needs.get-version.outputs.versionType == 'stable'
|
|
||||||
needs: [publish, get-version]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Slack Notification
|
|
||||||
uses: rtCamp/action-slack-notify@e31e87e03dd19038e411e38ae27cbad084a90661 # v2.3.3
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CRYPTOMATOR_DESKTOP }}
|
|
||||||
SLACK_USERNAME: 'Cryptobot'
|
|
||||||
SLACK_ICON: false
|
|
||||||
SLACK_ICON_EMOJI: ':bot:'
|
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
|
||||||
SLACK_TITLE: "MSI packages of ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} published."
|
|
||||||
SLACK_MESSAGE: "Ready to <https://github.com/${{ github.repository }}/actions/workflows/winget.yml| release them to winget>."
|
|
||||||
SLACK_FOOTER: false
|
|
||||||
MSG_MINIMAL: true
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
GH_TOKEN: ${{ secrets.CRYPTOBOT_PR_TOKEN }}
|
||||||
- name: Submit package
|
- name: Submit package
|
||||||
uses: vedantmgoyal2009/winget-releaser@19e706d4c9121098010096f9c495a70a7518b30f # no_specific_version
|
uses: vedantmgoyal2009/winget-releaser@7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 # no_specific_version
|
||||||
with:
|
with:
|
||||||
identifier: Cryptomator.Cryptomator
|
identifier: Cryptomator.Cryptomator
|
||||||
version: ${{ inputs.tag }}
|
version: ${{ inputs.tag }}
|
||||||
|
|||||||
Generated
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="25" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_26" project-jdk-name="temurin-26" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
</envs>
|
</envs>
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-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" />
|
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
</envs>
|
</envs>
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-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" />
|
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator-Dev/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator-Dev/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator-Dev" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator-Dev/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Library/Application Support/Cryptomator-Dev/mnt" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.hub.enableTrustOnFirstUse=true -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
wrapperVersion=3.3.4
|
||||||
|
distributionType=only-script
|
||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip
|
||||||
|
distributionSha256Sum=5af3b743dd8b876b5c45da33b676251e5f1687712644abb4ee519ca56e1d89ce
|
||||||
+31
-1
@@ -8,11 +8,41 @@ The changelog starts with version 1.19.0.
|
|||||||
Changes to prior versions can be found on the [Github release page](https://github.com/cryptomator/cryptomator/releases).
|
Changes to prior versions can be found on the [Github release page](https://github.com/cryptomator/cryptomator/releases).
|
||||||
|
|
||||||
|
|
||||||
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.19.1...HEAD)
|
## [Unreleased](https://github.com/cryptomator/cryptomator/compare/1.19.3...HEAD)
|
||||||
|
|
||||||
No changes yet.
|
No changes yet.
|
||||||
|
|
||||||
|
|
||||||
|
## [1.19.3](https://github.com/cryptomator/cryptomator/releases/1.19.3) - 2026-06-29
|
||||||
|
### Added
|
||||||
|
* New error dialog if importing a vault fails ([#4243](https://github.com/cryptomator/cryptomator/pull/4243))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* Fixed Cryptomator file extensions were not registered on Windows ([#4219](https://github.com/cryptomator/cryptomator/issues/4219))
|
||||||
|
* Fixed warning was displayed when accessing update tab in settings even though an update check did not ran ([#4199](https://github.com/cryptomator/cryptomator/pull/4199))
|
||||||
|
* Fixed several Decrypt Name dialogs could be opened on the same vault ([#4164](https://github.com/cryptomator/cryptomator/pull/4164))
|
||||||
|
* Fixed not all mount options in vault specific settings could be displayed ([#4227](https://github.com/cryptomator/cryptomator/pull/4227))
|
||||||
|
* Fixed localhost alias on Windows was not removed on uninstall ([#3993](https://github.com/cryptomator/cryptomator/issues/3993))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Refactored release pipeline to allow immutable releases ([#4205](https://github.com/cryptomator/cryptomator/pull/4205))
|
||||||
|
* Updated to JDK 26.0.1 ([#4244](https://github.com/cryptomator/cryptomator/pull/4244))
|
||||||
|
* Updated to JavaFX 25.0.3 ([#4255](https://github.com/cryptomator/cryptomator/pull/4255))
|
||||||
|
* Drop signing with Actalis issued certificate ([#4169](https://github.com/cryptomator/cryptomator/pull/4169), [#4262](https://github.com/cryptomator/cryptomator/pull/4262))
|
||||||
|
* Fix dagger binding graph issues ([#4147](https://github.com/cryptomator/cryptomator/pull/4147))
|
||||||
|
* Added flatpak build to CI ([#4199](https://github.com/cryptomator/cryptomator/pull/4199))
|
||||||
|
* Updated dependencies:
|
||||||
|
- `org.cryptomator:webdav-nio-adapter` from 3.0.1 to 3.0.2
|
||||||
|
- `org.cryptomator:integrations-api` from 1.8.0 to 1.9.0
|
||||||
|
- `org.slf4j:slf4j-api` from 2.0.17 to 2.0.18
|
||||||
|
- `ch.qos.logback:logback-core` from 1.5.32 to 1.5.35
|
||||||
|
- `ch.qos.logback:logback-classic` from 1.5.32 to 1.5.35
|
||||||
|
- `com.auth0:java-jwt` from 4.5.1 4.5.2
|
||||||
|
- `com.fasterxml.jackson.core:jackson-databind` from 2.21.1 to 2.21.4
|
||||||
|
- `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.21.1 to 2.21.4
|
||||||
|
- `com.github.ben-manes.caffeine:caffeine` from 3.2.3 to 3.2.4
|
||||||
|
|
||||||
|
|
||||||
## [1.19.2](https://github.com/cryptomator/cryptomator/releases/1.19.2) - 2026-03-20
|
## [1.19.2](https://github.com/cryptomator/cryptomator/releases/1.19.2) - 2026-03-20
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ Become our Gold Sponsor and showcase your brand to a targeted audience! Please c
|
|||||||
<tr>
|
<tr>
|
||||||
<td><a href="https://www.gee-whiz.de/"><img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="56"></a></td>
|
<td><a href="https://www.gee-whiz.de/"><img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="56"></a></td>
|
||||||
<td><a href="https://www.route4me.com/"><img src="https://cryptomator.org/img/sponsors/route4me.svg" alt="Route4Me" height="56"></a></td>
|
<td><a href="https://www.route4me.com/"><img src="https://cryptomator.org/img/sponsors/route4me.svg" alt="Route4Me" height="56"></a></td>
|
||||||
|
<td><a href="https://www.apivoid.com/"><img src="https://cryptomator.org/img/sponsors/apivoid.svg" alt="ApiVoid" height="56"></a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -78,16 +79,12 @@ For more information on the security details visit [cryptomator.org](https://doc
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* JDK 25 (e.g. temurin, zulu)
|
* JDK 26 (e.g. temurin, zulu)
|
||||||
* Maven 3
|
|
||||||
|
|
||||||
### Run Maven
|
### Run Maven
|
||||||
|
|
||||||
```
|
```
|
||||||
mvn clean install
|
./mvnw clean install
|
||||||
# or mvn clean install -Pwin
|
|
||||||
# or mvn clean install -Pmac
|
|
||||||
# or mvn clean install -Plinux
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This will build all the jars and bundle them together with their OS-specific dependencies under `target`. This can now be used to build native packages.
|
This will build all the jars and bundle them together with their OS-specific dependencies under `target`. This can now be used to build native packages.
|
||||||
|
|||||||
Vendored
+9
-9
@@ -6,29 +6,29 @@ REVISION_NO=`git rev-list --count HEAD`
|
|||||||
|
|
||||||
# check preconditions
|
# check preconditions
|
||||||
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
||||||
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found."; exit 1; }
|
[ -x ../../../mvnw ] || { echo >&2 "mvnw not found at ../../../mvnw."; exit 1; }
|
||||||
command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; }
|
command -v curl >/dev/null 2>&1 || { echo >&2 "curl not found."; exit 1; }
|
||||||
command -v unzip >/dev/null 2>&1 || { echo >&2 "unzip not found."; exit 1; }
|
command -v unzip >/dev/null 2>&1 || { echo >&2 "unzip not found."; exit 1; }
|
||||||
|
|
||||||
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
|
VERSION=$(../../../mvnw -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||||
SEMVER_STR=${VERSION}
|
SEMVER_STR=${VERSION}
|
||||||
CPU_ARCH=$(uname -m)
|
CPU_ARCH=$(uname -m)
|
||||||
|
|
||||||
if [[ ! "${CPU_ARCH}" =~ x86_64|aarch64 ]]; then echo "Platform ${CPU_ARCH} not supported"; exit 1; fi
|
if [[ ! "${CPU_ARCH}" =~ x86_64|aarch64 ]]; then echo "Platform ${CPU_ARCH} not supported"; exit 1; fi
|
||||||
|
|
||||||
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
|
../../../mvnw -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
|
||||||
|
|
||||||
# compile
|
# compile
|
||||||
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
|
../../../mvnw -B -f ../../../pom.xml clean package -DskipTests
|
||||||
cp ../../../LICENSE.txt ../../../target
|
cp ../../../LICENSE.txt ../../../target
|
||||||
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
||||||
|
|
||||||
JAVAFX_VERSION=25.0.2
|
JAVAFX_VERSION=25.0.3
|
||||||
JAVAFX_ARCH="x64"
|
JAVAFX_ARCH="x64"
|
||||||
JAVAFX_JMODS_SHA256='e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b'
|
JAVAFX_JMODS_SHA256='47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50'
|
||||||
if [ "${CPU_ARCH}" = "aarch64" ]; then
|
if [ "${CPU_ARCH}" = "aarch64" ]; then
|
||||||
JAVAFX_ARCH="aarch64"
|
JAVAFX_ARCH="aarch64"
|
||||||
JAVAFX_JMODS_SHA256='c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646'
|
JAVAFX_JMODS_SHA256='e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# download javaFX jmods
|
# download javaFX jmods
|
||||||
@@ -42,7 +42,7 @@ unzip -o -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/jav
|
|||||||
JMOD_VERSION=$(jmod describe ./openjfx-jmods/javafx.base.jmod | head -1)
|
JMOD_VERSION=$(jmod describe ./openjfx-jmods/javafx.base.jmod | head -1)
|
||||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
JMOD_VERSION=${JMOD_VERSION#*@}
|
||||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
JMOD_VERSION=${JMOD_VERSION%%.*}
|
||||||
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout -B -f ../../../pom.xml)
|
POM_JFX_VERSION=$(../../../mvnw help:evaluate "-Dexpression=javafx.version" -q -DforceStdout -B -f ../../../pom.xml)
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||||
if [ $POM_JFX_VERSION -ne $JMOD_VERSION ]; then
|
if [ $POM_JFX_VERSION -ne $JMOD_VERSION ]; then
|
||||||
@@ -82,7 +82,7 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--vendor "Skymatic GmbH" \
|
--vendor "Skymatic GmbH" \
|
||||||
--java-options "--enable-preview" \
|
--java-options "--enable-preview" \
|
||||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
|
--copyright "(C) 2016 - 2026 Skymatic GmbH" \
|
||||||
--java-options "-Xss5m" \
|
--java-options "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--app-version "${VERSION}.${REVISION_NO}" \
|
--app-version "${VERSION}.${REVISION_NO}" \
|
||||||
|
|||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=Cryptomator
|
Name=Cryptomator
|
||||||
Comment=Cloud Storage Encryption Utility
|
Comment=Cloud Storage Encryption Utility
|
||||||
Exec=cryptomator %F
|
Exec=cryptomator %U
|
||||||
Icon=org.cryptomator.Cryptomator
|
Icon=org.cryptomator.Cryptomator
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Utility;Security;FileTools;
|
Categories=Utility;Security;FileTools;
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
StartupWMClass=org.cryptomator.launcher.Cryptomator$MainApp
|
StartupWMClass=org.cryptomator.launcher.Cryptomator$MainApp
|
||||||
MimeType=application/vnd.cryptomator.encrypted;application/vnd.cryptomator.vault;
|
MimeType=application/vnd.cryptomator.encrypted;application/vnd.cryptomator.vault;x-scheme-handler/org.cryptomator;
|
||||||
|
|||||||
@@ -84,6 +84,9 @@
|
|||||||
</content_rating>
|
</content_rating>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release date="2026-06-29" version="1.19.3">
|
||||||
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.3</url>
|
||||||
|
</release>
|
||||||
<release date="2026-03-20" version="1.19.2">
|
<release date="2026-03-20" version="1.19.2">
|
||||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.2</url>
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.19.2</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
Vendored
+1
-1
@@ -2,7 +2,7 @@ Source: cryptomator
|
|||||||
Maintainer: Cryptobot <releases@cryptomator.org>
|
Maintainer: Cryptobot <releases@cryptomator.org>
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends: debhelper (>=10), openjdk-25-jdk (>= 25+36), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1
|
Build-Depends: debhelper (>=10), coffeelibs-jdk-26 (>= 26.0.1+8-0ppa1), libgtk-3-0 (>= 3.20.0), libxxf86vm1, libgl1
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://cryptomator.org
|
Homepage: https://cryptomator.org
|
||||||
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
||||||
|
|||||||
Vendored
+2
-3
@@ -4,12 +4,11 @@
|
|||||||
# Uncomment this to turn on verbose mode.
|
# Uncomment this to turn on verbose mode.
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
|
JAVA_HOME = /usr/lib/jvm/java-26-coffeelibs
|
||||||
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
||||||
ifeq ($(DEB_BUILD_ARCH),amd64)
|
ifeq ($(DEB_BUILD_ARCH),amd64)
|
||||||
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-amd64
|
|
||||||
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
||||||
else ifeq ($(DEB_BUILD_ARCH),arm64)
|
else ifeq ($(DEB_BUILD_ARCH),arm64)
|
||||||
JAVA_HOME = /usr/lib/jvm/java-25-openjdk-arm64
|
|
||||||
JMODS_PATH = jmods/aarch64:${JAVA_HOME}/jmods
|
JMODS_PATH = jmods/aarch64:${JAVA_HOME}/jmods
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ override_dh_auto_build:
|
|||||||
--vendor "Skymatic GmbH" \
|
--vendor "Skymatic GmbH" \
|
||||||
--java-options "--enable-preview" \
|
--java-options "--enable-preview" \
|
||||||
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
--java-options "--enable-native-access=javafx.graphics,org.cryptomator.jfuse.linux.amd64,org.cryptomator.jfuse.linux.aarch64,org.purejava.appindicator" \
|
||||||
--copyright "(C) 2016 - 2025 Skymatic GmbH" \
|
--copyright "(C) 2016 - 2026 Skymatic GmbH" \
|
||||||
--java-options "-Xss5m" \
|
--java-options "-Xss5m" \
|
||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
|
|||||||
+10
-10
@@ -94,7 +94,7 @@ modules:
|
|||||||
- mkdir maven
|
- mkdir maven
|
||||||
- tar xf maven.tar.gz --strip-components=1 --exclude=jansi-native --directory=maven
|
- tar xf maven.tar.gz --strip-components=1 --exclude=jansi-native --directory=maven
|
||||||
# Build project
|
# Build project
|
||||||
- maven/bin/mvn clean package -DskipTests -P"linux-$(uname -m)"
|
- maven/bin/mvn clean package -DskipTests
|
||||||
- cp target/cryptomator-*.jar target/mods
|
- cp target/cryptomator-*.jar target/mods
|
||||||
- cd target
|
- cd target
|
||||||
- $JAVA_HOME/bin/jlink
|
- $JAVA_HOME/bin/jlink
|
||||||
@@ -114,7 +114,7 @@ modules:
|
|||||||
--dest .
|
--dest .
|
||||||
--name Cryptomator
|
--name Cryptomator
|
||||||
--vendor 'Skymatic GmbH'
|
--vendor 'Skymatic GmbH'
|
||||||
--copyright '(C) 2016 - 2025 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 '--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 "--sun-misc-unsafe-memory-access=allow"
|
||||||
--java-options '-Xss5m'
|
--java-options '-Xss5m'
|
||||||
@@ -150,26 +150,26 @@ modules:
|
|||||||
dest-filename: jdk.tar.gz
|
dest-filename: jdk.tar.gz
|
||||||
only-arches:
|
only-arches:
|
||||||
- x86_64
|
- 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
|
url: https://github.com/adoptium/temurin26-binaries/releases/download/jdk-26.0.1%2B8/OpenJDK26U-jdk_x64_linux_hotspot_26.0.1_8.tar.gz
|
||||||
sha512: 29043fde119a031c2ca8d57aed445fedd9e7f74608fcdc7a809076ba84cfd1c31f08de2ecccf352e159fdcd1cae172395ed46363007552ff242057826c81ab3a
|
sha512: eb46cda97ffd46e2e0c1f6f977dc204d9cc969b958946287b7e7d0bfe859fd92faccb2f6ef79995421a963c6de140c436af559403e0a2cd27c90b06c20260d5c
|
||||||
- type: file
|
- type: file
|
||||||
dest-filename: jdk.tar.gz
|
dest-filename: jdk.tar.gz
|
||||||
only-arches:
|
only-arches:
|
||||||
- aarch64
|
- 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
|
url: https://github.com/adoptium/temurin26-binaries/releases/download/jdk-26.0.1%2B8/OpenJDK26U-jdk_aarch64_linux_hotspot_26.0.1_8.tar.gz
|
||||||
sha512: f1d3ccec3e1f1bed9d632f14b9223709d6e5c2e0d922125d068870dd3016492a2ca8f08924d4a9d0dc5eb2159fa09efee366a748fd0093475baf29e5c70c781a
|
sha512: 3c31671552712a8f0df96df2eeae7e9ad5156c0e98ebd5bf4fa04c14bba58bd5e19ff567ddcdc7aa478f6f4b0a852762a09bd88d3132acb121e667cfe0397034
|
||||||
- type: file
|
- type: file
|
||||||
dest-filename: openjfx.zip
|
dest-filename: openjfx.zip
|
||||||
only-arches:
|
only-arches:
|
||||||
- x86_64
|
- x86_64
|
||||||
url: https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-x64_bin-jmods.zip
|
url: https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-x64_bin-jmods.zip
|
||||||
sha512: 21f550217101c513f9eb1d7947eba30cb79618238e6539ce770e54e84b01574cdaeba40af602391145f163dd8e43e3794395467413152f13ffffeff948b0ca1b
|
sha512: 75605440f13d0337e70ada1220f77d0d9780fb4602e676f1f07674d8b2e296b25080549d07edcca8f66733e15388860e3472d4ff6d51fd72b0452ed8bbe78022
|
||||||
- type: file
|
- type: file
|
||||||
dest-filename: openjfx.zip
|
dest-filename: openjfx.zip
|
||||||
only-arches:
|
only-arches:
|
||||||
- aarch64
|
- aarch64
|
||||||
url: https://download2.gluonhq.com/openjfx/25.0.2/openjfx-25.0.2_linux-aarch64_bin-jmods.zip
|
url: https://download2.gluonhq.com/openjfx/25.0.3/openjfx-25.0.3_linux-aarch64_bin-jmods.zip
|
||||||
sha512: a9268409b3803e386490bf1319d0f0a14173cebe862c12254cd51b430ee0a297437d9e38d5ebeae0da8899be898b312b103330d09dcfd3e63c1e7d15f2f14311
|
sha512: 30c509b880ced1b29e0fafa41073d2350a16296d9439124b9a0e3bb391bf7f27e34d5abcfb338df11e5e898175699bceac40574bc9ad123ff071a5964f8fb14d
|
||||||
- type: file
|
- type: file
|
||||||
dest-filename: maven.tar.gz
|
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
|
url: https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.9.13/apache-maven-3.9.13-bin.tar.gz
|
||||||
|
|||||||
Vendored
+8
-8
@@ -11,11 +11,11 @@ pkgdesc="Multiplatform transparent client-side encryption of your files in the c
|
|||||||
arch=('any')
|
arch=('any')
|
||||||
url="https://cryptomator.org/"
|
url="https://cryptomator.org/"
|
||||||
license=('GPL3')
|
license=('GPL3')
|
||||||
depends=('fuse3' 'alsa-lib' 'hicolor-icon-theme' 'libxtst' 'libnet' 'libxrender')
|
depends=('fuse3' 'alsa-lib' 'hicolor-icon-theme' 'libxtst' 'libnet' 'libxrender' 'desktop-file-utils')
|
||||||
makedepends=('maven' 'unzip')
|
makedepends=('maven' 'unzip')
|
||||||
optdepends=('keepassxc-cryptomator: Use KeePassXC to store vault passwords' 'ttf-hanazono: Install this font when using Japanese system language')
|
optdepends=('keepassxc-cryptomator: Use KeePassXC to store vault passwords' 'ttf-hanazono: Install this font when using Japanese system language')
|
||||||
_jdkver=25.0.2+10
|
_jdkver=26.0.1+8
|
||||||
_jfxver=25.0.2
|
_jfxver=25.0.3
|
||||||
_src_app_dir=cryptomator-${pkgver//_/-}
|
_src_app_dir=cryptomator-${pkgver//_/-}
|
||||||
source=($SOURCES);
|
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"
|
source_x86_64=("jdk-${_jdkver}.tar.gz::https://github.com/adoptium/temurin${_jdkver:0:2}-binaries/releases/download/jdk-${_jdkver//\+/%2B}/OpenJDK${_jdkver:0:2}U-jdk_x64_linux_hotspot_${_jdkver//\+/_}.tar.gz"
|
||||||
@@ -24,10 +24,10 @@ source_aarch64=("jdk-${_jdkver}.tar.gz::https://github.com/adoptium/temurin${_jd
|
|||||||
"openjfx-${_jfxver}.zip::https://download2.gluonhq.com/openjfx/${_jfxver}/openjfx-${_jfxver}_linux-aarch64_bin-jmods.zip")
|
"openjfx-${_jfxver}.zip::https://download2.gluonhq.com/openjfx/${_jfxver}/openjfx-${_jfxver}_linux-aarch64_bin-jmods.zip")
|
||||||
noextract=("jdk-${_jdkver}.tar.gz" "openjfx-${_jfxver}.zip")
|
noextract=("jdk-${_jdkver}.tar.gz" "openjfx-${_jfxver}.zip")
|
||||||
sha256sums=($SOURCES_SHA)
|
sha256sums=($SOURCES_SHA)
|
||||||
sha256sums_x86_64=('987387933b64b9833846dee373b640440d3e1fd48a04804ec01a6dbf718e8ab8'
|
sha256sums_x86_64=('8e512f13e575a43655fc92319436c94890c137b9035cc6bd6f9cf24239704d3a'
|
||||||
'e0a9c29d8cf3af9b8b48848b43f87b5785bc107c53a951b19668ce05842bba1b')
|
'47035c653863a8e4be3dc6f142b8dbd84b4bb1efc9a8cbc68413e6a5ff5e9f50')
|
||||||
sha256sums_aarch64=('a9d73e711d967dc44896d4f430f73a68fd33590dabc29a7f2fb9f593425b854c'
|
sha256sums_aarch64=('613f9b2861dea937b24d5eca745ef8567733b377d0bb612195acaad0e3f61360'
|
||||||
'c3408f818693cce09e59829a8e862a82c7695fdfcd585c41cfd527f5fc3fe646')
|
'e3fd682354346845d2944a2da2b1ff2b6cb9259d92027f2f9c121b9b93c5e42f')
|
||||||
options=('!strip')
|
options=('!strip')
|
||||||
|
|
||||||
validpgpkeys=('58117AFA1F85B3EEC154677D615D449FE6E6A235')
|
validpgpkeys=('58117AFA1F85B3EEC154677D615D449FE6E6A235')
|
||||||
@@ -47,7 +47,7 @@ build() {
|
|||||||
|
|
||||||
cd "${srcdir}/${_src_app_dir}"
|
cd "${srcdir}/${_src_app_dir}"
|
||||||
|
|
||||||
mvn -B clean package -DskipTests -Plinux
|
mvn -B clean package -DskipTests
|
||||||
|
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
cp target/cryptomator-*.jar target/mods
|
cp target/cryptomator-*.jar target/mods
|
||||||
|
|||||||
Vendored
+10
-9
@@ -24,29 +24,29 @@ rm -rf runtime dmg *.app *.dmg
|
|||||||
# set variables
|
# set variables
|
||||||
APP_NAME="Cryptomator"
|
APP_NAME="Cryptomator"
|
||||||
VENDOR="Skymatic GmbH"
|
VENDOR="Skymatic GmbH"
|
||||||
COPYRIGHT_YEARS="2016 - 2025"
|
COPYRIGHT_YEARS="2016 - 2026"
|
||||||
PACKAGE_IDENTIFIER="org.cryptomator"
|
PACKAGE_IDENTIFIER="org.cryptomator"
|
||||||
MAIN_JAR_GLOB="cryptomator-*.jar"
|
MAIN_JAR_GLOB="cryptomator-*.jar"
|
||||||
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
|
||||||
REVISION_NO=`git rev-list --count HEAD`
|
REVISION_NO=`git rev-list --count HEAD`
|
||||||
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
VERSION_NO=`../../../mvnw -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
||||||
FUSE_LIB="FUSE-T"
|
FUSE_LIB="FUSE-T"
|
||||||
|
|
||||||
JAVAFX_VERSION=25.0.2
|
JAVAFX_VERSION=25.0.3
|
||||||
JAVAFX_ARCH="undefined"
|
JAVAFX_ARCH="undefined"
|
||||||
JAVAFX_JMODS_SHA256="undefined"
|
JAVAFX_JMODS_SHA256="undefined"
|
||||||
if [ "$(machine)" = "arm64e" ]; then
|
if [ "$(machine)" = "arm64e" ]; then
|
||||||
JAVAFX_ARCH="aarch64"
|
JAVAFX_ARCH="aarch64"
|
||||||
JAVAFX_JMODS_SHA256="4cd258001c75af7047005c5c891e2400ed11d24fbb09412324c0cbaf8b503c5a"
|
JAVAFX_JMODS_SHA256="a52014d625b8b04e57fd71650f881c1397542b4018e1b04f1b4e66c8800a1f34"
|
||||||
else
|
else
|
||||||
JAVAFX_ARCH="x64"
|
JAVAFX_ARCH="x64"
|
||||||
JAVAFX_JMODS_SHA256="0b4d8463f03901b7425d94628e4116b7078abb8dd540fbec415266fac20bda5c"
|
JAVAFX_JMODS_SHA256="3512fabe43aee467538d329cfbbaab3c53dff2a810f0d54e381f461d5e0fac43"
|
||||||
fi
|
fi
|
||||||
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
|
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
|
||||||
|
|
||||||
# check preconditions
|
# check preconditions
|
||||||
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
||||||
command -v mvn >/dev/null 2>&1 || { echo >&2 "mvn not found. Fix by 'brew install maven'."; exit 1; }
|
[ -x ../../../mvnw ] || { echo >&2 "mvnw not found at ../../../mvnw."; exit 1; }
|
||||||
command -v create-dmg >/dev/null 2>&1 || { echo >&2 "create-dmg not found. Fix by 'brew install create-dmg'."; exit 1; }
|
command -v create-dmg >/dev/null 2>&1 || { echo >&2 "create-dmg not found. Fix by 'brew install create-dmg'."; exit 1; }
|
||||||
if [ -n "${CODESIGN_IDENTITY}" ]; then
|
if [ -n "${CODESIGN_IDENTITY}" ]; then
|
||||||
command -v codesign >/dev/null 2>&1 || { echo >&2 "codesign not found. Fix by 'xcode-select --install'."; exit 1; }
|
command -v codesign >/dev/null 2>&1 || { echo >&2 "codesign not found. Fix by 'xcode-select --install'."; exit 1; }
|
||||||
@@ -61,7 +61,7 @@ unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javaf
|
|||||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||||
JMOD_VERSION=${JMOD_VERSION#*@}
|
JMOD_VERSION=${JMOD_VERSION#*@}
|
||||||
JMOD_VERSION=${JMOD_VERSION%%.*}
|
JMOD_VERSION=${JMOD_VERSION%%.*}
|
||||||
POM_JFX_VERSION=$(mvn -f../../../pom.xml help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
POM_JFX_VERSION=$(../../../mvnw -f../../../pom.xml help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||||
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# compile
|
# compile
|
||||||
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
|
../../../mvnw -B -f../../../pom.xml clean package -DskipTests -Pmac
|
||||||
cp ../../../LICENSE.txt ../../../target
|
cp ../../../LICENSE.txt ../../../target
|
||||||
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
||||||
|
|
||||||
@@ -111,6 +111,7 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
--java-options "-Xmx256m" \
|
--java-options "-Xmx256m" \
|
||||||
--java-options "-Dfile.encoding=\"utf-8\"" \
|
--java-options "-Dfile.encoding=\"utf-8\"" \
|
||||||
--java-options "-Djava.net.useSystemProxies=true" \
|
--java-options "-Djava.net.useSystemProxies=true" \
|
||||||
|
--java-options "-Dapple.awt.enableTemplateImages=true" \
|
||||||
--java-options "-Dsun.java2d.metal=true" \
|
--java-options "-Dsun.java2d.metal=true" \
|
||||||
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
|
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
|
||||||
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\"" \
|
--java-options "-Dcryptomator.adminConfigPath=\"/Library/Application Support/Cryptomator/config.properties\"" \
|
||||||
@@ -136,7 +137,7 @@ sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Inf
|
|||||||
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
|
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
|
||||||
|
|
||||||
# generate license
|
# generate license
|
||||||
mvn -B -f../../../pom.xml license:add-third-party \
|
../../../mvnw -B -f../../../pom.xml license:add-third-party \
|
||||||
-Dlicense.thirdPartyFilename=license.rtf \
|
-Dlicense.thirdPartyFilename=license.rtf \
|
||||||
-Dlicense.outputDirectory=dist/mac/dmg/resources \
|
-Dlicense.outputDirectory=dist/mac/dmg/resources \
|
||||||
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \
|
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \
|
||||||
|
|||||||
Vendored
+14
@@ -46,6 +46,20 @@
|
|||||||
<string>Any</string>
|
<string>Any</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<!-- register org.cryptomator:// URL scheme -->
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>org.cryptomator.deeplink</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>org.cryptomator</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
<!-- register .cryptomator extension -->
|
<!-- register .cryptomator extension -->
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ installer
|
|||||||
*.jmod
|
*.jmod
|
||||||
resources/jfxJmods.zip
|
resources/jfxJmods.zip
|
||||||
license.rtf
|
license.rtf
|
||||||
|
**/FAvaultFile.properties
|
||||||
Vendored
+90
-66
@@ -16,6 +16,19 @@ Param(
|
|||||||
# Function Definitions Section
|
# Function Definitions Section
|
||||||
# ============================
|
# ============================
|
||||||
|
|
||||||
|
function Invoke-CommandWithExitCheck {
|
||||||
|
param (
|
||||||
|
[string]$Command,
|
||||||
|
[string[]]$Arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
& $Command @Arguments
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "Command '$Command' failed with exit code $LASTEXITCODE"
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Main {
|
function Main {
|
||||||
|
|
||||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
@@ -65,7 +78,8 @@ Write-Host "`$Env:JAVA_HOME=$Env:JAVA_HOME"
|
|||||||
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
|
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
|
||||||
|
|
||||||
# compile
|
# compile
|
||||||
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
|
Invoke-CommandWithExitCheck -Command `
|
||||||
|
"mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "clean", "package", "-DskipTests", "-Pwin")
|
||||||
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
|
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
|
||||||
|
|
||||||
# add runtime
|
# add runtime
|
||||||
@@ -93,9 +107,9 @@ switch ($archName) {
|
|||||||
$jmodPaths = "$Env:JAVA_HOME/jmods"
|
$jmodPaths = "$Env:JAVA_HOME/jmods"
|
||||||
}
|
}
|
||||||
'x64' {
|
'x64' {
|
||||||
$javaFxVersion='25.0.2'
|
$javaFxVersion='25.0.3'
|
||||||
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
|
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
|
||||||
$javaFxJmodsSHA256 = '33d878dfac85590c4d77c518ed413e512d34a8479d90132b230a7ddd173576b3'
|
$javaFxJmodsSHA256 = '0bf9b83260b85607a9ba200124debabd9cdb013cbc0d659e62a20192a7137907'
|
||||||
$javaFxJmods = '.\resources\jfxJmods.zip'
|
$javaFxJmods = '.\resources\jfxJmods.zip'
|
||||||
|
|
||||||
if( !(Test-Path -Path $javaFxJmods) ) {
|
if( !(Test-Path -Path $javaFxJmods) ) {
|
||||||
@@ -129,16 +143,18 @@ if ((& "$Env:JAVA_HOME\bin\jlink" --help | Select-String -Pattern "Linking from
|
|||||||
}
|
}
|
||||||
|
|
||||||
### create runtime
|
### create runtime
|
||||||
& "$Env:JAVA_HOME\bin\jlink" `
|
Invoke-CommandWithExitCheck -Command `
|
||||||
--verbose `
|
"$Env:JAVA_HOME\bin\jlink" -Arguments @(
|
||||||
--output runtime `
|
"--verbose",
|
||||||
--module-path $jmodPaths `
|
"--output", "runtime",
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
|
"--module-path", $jmodPaths,
|
||||||
--strip-native-commands `
|
"--add-modules", "java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml",
|
||||||
--no-header-files `
|
"--strip-native-commands",
|
||||||
--no-man-pages `
|
"--no-header-files",
|
||||||
--strip-debug `
|
"--no-man-pages",
|
||||||
--compress "zip-0" #do not compress and use msi compression
|
"--strip-debug",
|
||||||
|
"--compress", "zip-0" #do not compress and use msi compression
|
||||||
|
)
|
||||||
|
|
||||||
$appPath = ".\$AppName"
|
$appPath = ".\$AppName"
|
||||||
if ($clean -and (Test-Path -Path $appPath)) {
|
if ($clean -and (Test-Path -Path $appPath)) {
|
||||||
@@ -195,14 +211,15 @@ if ($LASTEXITCODE -ne 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#Create RTF license for msi
|
#Create RTF license for msi
|
||||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
Invoke-CommandWithExitCheck -Command `
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
"mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "license:add-third-party", `
|
||||||
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
|
"-Dlicense.thirdPartyFilename=license.rtf", `
|
||||||
"-Dlicense.outputDirectory=$buildDir\resources\" `
|
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl", `
|
||||||
"-Dlicense.includedScopes=compile" `
|
"-Dlicense.outputDirectory=$buildDir\resources\", `
|
||||||
"-Dlicense.excludedGroups=^org\.cryptomator" `
|
"-Dlicense.includedScopes=compile", `
|
||||||
"-Dlicense.failOnMissing=true" `
|
"-Dlicense.excludedGroups=^org\.cryptomator", `
|
||||||
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
"-Dlicense.failOnMissing=true", `
|
||||||
|
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges")
|
||||||
|
|
||||||
# patch app dir
|
# patch app dir
|
||||||
Copy-Item "contrib\*" -Destination "$AppName"
|
Copy-Item "contrib\*" -Destination "$AppName"
|
||||||
@@ -210,42 +227,46 @@ attrib -r "$AppName\$AppName.exe"
|
|||||||
attrib -r "$AppName\${AppName} (Debug).exe"
|
attrib -r "$AppName\${AppName} (Debug).exe"
|
||||||
|
|
||||||
# create .msi
|
# create .msi
|
||||||
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
|
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources\"
|
||||||
$Env:JP_WIXHELPER_DIR = "."
|
$Env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT = "${Env:JP_WIXWIZARD_RESOURCES}".Replace('\', '\\');
|
||||||
& "$Env:JAVA_HOME\bin\jpackage" `
|
$Env:JP_WIXHELPER_DIR = ""
|
||||||
--verbose `
|
|
||||||
--type msi `
|
|
||||||
--win-upgrade-uuid $UpgradeUUID `
|
|
||||||
--app-image $AppName `
|
|
||||||
--dest installer `
|
|
||||||
--name $AppName `
|
|
||||||
--vendor $Vendor `
|
|
||||||
--copyright $copyright `
|
|
||||||
--app-version "$semVerNo.$revisionNo" `
|
|
||||||
--win-menu `
|
|
||||||
--win-dir-chooser `
|
|
||||||
--win-shortcut-prompt `
|
|
||||||
--win-menu-group $AppName `
|
|
||||||
--resource-dir resources `
|
|
||||||
--license-file resources/license.rtf `
|
|
||||||
--win-update-url $UpdateUrl `
|
|
||||||
--about-url $AboutUrl `
|
|
||||||
--file-associations resources/FAvaultFile.properties
|
|
||||||
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
Get-Content .\resources\FAvaultFile.template.properties ` # Similar to envsubst
|
||||||
Write-Error "jpackage MSI failed with exit code $LASTEXITCODE"
|
| ForEach-Object { $ExecutionContext.InvokeCommand.ExpandString($_) } `
|
||||||
return 1;
|
| Out-File -FilePath .\resources\FAvaultFile.properties
|
||||||
}
|
|
||||||
|
Invoke-CommandWithExitCheck -Command `
|
||||||
|
"$Env:JAVA_HOME\bin\jpackage" -Arguments @(
|
||||||
|
"--verbose",
|
||||||
|
"--type", "msi",
|
||||||
|
"--win-upgrade-uuid", $UpgradeUUID,
|
||||||
|
"--app-image", $AppName,
|
||||||
|
"--dest", "installer",
|
||||||
|
"--name", $AppName,
|
||||||
|
"--vendor", $Vendor,
|
||||||
|
"--copyright", $copyright,
|
||||||
|
"--app-version", "$semVerNo.$revisionNo",
|
||||||
|
"--win-menu",
|
||||||
|
"--win-dir-chooser",
|
||||||
|
"--win-shortcut-prompt",
|
||||||
|
"--win-menu-group", $AppName,
|
||||||
|
"--resource-dir", "resources",
|
||||||
|
"--license-file", "resources/license.rtf",
|
||||||
|
"--win-update-url", $UpdateUrl,
|
||||||
|
"--about-url", $AboutUrl,
|
||||||
|
"--file-associations", "resources/FAvaultFile.properties"
|
||||||
|
)
|
||||||
|
|
||||||
#Create RTF license for bundle
|
#Create RTF license for bundle
|
||||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
Invoke-CommandWithExitCheck -Command `
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
"mvn" -Arguments @("-B", "-f", "$buildDir/../../pom.xml", "license:add-third-party", `
|
||||||
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
|
"-Dlicense.thirdPartyFilename=license.rtf", `
|
||||||
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
|
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl", `
|
||||||
"-Dlicense.includedScopes=compile" `
|
"-Dlicense.outputDirectory=$buildDir\bundle\resources\", `
|
||||||
"-Dlicense.excludedGroups=^org\.cryptomator" `
|
"-Dlicense.includedScopes=compile", `
|
||||||
"-Dlicense.failOnMissing=true" `
|
"-Dlicense.excludedGroups=^org\.cryptomator", `
|
||||||
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
|
"-Dlicense.failOnMissing=true", `
|
||||||
|
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges")
|
||||||
|
|
||||||
# download Winfsp
|
# download Winfsp
|
||||||
$winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
$winfspMsiUrl= 'https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi'
|
||||||
@@ -271,18 +292,21 @@ Invoke-WebRequest $winfspUninstaller -OutFile ".\bundle\resources\winfsp-uninsta
|
|||||||
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi" -Force
|
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi" -Force
|
||||||
|
|
||||||
# create bundle including winfsp
|
# create bundle including winfsp
|
||||||
& wix build `
|
Invoke-CommandWithExitCheck -Command `
|
||||||
-define BundleName="$AppName" `
|
"wix" -Arguments @(
|
||||||
-define BundleVersion="$semVerNo.$revisionNo" `
|
"build",
|
||||||
-define BundleVendor="$Vendor" `
|
"-define", "BundleName=$AppName",
|
||||||
-define BundleCopyright="$copyright" `
|
"-define", "BundleVersion=$semVerNo.$revisionNo",
|
||||||
-define AboutUrl="$AboutUrl" `
|
"-define", "BundleVendor=$Vendor",
|
||||||
-define HelpUrl="$HelpUrl" `
|
"-define", "BundleCopyright=$copyright",
|
||||||
-define UpdateUrl="$UpdateUrl" `
|
"-define", "AboutUrl=$AboutUrl",
|
||||||
-ext "WixToolset.Util.wixext" `
|
"-define", "HelpUrl=$HelpUrl",
|
||||||
-ext "WixToolset.BootstrapperApplications.wixext" `
|
"-define", "UpdateUrl=$UpdateUrl",
|
||||||
.\bundle\bundleWithWinfsp.wxs `
|
"-ext", "WixToolset.Util.wixext",
|
||||||
-out "installer\$AppName-Installer.exe"
|
"-ext", "WixToolset.BootstrapperApplications.wixext",
|
||||||
|
".\bundle\bundleWithWinfsp.wxs",
|
||||||
|
"-out", ".\installer\$AppName-Installer.exe"
|
||||||
|
)
|
||||||
|
|
||||||
Write-Host "Created EXE installer .\installer\$AppName-Installer.exe"
|
Write-Host "Created EXE installer .\installer\$AppName-Installer.exe"
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Vendored
+4
-1
@@ -4,15 +4,18 @@
|
|||||||
:: This file must be located in the INSTALLDIR
|
:: This file must be located in the INSTALLDIR
|
||||||
|
|
||||||
set "LOOPBACK_ALIAS=%1"
|
set "LOOPBACK_ALIAS=%1"
|
||||||
|
set "ACTION=%2"
|
||||||
|
if "%ACTION%"=="" set "ACTION=install"
|
||||||
|
|
||||||
:: Log for debugging
|
:: Log for debugging
|
||||||
echo LOOPBACK_ALIAS=%LOOPBACK_ALIAS%
|
echo LOOPBACK_ALIAS=%LOOPBACK_ALIAS%
|
||||||
|
echo ACTION=%ACTION%
|
||||||
|
|
||||||
:: Change to INSTALLDIR
|
:: Change to INSTALLDIR
|
||||||
cd %~dp0
|
cd %~dp0
|
||||||
:: Execute the PowerShell script
|
:: Execute the PowerShell script
|
||||||
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File .\patchWebDAV.ps1^
|
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -File .\patchWebDAV.ps1^
|
||||||
-LoopbackAlias %LOOPBACK_ALIAS%
|
-LoopbackAlias %LOOPBACK_ALIAS% -Action %ACTION%
|
||||||
|
|
||||||
:: Return the exit code from PowerShell
|
:: Return the exit code from PowerShell
|
||||||
exit /b %ERRORLEVEL%
|
exit /b %ERRORLEVEL%
|
||||||
Vendored
+36
-11
@@ -1,15 +1,17 @@
|
|||||||
#Requires -RunAsAdministrator
|
#Requires -RunAsAdministrator
|
||||||
Param(
|
Param(
|
||||||
[Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias
|
[Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias,
|
||||||
|
[string] $Action = "install"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
New-Variable -Name "sysdir" -Value ([Environment]::SystemDirectory) -Option Constant -Scope Global
|
||||||
|
New-Variable -Name "hostsFile" -Value "$sysdir\drivers\etc\hosts" -Option Constant -Scope Global
|
||||||
|
|
||||||
# Adds an alias for 127.0.0.1 to the hosts file
|
# Adds an alias for 127.0.0.1 to the hosts file
|
||||||
function Add-AliasToHost {
|
function Add-AliasToHost {
|
||||||
param (
|
param (
|
||||||
[string]$LoopbackAlias
|
[string]$LoopbackAlias
|
||||||
)
|
)
|
||||||
$sysdir = [Environment]::SystemDirectory
|
|
||||||
$hostsFile = "$sysdir\drivers\etc\hosts"
|
|
||||||
$aliasLine = "127.0.0.1 $LoopbackAlias"
|
$aliasLine = "127.0.0.1 $LoopbackAlias"
|
||||||
|
|
||||||
foreach ($line in Get-Content $hostsFile) {
|
foreach ($line in Get-Content $hostsFile) {
|
||||||
@@ -18,9 +20,26 @@ function Add-AliasToHost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Add-Content -Path $hostsFile -Encoding ascii -Value "`r`n$aliasLine"
|
$content = Get-Content $hostsFile
|
||||||
|
$content += "`r`n$aliasLine"
|
||||||
|
|
||||||
|
$content | Set-Content "$hostsFile.tmp" -Encoding ascii
|
||||||
|
Move-Item "$hostsFile.tmp" $hostsFile -Force
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Removes an alias for 127.0.0.1 from the hosts file
|
||||||
|
function Remove-AliasFromHost {
|
||||||
|
param (
|
||||||
|
[string]$LoopbackAlias
|
||||||
|
)
|
||||||
|
$aliasLine = "127.0.0.1 $LoopbackAlias"
|
||||||
|
|
||||||
|
$content = Get-Content $hostsFile
|
||||||
|
$newContent = $content | Where-Object { $_ -ne $aliasLine }
|
||||||
|
|
||||||
|
$newContent | Set-Content "$hostsFile.tmp" -Encoding ascii
|
||||||
|
Move-Item "$hostsFile.tmp" $hostsFile -Force
|
||||||
|
}
|
||||||
|
|
||||||
# Sets in the registry the webclient file size limit to the maximum value
|
# Sets in the registry the webclient file size limit to the maximum value
|
||||||
function Set-WebDAVFileSizeLimit {
|
function Set-WebDAVFileSizeLimit {
|
||||||
@@ -54,14 +73,20 @@ function Edit-ProviderOrder {
|
|||||||
New-ItemProperty -Path $RegistryPath -Name $Name -Value $UpdatedOrder -PropertyType String -Force | Out-Null
|
New-ItemProperty -Path $RegistryPath -Name $Name -Value $UpdatedOrder -PropertyType String -Force | Out-Null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($Action -eq "install") {
|
||||||
|
Add-AliasToHost $LoopbackAlias
|
||||||
|
Write-Output 'Ensured alias exists in hosts file'
|
||||||
|
|
||||||
Add-AliasToHost $LoopbackAlias
|
Set-WebDAVFileSizeLimit
|
||||||
Write-Output 'Ensured alias exists in hosts file'
|
Write-Output 'Set WebDAV file size limit'
|
||||||
|
|
||||||
Set-WebDAVFileSizeLimit
|
Edit-ProviderOrder
|
||||||
Write-Output 'Set WebDAV file size limit'
|
Write-Output 'Ensured correct provider order'
|
||||||
|
} elseif ($Action -eq "uninstall") {
|
||||||
Edit-ProviderOrder
|
Remove-AliasFromHost $LoopbackAlias
|
||||||
Write-Output 'Ensured correct provider order'
|
Write-Output 'Ensured alias removed from hosts file'
|
||||||
|
} else {
|
||||||
|
Write-Error "Invalid action: $Action. Only 'install' or 'uninstall' are valid."
|
||||||
|
}
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
mime-type=application/vnd.cryptomator.vault
|
mime-type=application/vnd.cryptomator.vault
|
||||||
extension=cryptomator
|
extension=cryptomator
|
||||||
description=Cryptomator Vault File
|
description=Cryptomator Vault File
|
||||||
icon=resources/Cryptomator-Vault.ico
|
icon=${env:JP_WIXWIZARD_RESOURCES_PROPERTIES_FORMAT}Cryptomator-Vault.ico
|
||||||
Vendored
+25
-7
@@ -27,6 +27,7 @@
|
|||||||
<?define ProgIdContentType= "application/vnd.cryptomator.encrypted" ?>
|
<?define ProgIdContentType= "application/vnd.cryptomator.encrypted" ?>
|
||||||
<?define CloseApplicationTarget= "cryptomator.exe" ?>
|
<?define CloseApplicationTarget= "cryptomator.exe" ?>
|
||||||
<?define LoopbackAlias= "cryptomator-vault" ?>
|
<?define LoopbackAlias= "cryptomator-vault" ?>
|
||||||
|
<?define UrlProtocolScheme= "org.cryptomator" ?>
|
||||||
|
|
||||||
<?include $(var.JpConfigDir)/overrides.wxi ?>
|
<?include $(var.JpConfigDir)/overrides.wxi ?>
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@
|
|||||||
<?endif?>
|
<?endif?>
|
||||||
|
|
||||||
<!-- TODO: how does this work again? -->
|
<!-- TODO: how does this work again? -->
|
||||||
<ns0:Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)\wixhelper.dll" />
|
<ns0:Binary Id="JpCaDll" SourceFile="$(env.JP_WIXHELPER_DIR)msica.dll"></ns0:Binary>
|
||||||
<ns0:CustomAction Id="JpFindRelatedProducts" BinaryRef="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
<ns0:CustomAction Id="JpFindRelatedProducts" BinaryRef="JpCaDll" DllEntry="FindRelatedProductsEx" />
|
||||||
|
|
||||||
<?ifndef SkipCryptomatorLegacyCheck ?>
|
<?ifndef SkipCryptomatorLegacyCheck ?>
|
||||||
@@ -97,6 +98,19 @@
|
|||||||
<ns0:Extension Id="c9u" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
|
<ns0:Extension Id="c9u" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
|
||||||
</ns0:ProgId>
|
</ns0:ProgId>
|
||||||
</ns0:Component>
|
</ns0:Component>
|
||||||
|
<!-- Register "org.cryptomator://" URL protocol handler -->
|
||||||
|
<ns0:Component Bitness="always64" Id="UrlProtocolHandler" Guid="*">
|
||||||
|
<ns0:RegistryKey Root="HKMU" Key="Software\Classes\$(var.UrlProtocolScheme)">
|
||||||
|
<ns0:RegistryValue Type="string" Value="URL:$(var.JpAppName) Protocol" KeyPath="yes"/>
|
||||||
|
<ns0:RegistryValue Name="URL Protocol" Type="string" Value=""/>
|
||||||
|
<ns0:RegistryKey Key="DefaultIcon">
|
||||||
|
<ns0:RegistryValue Type="string" Value="[INSTALLDIR]$(var.JpAppName).exe,0"/>
|
||||||
|
</ns0:RegistryKey>
|
||||||
|
<ns0:RegistryKey Key="shell\open\command">
|
||||||
|
<ns0:RegistryValue Type="string" Value=""[INSTALLDIR]$(var.JpAppName).exe" "%1""/>
|
||||||
|
</ns0:RegistryKey>
|
||||||
|
</ns0:RegistryKey>
|
||||||
|
</ns0:Component>
|
||||||
</ns0:DirectoryRef>
|
</ns0:DirectoryRef>
|
||||||
|
|
||||||
<ns0:StandardDirectory Id="CommonAppDataFolder">
|
<ns0:StandardDirectory Id="CommonAppDataFolder">
|
||||||
@@ -126,6 +140,7 @@
|
|||||||
<ns0:ComponentGroupRef Id="FileAssociations"/>
|
<ns0:ComponentGroupRef Id="FileAssociations"/>
|
||||||
<!-- Ref to additional ProgIDs -->
|
<!-- Ref to additional ProgIDs -->
|
||||||
<ns0:ComponentRef Id="nonStartingProgID"/>
|
<ns0:ComponentRef Id="nonStartingProgID"/>
|
||||||
|
<ns0:ComponentRef Id="UrlProtocolHandler"/>
|
||||||
<ns0:ComponentRef Id="AdminConfigDir"/>
|
<ns0:ComponentRef Id="AdminConfigDir"/>
|
||||||
<ns0:ComponentRef Id="AdminConfigFile"/>
|
<ns0:ComponentRef Id="AdminConfigFile"/>
|
||||||
</ns0:Feature>
|
</ns0:Feature>
|
||||||
@@ -158,9 +173,13 @@
|
|||||||
<ns0:CustomAction Id="DisableUserConfig" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
<ns0:CustomAction Id="DisableUserConfig" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||||
|
|
||||||
<!-- WebDAV patches -->
|
<!-- WebDAV patches -->
|
||||||
<ns0:SetProperty Id="PatchWebDAV" Value=""[INSTALLDIR]patchWebDAV.bat" "$(var.LoopbackAlias)"" Sequence="execute" Before="PatchWebDAV" />
|
<ns0:SetProperty Id="PatchWebDAV" Value=""[INSTALLDIR]patchWebDAV.bat" "$(var.LoopbackAlias)" install" Sequence="execute" Before="PatchWebDAV" />
|
||||||
<ns0:CustomAction Id="PatchWebDAV" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
<ns0:CustomAction Id="PatchWebDAV" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||||
|
|
||||||
|
<!-- WebDAV patches (Uninstall) -->
|
||||||
|
<ns0:SetProperty Id="PatchWebDAVUninstall" Value=""[INSTALLDIR]patchWebDAV.bat" "$(var.LoopbackAlias)" uninstall" Sequence="execute" Before="PatchWebDAVUninstall" />
|
||||||
|
<ns0:CustomAction Id="PatchWebDAVUninstall" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec" Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||||
|
|
||||||
<!-- Update check configuration -->
|
<!-- Update check configuration -->
|
||||||
<ns0:SetProperty Id="PatchUpdateCheck" Value=""[INSTALLDIR]patchUpdateCheck.bat" "[DISABLEUPDATECHECK]"" Sequence="execute" Before="PatchUpdateCheck" />
|
<ns0:SetProperty Id="PatchUpdateCheck" Value=""[INSTALLDIR]patchUpdateCheck.bat" "[DISABLEUPDATECHECK]"" Sequence="execute" Before="PatchUpdateCheck" />
|
||||||
<ns0:CustomAction Id="PatchUpdateCheck" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
|
<ns0:CustomAction Id="PatchUpdateCheck" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||||
@@ -214,9 +233,8 @@
|
|||||||
|
|
||||||
<ns0:RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to Wix4CloseApplications_* -->
|
<ns0:RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to Wix4CloseApplications_* -->
|
||||||
<ns0:Custom Action="DisableUserConfig" After="InstallFiles" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
<ns0:Custom Action="DisableUserConfig" After="InstallFiles" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||||
<!-- Skip action on uninstall -->
|
|
||||||
<!-- TODO: don't skip action, but remove cryptomator alias from hosts file -->
|
|
||||||
<ns0:Custom Action="PatchWebDAV" After="DisableUserConfig" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
<ns0:Custom Action="PatchWebDAV" After="DisableUserConfig" Condition="NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||||
|
<ns0:Custom Action="PatchWebDAVUninstall" Before="RemoveFiles" Condition="Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE" />
|
||||||
<!-- Configure update check setting if property is provided -->
|
<!-- Configure update check setting if property is provided -->
|
||||||
<ns0:Custom Action="PatchUpdateCheck" After="PatchWebDAV" Condition="DISABLEUPDATECHECK AND NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
<ns0:Custom Action="PatchUpdateCheck" After="PatchWebDAV" Condition="DISABLEUPDATECHECK AND NOT (Installed AND (NOT REINSTALL) AND (NOT UPGRADINGPRODUCTCODE) AND REMOVE)"/>
|
||||||
</ns0:InstallExecuteSequence>
|
</ns0:InstallExecuteSequence>
|
||||||
@@ -225,7 +243,7 @@
|
|||||||
<ns0:Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
<ns0:Custom Action="JpFindRelatedProducts" After="FindRelatedProducts"/>
|
||||||
</ns0:InstallUISequence>
|
</ns0:InstallUISequence>
|
||||||
|
|
||||||
<ns0:WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\banner.bmp" />
|
<ns0:WixVariable Id="WixUIBannerBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)banner.bmp" />
|
||||||
<ns0:WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)\background.bmp" />
|
<ns0:WixVariable Id="WixUIDialogBmp" Value="$(env.JP_WIXWIZARD_RESOURCES)background.bmp" />
|
||||||
</ns0:Package>
|
</ns0:Package>
|
||||||
</ns0:Wix>
|
</ns0:Wix>
|
||||||
@@ -0,0 +1,295 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Apache Maven Wrapper startup batch script, version 3.3.4
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||||
|
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
|
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
|
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
set -euf
|
||||||
|
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||||
|
|
||||||
|
# OS specific support.
|
||||||
|
native_path() { printf %s\\n "$1"; }
|
||||||
|
case "$(uname)" in
|
||||||
|
CYGWIN* | MINGW*)
|
||||||
|
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||||
|
native_path() { cygpath --path --windows "$1"; }
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# set JAVACMD and JAVACCMD
|
||||||
|
set_java_home() {
|
||||||
|
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||||
|
if [ -n "${JAVA_HOME-}" ]; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||||
|
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||||
|
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v java
|
||||||
|
)" || :
|
||||||
|
JAVACCMD="$(
|
||||||
|
'set' +e
|
||||||
|
'unset' -f command 2>/dev/null
|
||||||
|
'command' -v javac
|
||||||
|
)" || :
|
||||||
|
|
||||||
|
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||||
|
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# hash string like Java String::hashCode
|
||||||
|
hash_string() {
|
||||||
|
str="${1:-}" h=0
|
||||||
|
while [ -n "$str" ]; do
|
||||||
|
char="${str%"${str#?}"}"
|
||||||
|
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||||
|
str="${str#?}"
|
||||||
|
done
|
||||||
|
printf %x\\n $h
|
||||||
|
}
|
||||||
|
|
||||||
|
verbose() { :; }
|
||||||
|
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||||
|
|
||||||
|
die() {
|
||||||
|
printf %s\\n "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
trim() {
|
||||||
|
# MWRAPPER-139:
|
||||||
|
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||||
|
# Needed for removing poorly interpreted newline sequences when running in more
|
||||||
|
# exotic environments such as mingw bash on Windows.
|
||||||
|
printf "%s" "${1}" | tr -d '[:space:]'
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptDir="$(dirname "$0")"
|
||||||
|
scriptName="$(basename "$0")"
|
||||||
|
|
||||||
|
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
|
while IFS="=" read -r key value; do
|
||||||
|
case "${key-}" in
|
||||||
|
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||||
|
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||||
|
esac
|
||||||
|
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
|
||||||
|
case "${distributionUrl##*/}" in
|
||||||
|
maven-mvnd-*bin.*)
|
||||||
|
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||||
|
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||||
|
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||||
|
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||||
|
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||||
|
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||||
|
*)
|
||||||
|
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||||
|
distributionPlatform=linux-amd64
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||||
|
;;
|
||||||
|
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||||
|
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
|
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||||
|
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||||
|
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||||
|
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||||
|
|
||||||
|
exec_maven() {
|
||||||
|
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||||
|
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d "$MAVEN_HOME" ]; then
|
||||||
|
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
exec_maven "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${distributionUrl-}" in
|
||||||
|
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||||
|
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# prepare tmp dir
|
||||||
|
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||||
|
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||||
|
trap clean HUP INT TERM EXIT
|
||||||
|
else
|
||||||
|
die "cannot create temp dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||||
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
|
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
verbose "Downloading from: $distributionUrl"
|
||||||
|
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
|
# select .zip or .tar.gz
|
||||||
|
if ! command -v unzip >/dev/null; then
|
||||||
|
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||||
|
distributionUrlName="${distributionUrl##*/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# verbose opt
|
||||||
|
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||||
|
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||||
|
|
||||||
|
# normalize http auth
|
||||||
|
case "${MVNW_PASSWORD:+has-password}" in
|
||||||
|
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||||
|
verbose "Found wget ... using wget"
|
||||||
|
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||||
|
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||||
|
verbose "Found curl ... using curl"
|
||||||
|
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||||
|
elif set_java_home; then
|
||||||
|
verbose "Falling back to use Java to download"
|
||||||
|
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||||
|
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
cat >"$javaSource" <<-END
|
||||||
|
public class Downloader extends java.net.Authenticator
|
||||||
|
{
|
||||||
|
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||||
|
{
|
||||||
|
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||||
|
}
|
||||||
|
public static void main( String[] args ) throws Exception
|
||||||
|
{
|
||||||
|
setDefault( new Downloader() );
|
||||||
|
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END
|
||||||
|
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||||
|
verbose " - Compiling Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||||
|
verbose " - Running Downloader.java ..."
|
||||||
|
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
if [ -n "${distributionSha256Sum-}" ]; then
|
||||||
|
distributionSha256Result=false
|
||||||
|
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||||
|
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||||
|
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
|
exit 1
|
||||||
|
elif command -v sha256sum >/dev/null; then
|
||||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
|
||||||
|
distributionSha256Result=true
|
||||||
|
fi
|
||||||
|
elif command -v shasum >/dev/null; then
|
||||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||||
|
distributionSha256Result=true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||||
|
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ $distributionSha256Result = false ]; then
|
||||||
|
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||||
|
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# unzip and move
|
||||||
|
if command -v unzip >/dev/null; then
|
||||||
|
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||||
|
else
|
||||||
|
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||||
|
actualDistributionDir=""
|
||||||
|
|
||||||
|
# First try the expected directory name (for regular distributions)
|
||||||
|
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
|
||||||
|
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
|
||||||
|
actualDistributionDir="$distributionUrlNameMain"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||||
|
if [ -z "$actualDistributionDir" ]; then
|
||||||
|
# enable globbing to iterate over items
|
||||||
|
set +f
|
||||||
|
for dir in "$TMP_DOWNLOAD_DIR"/*; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
if [ -f "$dir/bin/$MVN_CMD" ]; then
|
||||||
|
actualDistributionDir="$(basename "$dir")"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
set -f
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$actualDistributionDir" ]; then
|
||||||
|
verbose "Contents of $TMP_DOWNLOAD_DIR:"
|
||||||
|
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
|
||||||
|
die "Could not find Maven distribution directory in extracted archive"
|
||||||
|
fi
|
||||||
|
|
||||||
|
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||||
|
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
|
||||||
|
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||||
|
|
||||||
|
clean || :
|
||||||
|
exec_maven "$@"
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
<# : batch portion
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Apache Maven Wrapper startup batch script, version 3.3.4
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
|
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
|
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||||
|
@SET __MVNW_CMD__=
|
||||||
|
@SET __MVNW_ERROR__=
|
||||||
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||||
|
@SET PSModulePath=
|
||||||
|
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||||
|
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||||
|
)
|
||||||
|
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||||
|
@SET __MVNW_PSMODULEP_SAVE=
|
||||||
|
@SET __MVNW_ARG0_NAME__=
|
||||||
|
@SET MVNW_USERNAME=
|
||||||
|
@SET MVNW_PASSWORD=
|
||||||
|
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
|
||||||
|
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||||
|
@GOTO :EOF
|
||||||
|
: end batch / begin powershell #>
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
if ($env:MVNW_VERBOSE -eq "true") {
|
||||||
|
$VerbosePreference = "Continue"
|
||||||
|
}
|
||||||
|
|
||||||
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
|
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||||
|
if (!$distributionUrl) {
|
||||||
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||||
|
"maven-mvnd-*" {
|
||||||
|
$USE_MVND = $true
|
||||||
|
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||||
|
$MVN_CMD = "mvnd.cmd"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
$USE_MVND = $false
|
||||||
|
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
|
if ($env:MVNW_REPOURL) {
|
||||||
|
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||||
|
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
|
||||||
|
}
|
||||||
|
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||||
|
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||||
|
|
||||||
|
$MAVEN_M2_PATH = "$HOME/.m2"
|
||||||
|
if ($env:MAVEN_USER_HOME) {
|
||||||
|
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
|
||||||
|
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$MAVEN_WRAPPER_DISTS = $null
|
||||||
|
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
|
||||||
|
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
|
||||||
|
} else {
|
||||||
|
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
|
||||||
|
}
|
||||||
|
|
||||||
|
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
|
||||||
|
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||||
|
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||||
|
|
||||||
|
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||||
|
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
|
exit $?
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||||
|
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||||
|
}
|
||||||
|
|
||||||
|
# prepare tmp dir
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||||
|
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||||
|
trap {
|
||||||
|
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||||
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
|
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
|
Write-Verbose "Downloading from: $distributionUrl"
|
||||||
|
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
|
|
||||||
|
$webclient = New-Object System.Net.WebClient
|
||||||
|
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||||
|
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||||
|
}
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||||
|
|
||||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||||
|
if ($distributionSha256Sum) {
|
||||||
|
if ($USE_MVND) {
|
||||||
|
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||||
|
}
|
||||||
|
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||||
|
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||||
|
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# unzip and move
|
||||||
|
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||||
|
|
||||||
|
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||||
|
$actualDistributionDir = ""
|
||||||
|
|
||||||
|
# First try the expected directory name (for regular distributions)
|
||||||
|
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
|
||||||
|
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
|
||||||
|
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
|
||||||
|
$actualDistributionDir = $distributionUrlNameMain
|
||||||
|
}
|
||||||
|
|
||||||
|
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||||
|
if (!$actualDistributionDir) {
|
||||||
|
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
|
||||||
|
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
|
||||||
|
if (Test-Path -Path $testPath -PathType Leaf) {
|
||||||
|
$actualDistributionDir = $_.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$actualDistributionDir) {
|
||||||
|
Write-Error "Could not find Maven distribution directory in extracted archive"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||||
|
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||||
|
try {
|
||||||
|
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||||
|
} catch {
|
||||||
|
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||||
|
Write-Error "fail to move MAVEN_HOME"
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
@@ -26,52 +26,51 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.jdk.version>25</project.jdk.version>
|
<project.jdk.version>26</project.jdk.version>
|
||||||
|
|
||||||
<!-- Group IDs of jars that need to stay on the class path for now -->
|
<!-- Group IDs of jars that need to stay on the class path for now -->
|
||||||
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
|
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
|
||||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
||||||
|
|
||||||
<!-- cryptomator dependencies -->
|
<!-- cryptomator dependencies -->
|
||||||
<cryptomator.cryptofs.version>2.10.0</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>2.11.0-SNAPSHOT</cryptomator.cryptofs.version>
|
||||||
<cryptomator.cryptolib.version>2.2.2</cryptomator.cryptolib.version>
|
<cryptomator.cryptolib.version>2.2.2</cryptomator.cryptolib.version>
|
||||||
<cryptomator.integrations.version>1.8.0</cryptomator.integrations.version>
|
<cryptomator.integrations.version>1.9.0</cryptomator.integrations.version>
|
||||||
<cryptomator.integrations.win.version>1.6.0</cryptomator.integrations.win.version>
|
<cryptomator.integrations.win.version>1.6.1</cryptomator.integrations.win.version>
|
||||||
<cryptomator.integrations.mac.version>1.5.0-SNAPSHOT</cryptomator.integrations.mac.version>
|
<cryptomator.integrations.mac.version>1.5.0</cryptomator.integrations.mac.version>
|
||||||
<cryptomator.integrations.linux.version>1.7.0</cryptomator.integrations.linux.version>
|
<cryptomator.integrations.linux.version>1.7.0</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.fuse.version>6.0.1</cryptomator.fuse.version>
|
<cryptomator.fuse.version>6.0.1</cryptomator.fuse.version>
|
||||||
<cryptomator.webdav.version>3.0.1</cryptomator.webdav.version>
|
<cryptomator.webdav.version>3.0.2</cryptomator.webdav.version>
|
||||||
<cryptomator.webdav-servlet.version>1.2.12</cryptomator.webdav-servlet.version>
|
<cryptomator.webdav-servlet.version>1.2.12</cryptomator.webdav-servlet.version>
|
||||||
|
|
||||||
<!-- 3rd party dependencies -->
|
<!-- 3rd party dependencies -->
|
||||||
<caffeine.version>3.2.3</caffeine.version>
|
<caffeine.version>3.2.4</caffeine.version>
|
||||||
<commons-lang3.version>3.20.0</commons-lang3.version>
|
<commons-lang3.version>3.20.0</commons-lang3.version>
|
||||||
<dagger.version>2.59.2</dagger.version>
|
<dagger.version>2.59.2</dagger.version>
|
||||||
<easybind.version>2.2</easybind.version>
|
<easybind.version>2.2</easybind.version>
|
||||||
<jackson.version>2.21.1</jackson.version>
|
<jackson.version>2.21.4</jackson.version>
|
||||||
<javafx.version>25.0.2</javafx.version>
|
<javafx.version>25.0.3</javafx.version>
|
||||||
<jwt.version>4.5.1</jwt.version>
|
<jwt.version>4.5.2</jwt.version>
|
||||||
<nimbus-jose.version>10.5</nimbus-jose.version>
|
<nimbus-jose.version>10.5</nimbus-jose.version>
|
||||||
<logback.version>1.5.32</logback.version>
|
<logback.version>1.5.35</logback.version>
|
||||||
<slf4j.version>2.0.17</slf4j.version>
|
<slf4j.version>2.0.18</slf4j.version>
|
||||||
<tinyoauth2.version>0.8.1</tinyoauth2.version>
|
<tinyoauth2.version>0.8.1</tinyoauth2.version>
|
||||||
<zxcvbn.version>1.9.0</zxcvbn.version>
|
<zxcvbn.version>1.9.0</zxcvbn.version>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<junit.jupiter.version>6.0.3</junit.jupiter.version>
|
<junit.jupiter.version>6.1.0</junit.jupiter.version>
|
||||||
<mockito.version>5.22.0</mockito.version>
|
<mockito.version>5.23.0</mockito.version>
|
||||||
<hamcrest.version>3.0</hamcrest.version>
|
<hamcrest.version>3.0</hamcrest.version>
|
||||||
|
|
||||||
<!-- build-time dependencies -->
|
<!-- build-time dependencies -->
|
||||||
<jetbrains.annotations.version>26.1.0</jetbrains.annotations.version>
|
<jetbrains.annotations.version>26.1.0</jetbrains.annotations.version>
|
||||||
<dependency-check.version>12.2.0</dependency-check.version>
|
<dependency-check.version>12.2.2</dependency-check.version>
|
||||||
<jacoco.version>0.8.14</jacoco.version>
|
<jacoco.version>0.8.15</jacoco.version>
|
||||||
<license-generator.version>2.7.1</license-generator.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-compiler.version>3.15.0</mvn-compiler.version>
|
||||||
<mvn-resources.version>3.5.0</mvn-resources.version>
|
<mvn-resources.version>3.5.0</mvn-resources.version>
|
||||||
<mvn-dependency.version>3.10.0</mvn-dependency.version>
|
<mvn-dependency.version>3.11.0</mvn-dependency.version>
|
||||||
<mvn-surefire.version>3.5.3</mvn-surefire.version>
|
<mvn-surefire.version>3.5.6</mvn-surefire.version>
|
||||||
<mvn-jar.version>3.5.0</mvn-jar.version>
|
<mvn-jar.version>3.5.0</mvn-jar.version>
|
||||||
|
|
||||||
<!-- Property used by surefire to determine jacoco engine -->
|
<!-- Property used by surefire to determine jacoco engine -->
|
||||||
@@ -356,22 +355,12 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>me.fabriciorby</groupId>
|
|
||||||
<artifactId>maven-surefire-junit5-tree-reporter</artifactId>
|
|
||||||
<version>${junit-tree-reporter.version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<reportFormat>plain</reportFormat>
|
<reportFormat>plain</reportFormat>
|
||||||
<consoleOutputReporter>
|
<consoleOutputReporter>
|
||||||
<disable>true</disable>
|
<disable>true</disable>
|
||||||
</consoleOutputReporter>
|
</consoleOutputReporter>
|
||||||
<argLine>@{surefire.jacoco.args} -javaagent:${org.mockito:mockito-core:jar} --enable-native-access=javafx.graphics</argLine>
|
<argLine>@{surefire.jacoco.args} -javaagent:${org.mockito:mockito-core:jar} --enable-native-access=javafx.graphics</argLine>
|
||||||
<statelessTestsetInfoReporter
|
|
||||||
implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5StatelessTestsetInfoTreeReporter">
|
|
||||||
</statelessTestsetInfoReporter>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -513,9 +502,6 @@
|
|||||||
<os>
|
<os>
|
||||||
<family>mac</family>
|
<family>mac</family>
|
||||||
</os>
|
</os>
|
||||||
<property>
|
|
||||||
<name>idea.version</name>
|
|
||||||
</property>
|
|
||||||
</activation>
|
</activation>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -531,12 +517,9 @@
|
|||||||
<activation>
|
<activation>
|
||||||
<os>
|
<os>
|
||||||
<family>unix</family>
|
<family>unix</family>
|
||||||
<name>Linux</name>
|
<name>linux</name>
|
||||||
<arch>aarch64</arch>
|
<arch>aarch64</arch>
|
||||||
</os>
|
</os>
|
||||||
<property>
|
|
||||||
<name>idea.version</name>
|
|
||||||
</property>
|
|
||||||
</activation>
|
</activation>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -576,12 +559,9 @@
|
|||||||
<activation>
|
<activation>
|
||||||
<os>
|
<os>
|
||||||
<family>unix</family>
|
<family>unix</family>
|
||||||
<name>Linux</name>
|
<name>linux</name>
|
||||||
<arch>amd64</arch>
|
<arch>amd64</arch>
|
||||||
</os>
|
</os>
|
||||||
<property>
|
|
||||||
<name>idea.version</name>
|
|
||||||
</property>
|
|
||||||
</activation>
|
</activation>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -598,9 +578,6 @@
|
|||||||
<os>
|
<os>
|
||||||
<family>windows</family>
|
<family>windows</family>
|
||||||
</os>
|
</os>
|
||||||
<property>
|
|
||||||
<name>idea.version</name>
|
|
||||||
</property>
|
|
||||||
</activation>
|
</activation>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class NotAVaultDirectoryException extends NoSuchFileException {
|
||||||
|
|
||||||
|
public enum Reason {
|
||||||
|
MISSING_DATA_DIR,
|
||||||
|
DATA_NOT_A_DIRECTORY,
|
||||||
|
MISSING_VAULT_CONFIG,
|
||||||
|
VAULT_CONFIG_ACCESS_DENIED,
|
||||||
|
UNSUPPORTED_STRUCTURE
|
||||||
|
}
|
||||||
|
|
||||||
|
private final transient Path path;
|
||||||
|
private final Reason reason;
|
||||||
|
|
||||||
|
public NotAVaultDirectoryException(Path path, Reason reason) {
|
||||||
|
super(path.toString(), null, "Not a vault directory: " + reason);
|
||||||
|
this.path = path;
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path path() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reason notAVaultReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,20 +22,31 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.file.AccessDeniedException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.NoSuchFileException;
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||||
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
|
import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME;
|
||||||
import static org.cryptomator.common.vaults.VaultState.Value.*;
|
import static org.cryptomator.common.vaults.VaultState.Value.ALL_MISSING;
|
||||||
|
import static org.cryptomator.common.vaults.VaultState.Value.ERROR;
|
||||||
|
import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
|
||||||
|
import static org.cryptomator.common.vaults.VaultState.Value.MISSING;
|
||||||
|
import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION;
|
||||||
|
import static org.cryptomator.common.vaults.VaultState.Value.PROCESSING;
|
||||||
|
import static org.cryptomator.common.vaults.VaultState.Value.UNLOCKED;
|
||||||
|
import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING;
|
||||||
|
import static org.cryptomator.cryptofs.common.Constants.DATA_DIR_NAME;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class VaultListManager {
|
public class VaultListManager {
|
||||||
@@ -72,18 +83,57 @@ public class VaultListManager {
|
|||||||
return vaultList.stream().anyMatch(v -> vaultPath.equals(v.getPath()));
|
return vaultList.stream().anyMatch(v -> vaultPath.equals(v.getPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safe to call from any thread: the IO work runs on the calling thread, but the
|
||||||
|
* {@code ObservableList} mutation is marshaled to the JavaFX application thread.
|
||||||
|
*/
|
||||||
public Vault add(Path pathToVault) throws IOException {
|
public Vault add(Path pathToVault) throws IOException {
|
||||||
Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath();
|
Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath();
|
||||||
if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) {
|
assertIsVaultDirectory(normalizedPathToVault);
|
||||||
throw new NoSuchFileException(normalizedPathToVault.toString(), null, "Not a vault directory");
|
|
||||||
}
|
|
||||||
|
|
||||||
return get(normalizedPathToVault) //
|
return get(normalizedPathToVault).orElseGet(() -> {
|
||||||
.orElseGet(() -> {
|
Vault newVault = create(newVaultSettings(normalizedPathToVault));
|
||||||
Vault newVault = create(newVaultSettings(normalizedPathToVault));
|
if (Platform.isFxApplicationThread()) {
|
||||||
vaultList.add(newVault);
|
addVault(newVault);
|
||||||
return newVault;
|
} else {
|
||||||
});
|
Platform.runLater(() -> addVault(newVault));
|
||||||
|
}
|
||||||
|
return newVault;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertIsVaultDirectory(Path pathToVault) throws IOException {
|
||||||
|
if (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) {
|
||||||
|
checkDataDir(pathToVault);
|
||||||
|
checkConfigFile(pathToVault);
|
||||||
|
//if vault is legacy _and_ not readable, just say unsupported
|
||||||
|
throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.UNSUPPORTED_STRUCTURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkDataDir(Path pathToVault) throws NotAVaultDirectoryException {
|
||||||
|
Path dataDir = pathToVault.resolve(DATA_DIR_NAME);
|
||||||
|
if (!Files.exists(dataDir)) {
|
||||||
|
throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.MISSING_DATA_DIR);
|
||||||
|
}
|
||||||
|
if (!Files.isDirectory(dataDir)) {
|
||||||
|
throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.DATA_NOT_A_DIRECTORY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkConfigFile(Path pathToVault) throws NotAVaultDirectoryException {
|
||||||
|
Path vaultConfig = pathToVault.resolve(VAULTCONFIG_FILENAME);
|
||||||
|
|
||||||
|
try (var ch = Files.newByteChannel(vaultConfig, StandardOpenOption.READ)) {
|
||||||
|
ch.read(ByteBuffer.allocate(1));
|
||||||
|
} catch (AccessDeniedException e) {
|
||||||
|
throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.VAULT_CONFIG_ACCESS_DENIED);
|
||||||
|
} catch (NoSuchFileException e) {
|
||||||
|
throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.MISSING_VAULT_CONFIG);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.warn("Failed to read vault config: {}", e.getMessage());
|
||||||
|
throw new NotAVaultDirectoryException(pathToVault, NotAVaultDirectoryException.Reason.UNSUPPORTED_STRUCTURE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private VaultSettings newVaultSettings(Path path) {
|
private VaultSettings newVaultSettings(Path path) {
|
||||||
@@ -153,7 +203,7 @@ public class VaultListManager {
|
|||||||
//for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
//for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
||||||
vaultSettings.lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME);
|
vaultSettings.lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME);
|
||||||
}
|
}
|
||||||
case VAULT_CONFIG_MISSING -> {
|
case VAULT_CONFIG_MISSING -> {
|
||||||
//Nothing to do here, since there is no config to read
|
//Nothing to do here, since there is no config to read
|
||||||
}
|
}
|
||||||
case MISSING, ALL_MISSING, ERROR, PROCESSING -> {
|
case MISSING, ALL_MISSING, ERROR, PROCESSING -> {
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
package org.cryptomator.launcher;
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
/**
|
||||||
import java.util.Collection;
|
* An event triggering an action in the running application instance.
|
||||||
|
* <p>
|
||||||
public record AppLaunchEvent(AppLaunchEvent.EventType type, Collection<Path> pathsToOpen) {
|
* Produced by the launch-argument handling (see {@link LaunchArgsParser} and the {@code *RequestHandler}s) and consumed
|
||||||
|
* by the UI's {@code AppLaunchEventHandler}. Each permitted subtype represents one supported action:
|
||||||
public enum EventType {
|
* <ul>
|
||||||
REVEAL_APP,
|
* <li>{@link RevealRunningEvent} - reveal the already-running app,</li>
|
||||||
OPEN_FILE
|
* <li>{@link OpenFileEvent} - open one or more paths,</li>
|
||||||
}
|
* <li>{@link OpenHubVaultEvent} - open a Hub vault from a deeplink.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public sealed interface AppLaunchEvent permits RevealRunningEvent, OpenFileEvent, OpenHubVaultEvent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class CryptomatorModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@Named("launchEventQueue")
|
@Named("launchEventQueue")
|
||||||
static BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
|
static BlockingQueue<AppLaunchEvent> provideLaunchEventQueue() {
|
||||||
return new ArrayBlockingQueue<>(10);
|
return new ArrayBlockingQueue<>(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class FileOpenRequestHandler {
|
|||||||
|
|
||||||
private void openFiles(OpenFilesEvent evt) {
|
private void openFiles(OpenFilesEvent evt) {
|
||||||
Collection<Path> pathsToOpen = evt.getFiles().stream().map(File::toPath).toList();
|
Collection<Path> pathsToOpen = evt.getFiles().stream().map(File::toPath).toList();
|
||||||
AppLaunchEvent launchEvent = new AppLaunchEvent(AppLaunchEvent.EventType.OPEN_FILE, pathsToOpen);
|
AppLaunchEvent launchEvent = new OpenFileEvent(pathsToOpen);
|
||||||
tryToEnqueueFileOpenRequest(launchEvent);
|
tryToEnqueueFileOpenRequest(launchEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ class FileOpenRequestHandler {
|
|||||||
}
|
}
|
||||||
}).filter(Objects::nonNull).toList();
|
}).filter(Objects::nonNull).toList();
|
||||||
if (!pathsToOpen.isEmpty()) {
|
if (!pathsToOpen.isEmpty()) {
|
||||||
AppLaunchEvent launchEvent = new AppLaunchEvent(AppLaunchEvent.EventType.OPEN_FILE, pathsToOpen);
|
AppLaunchEvent launchEvent = new OpenFileEvent(pathsToOpen);
|
||||||
tryToEnqueueFileOpenRequest(launchEvent);
|
tryToEnqueueFileOpenRequest(launchEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ class FileOpenRequestHandler {
|
|||||||
|
|
||||||
private void tryToEnqueueFileOpenRequest(AppLaunchEvent launchEvent) {
|
private void tryToEnqueueFileOpenRequest(AppLaunchEvent launchEvent) {
|
||||||
if (!launchEventQueue.offer(launchEvent)) {
|
if (!launchEventQueue.offer(launchEvent)) {
|
||||||
LOG.warn("Could not enqueue application launch event.", launchEvent);
|
LOG.warn("Could not enqueue application launch event {}.", launchEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.slf4j.LoggerFactory;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
|
||||||
@@ -16,24 +15,28 @@ class IpcMessageHandler implements IpcMessageListener {
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(IpcMessageHandler.class);
|
private static final Logger LOG = LoggerFactory.getLogger(IpcMessageHandler.class);
|
||||||
|
|
||||||
private final FileOpenRequestHandler fileOpenRequestHandler;
|
private final LaunchArgsParser launchArgsParser;
|
||||||
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public IpcMessageHandler(FileOpenRequestHandler fileOpenRequestHandler, @Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue) {
|
public IpcMessageHandler(LaunchArgsParser launchArgsParser, @Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue) {
|
||||||
this.fileOpenRequestHandler = fileOpenRequestHandler;
|
this.launchArgsParser = launchArgsParser;
|
||||||
this.launchEventQueue = launchEventQueue;
|
this.launchEventQueue = launchEventQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void revealRunningApp() {
|
public void revealRunningApp() {
|
||||||
launchEventQueue.add(new AppLaunchEvent(AppLaunchEvent.EventType.REVEAL_APP, Collections.emptyList()));
|
launchEventQueue.add(new RevealRunningEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleLaunchArgs(List<String> args) {
|
public void handleLaunchArgs(List<String> args) {
|
||||||
LOG.debug("Received launch args: {}", args.stream().reduce((a, b) -> a + ", " + b).orElse(""));
|
LOG.debug("Received launch args: {}", args);
|
||||||
fileOpenRequestHandler.handleLaunchArgs(args);
|
try {
|
||||||
|
launchArgsParser.process(args);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOG.warn("Ignoring malformed launch args: {}", e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preprocesses the launch arguments and delegates them to the matching handler.
|
||||||
|
* <p>
|
||||||
|
* An argument is treated as a URI if it starts with a (non-{@code file}) scheme of at least two characters, e.g.
|
||||||
|
* {@code cryptomator://…}. Everything else - including plain paths and {@code file://…} URIs - is treated as a file path
|
||||||
|
* and forwarded to the {@link FileOpenRequestHandler}. The two-character minimum prevents Windows drive letters
|
||||||
|
* (e.g. {@code C:\…}) from being misinterpreted as URIs.
|
||||||
|
* <p>
|
||||||
|
* URIs and file paths must not be mixed and at most a single URI is accepted, which has to be the first argument.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class LaunchArgsParser {
|
||||||
|
|
||||||
|
private static final Pattern SCHEME_PATTERN = Pattern.compile("^([a-zA-Z][a-zA-Z0-9+.-]+):.*");
|
||||||
|
private static final String FILE_SCHEME = "file";
|
||||||
|
|
||||||
|
private final FileOpenRequestHandler fileOpenRequestHandler;
|
||||||
|
private final URIOpenRequestHandler uriOpenRequestHandler;
|
||||||
|
private final NoopRequestHandler noopRequestHandler;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public LaunchArgsParser(FileOpenRequestHandler fileOpenRequestHandler, URIOpenRequestHandler uriOpenRequestHandler, NoopRequestHandler noopRequestHandler) {
|
||||||
|
this.fileOpenRequestHandler = fileOpenRequestHandler;
|
||||||
|
this.uriOpenRequestHandler = uriOpenRequestHandler;
|
||||||
|
this.noopRequestHandler = noopRequestHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classifies the given launch arguments and delegates them to the responsible handler.
|
||||||
|
*
|
||||||
|
* @param args the raw launch arguments
|
||||||
|
* @throws IllegalArgumentException if URIs and file paths are mixed, if more than one URI is given, if a URI is not
|
||||||
|
* the first argument, or if a URI argument is malformed
|
||||||
|
*/
|
||||||
|
public void process(List<String> args) {
|
||||||
|
if(args.isEmpty()) {
|
||||||
|
noopRequestHandler.revealApp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var classified = args.stream().map(LaunchArgsParser::classify).toList();
|
||||||
|
var uris = classified.stream().filter(arg -> arg.kind() == Kind.URI).toList();
|
||||||
|
|
||||||
|
if (uris.isEmpty()) {
|
||||||
|
var paths = classified.stream().map(Arg::value).toList();
|
||||||
|
fileOpenRequestHandler.handleLaunchArgs(paths);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uris.size() > 1) {
|
||||||
|
throw new IllegalArgumentException("Only a single URI argument is accepted, but got " + uris.size() + ".");
|
||||||
|
}
|
||||||
|
if (classified.getFirst().kind() != Kind.URI) {
|
||||||
|
throw new IllegalArgumentException("URI argument must be the first parameter.");
|
||||||
|
}
|
||||||
|
if (classified.size() > 1) {
|
||||||
|
throw new IllegalArgumentException("Mixing a URI with file paths is not supported.");
|
||||||
|
}
|
||||||
|
uriOpenRequestHandler.handleLaunchArgs(URI.create(classified.getFirst().value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Arg classify(String arg) {
|
||||||
|
var matcher = SCHEME_PATTERN.matcher(arg);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return new Arg(Kind.PATH, arg);
|
||||||
|
}
|
||||||
|
var scheme = matcher.group(1);
|
||||||
|
if (FILE_SCHEME.equalsIgnoreCase(scheme)) {
|
||||||
|
// file:// URIs (e.g. passed by Linux file managers) are file paths in disguise
|
||||||
|
return new Arg(Kind.PATH, Path.of(URI.create(arg)).toString());
|
||||||
|
}
|
||||||
|
return new Arg(Kind.URI, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Kind {PATH, URI}
|
||||||
|
|
||||||
|
private record Arg(Kind kind, String value) {}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class NoopRequestHandler {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(NoopRequestHandler.class);
|
||||||
|
|
||||||
|
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public NoopRequestHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue) {
|
||||||
|
this.launchEventQueue = launchEventQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void revealApp() {
|
||||||
|
AppLaunchEvent launchEvent = new RevealRunningEvent();
|
||||||
|
if (!launchEventQueue.offer(launchEvent)) {
|
||||||
|
LOG.warn("Could not enqueue application launch event {}.", launchEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that the given paths (e.g. {@code .cryptomator} vault files) are opened.
|
||||||
|
*
|
||||||
|
* @param pathsToOpen the paths to open
|
||||||
|
*/
|
||||||
|
public record OpenFileEvent(Collection<Path> pathsToOpen) implements AppLaunchEvent {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import org.cryptomator.cryptofs.VaultConfig;
|
||||||
|
import org.cryptomator.cryptofs.VaultConfigLoadException;
|
||||||
|
import org.cryptomator.ui.keyloading.hub.HubConfig;
|
||||||
|
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests opening a Hub vault from an {@code org.cryptomator://vault/open#vaultConfig=…} deeplink.
|
||||||
|
* <p>
|
||||||
|
* The single parameter is the vault's {@code vault.cryptomator}, a compact JWS embedded verbatim, carried in the fragment part.
|
||||||
|
* <p>
|
||||||
|
* Notes:
|
||||||
|
* <ul>
|
||||||
|
* <li> The config is read <em>unverified</em>, since its signature is keyed on the masterkey, which is only obtainable from Hu later on. </li>
|
||||||
|
* <li> The deeplink parsing makes a strict validation due to untrusted input</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param vaultConfig the decoded, unverified vault config
|
||||||
|
* @param vaultId the vault's id within its Hub instance, taken from the config's {@code jti} claim
|
||||||
|
*/
|
||||||
|
public record OpenHubVaultEvent(VaultConfig.UnverifiedVaultConfig vaultConfig, UUID vaultId) implements AppLaunchEvent {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(OpenHubVaultEvent.class);
|
||||||
|
private static final String SCHEME = "org.cryptomator";
|
||||||
|
private static final String HOST = "vault";
|
||||||
|
private static final String PATH = "/open";
|
||||||
|
private static final String PARAM_VAULT_CONFIG = "vaultConfig";
|
||||||
|
private static final String HUB_HEADER = "hub";
|
||||||
|
private static final int MAX_CONFIG_LENGTH = 8192; //real Hub vault config is ~1KB leaving some room for extensions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to interpret the given URI as an {@code org.cryptomator://vault/open#vaultConfig=…} deeplink.
|
||||||
|
*
|
||||||
|
* @param uri the deeplink URI
|
||||||
|
* @return the parsed event, or an empty optional if the URI's scheme, host or path do not identify a vault-open
|
||||||
|
* deeplink
|
||||||
|
* @throws IllegalArgumentException if the URI identifies a vault-open deeplink, but the config is missing, too
|
||||||
|
* large, not decodable, or does not describe a Hub vault
|
||||||
|
*/
|
||||||
|
public static Optional<OpenHubVaultEvent> tryParse(URI uri) {
|
||||||
|
if (!SCHEME.equalsIgnoreCase(uri.getScheme()) || !HOST.equalsIgnoreCase(uri.getHost()) || !PATH.equals(uri.getPath())) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
var params = parseParams(uri.getRawFragment());
|
||||||
|
|
||||||
|
var token = params.get(PARAM_VAULT_CONFIG);
|
||||||
|
if (token == null || token.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Missing required fragment parameter '" + PARAM_VAULT_CONFIG + "'.");
|
||||||
|
}
|
||||||
|
var vaultConfig = decode(token);
|
||||||
|
requireHubVault(vaultConfig);
|
||||||
|
var vaultId = extractVaultId(vaultConfig);
|
||||||
|
|
||||||
|
var leftoverParams = params.keySet().stream().filter(k -> !k.equals(PARAM_VAULT_CONFIG)).toList();
|
||||||
|
if (!leftoverParams.isEmpty()) {
|
||||||
|
LOG.debug("Ignoring unknown parameters {}", leftoverParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(new OpenHubVaultEvent(vaultConfig, vaultId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VaultConfig.UnverifiedVaultConfig decode(String token) {
|
||||||
|
// a compact JWS is ASCII, so its character count is its byte count
|
||||||
|
if (token.length() > MAX_CONFIG_LENGTH) {
|
||||||
|
throw new IllegalArgumentException("Fragment parameter '%s' must not exceed %d bytes.".formatted(PARAM_VAULT_CONFIG, MAX_CONFIG_LENGTH));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return VaultConfig.decode(token);
|
||||||
|
} catch (VaultConfigLoadException e) {
|
||||||
|
throw new IllegalArgumentException("Fragment parameter '" + PARAM_VAULT_CONFIG + "' is not a decodable vault config.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures the config describes a Hub vault and that the endpoints the app will talk to are usable.
|
||||||
|
*/
|
||||||
|
private static void requireHubVault(VaultConfig.UnverifiedVaultConfig vaultConfig) {
|
||||||
|
var keyIdScheme = vaultConfig.getKeyId().getScheme();
|
||||||
|
if (keyIdScheme == null || !keyIdScheme.startsWith(HubKeyLoadingStrategy.SCHEME_PREFIX)) {
|
||||||
|
throw new IllegalArgumentException("Vault config does not describe a Hub vault, but had key id scheme '" + keyIdScheme + "'.");
|
||||||
|
}
|
||||||
|
HubConfig hubConfig;
|
||||||
|
try {
|
||||||
|
hubConfig = vaultConfig.getHeader(HUB_HEADER, HubConfig.class);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw new IllegalArgumentException("Vault config contains an unreadable '" + HUB_HEADER + "' header.", e);
|
||||||
|
}
|
||||||
|
if (hubConfig == null) {
|
||||||
|
throw new IllegalArgumentException("Vault config contains no '" + HUB_HEADER + "' header.");
|
||||||
|
}
|
||||||
|
URI apiBaseUrl;
|
||||||
|
try {
|
||||||
|
apiBaseUrl = hubConfig.getApiBaseUrl();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw new IllegalArgumentException("Vault config declares no usable hub api base url.", e);
|
||||||
|
}
|
||||||
|
requireUsableEndpoint("apiBaseUrl", apiBaseUrl);
|
||||||
|
requireUsableEndpoint("authEndpoint", toUri("authEndpoint", hubConfig.authEndpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URI toUri(String field, String value) {
|
||||||
|
if (value == null || value.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Vault config declares no hub " + field + ".");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return URI.create(value);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IllegalArgumentException("Vault config's hub " + field + " is not a valid url, but was '" + value + "'.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void requireUsableEndpoint(String field, URI uri) {
|
||||||
|
if (!uri.isAbsolute() || uri.getHost() == null) {
|
||||||
|
throw new IllegalArgumentException("Vault config's hub " + field + " is not an absolute url with a host, but was '" + uri + "'.");
|
||||||
|
}
|
||||||
|
// Whether an http host is acceptable (it is, for local development) is decided by CheckHostTrustController
|
||||||
|
// Here we only ensure the endpoint is shaped like something that decision can be made on.
|
||||||
|
var scheme = uri.getScheme();
|
||||||
|
if (!"https".equalsIgnoreCase(scheme) && !"http".equalsIgnoreCase(scheme)) {
|
||||||
|
throw new IllegalArgumentException("Vault config's hub " + field + " is neither http nor https, but was '" + uri + "'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the vault id from the config's {@code jti} claim.
|
||||||
|
* <p>
|
||||||
|
* Requiring a UUID matters beyond well-formedness: the id is interpolated into the {@code api/vaults/{vaultId}/…}
|
||||||
|
* request path, so it must not be able to introduce a path segment. A {@code jti} is an arbitrary string, so parsing
|
||||||
|
* it as a {@link UUID} and passing that on - rather than the raw claim - is what keeps that guarantee.
|
||||||
|
* <p>
|
||||||
|
* Hub writes the same id into the key id's trailing path segment, and the two have always agreed, so a config where
|
||||||
|
* they differ is forged or broken and is rejected.
|
||||||
|
*/
|
||||||
|
private static UUID extractVaultId(VaultConfig.UnverifiedVaultConfig vaultConfig) {
|
||||||
|
var allegedVaultId = vaultConfig.allegedVaultId();
|
||||||
|
if (allegedVaultId == null || allegedVaultId.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Vault config declares no vault id.");
|
||||||
|
}
|
||||||
|
UUID vaultId;
|
||||||
|
try {
|
||||||
|
vaultId = UUID.fromString(allegedVaultId);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IllegalArgumentException("Vault config's vault id is not a uuid, but was '" + allegedVaultId + "'.", e);
|
||||||
|
}
|
||||||
|
var keyId = vaultConfig.getKeyId();
|
||||||
|
var path = keyId.getPath();
|
||||||
|
var lastSegment = path == null ? "" : path.substring(path.lastIndexOf('/') + 1);
|
||||||
|
if (!vaultId.toString().equalsIgnoreCase(lastSegment)) {
|
||||||
|
throw new IllegalArgumentException("Vault config's vault id '" + vaultId + "' does not match its key id '" + keyId + "'.");
|
||||||
|
}
|
||||||
|
return vaultId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> parseParams(String rawParams) {
|
||||||
|
var params = new HashMap<String, String>();
|
||||||
|
if (rawParams == null || rawParams.isEmpty()) {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
for (var pair : rawParams.split("&")) {
|
||||||
|
var idx = pair.indexOf('=');
|
||||||
|
if (idx < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var key = URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8);
|
||||||
|
var value = URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8);
|
||||||
|
params.put(key, value);
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that the already-running application instance reveals itself (brings its main window to the front).
|
||||||
|
*/
|
||||||
|
public record RevealRunningEvent() implements AppLaunchEvent {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package org.cryptomator.launcher;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class URIOpenRequestHandler {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(URIOpenRequestHandler.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The registered deeplink parsers, tried in order. Each returns a matching event, an empty optional if the URI is
|
||||||
|
* not its concern, or throws {@link IllegalArgumentException} if the URI is its concern but malformed.
|
||||||
|
*/
|
||||||
|
private static final List<Function<URI, Optional<? extends AppLaunchEvent>>> DEEPLINK_PARSERS = List.of( //
|
||||||
|
OpenHubVaultEvent::tryParse //
|
||||||
|
);
|
||||||
|
|
||||||
|
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public URIOpenRequestHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue) {
|
||||||
|
this.launchEventQueue = launchEventQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleLaunchArgs(URI uri) {
|
||||||
|
AppLaunchEvent launchEvent = toLaunchEvent(uri);
|
||||||
|
if (!launchEventQueue.offer(launchEvent)) {
|
||||||
|
LOG.warn("Could not enqueue application launch event {}.", launchEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppLaunchEvent toLaunchEvent(URI uri) {
|
||||||
|
try {
|
||||||
|
for (var parser : DEEPLINK_PARSERS) {
|
||||||
|
var event = parser.apply(uri);
|
||||||
|
if (event.isPresent()) {
|
||||||
|
return event.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOG.warn("Received malformed deeplink {}: {}. Revealing running app instead.", uri, e.getMessage());
|
||||||
|
return new RevealRunningEvent();
|
||||||
|
}
|
||||||
|
LOG.warn("Received unsupported deeplink {}, revealing running app instead.", uri);
|
||||||
|
return new RevealRunningEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,12 +2,14 @@ package org.cryptomator.ui.addvaultwizard;
|
|||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.vaults.NotAVaultDirectoryException;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultListManager;
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
import org.cryptomator.integrations.uiappearance.Theme;
|
import org.cryptomator.integrations.uiappearance.Theme;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationStyle;
|
import org.cryptomator.ui.fxapp.FxApplicationStyle;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -41,6 +43,7 @@ public class ChooseExistingVaultController implements FxController {
|
|||||||
private final ObjectProperty<Vault> vault;
|
private final ObjectProperty<Vault> vault;
|
||||||
private final VaultListManager vaultListManager;
|
private final VaultListManager vaultListManager;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
|
private final Dialogs dialogs;
|
||||||
private final ObservableValue<Image> screenshot;
|
private final ObservableValue<Image> screenshot;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -51,6 +54,7 @@ public class ChooseExistingVaultController implements FxController {
|
|||||||
@AddVaultWizardWindow ObjectProperty<Vault> vault, //
|
@AddVaultWizardWindow ObjectProperty<Vault> vault, //
|
||||||
VaultListManager vaultListManager, //
|
VaultListManager vaultListManager, //
|
||||||
ResourceBundle resourceBundle, //
|
ResourceBundle resourceBundle, //
|
||||||
|
Dialogs dialogs, //
|
||||||
FxApplicationStyle applicationStyle) {
|
FxApplicationStyle applicationStyle) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.successScene = successScene;
|
this.successScene = successScene;
|
||||||
@@ -59,6 +63,7 @@ public class ChooseExistingVaultController implements FxController {
|
|||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.vaultListManager = vaultListManager;
|
this.vaultListManager = vaultListManager;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
|
this.dialogs = dialogs;
|
||||||
this.screenshot = applicationStyle.appliedAppThemeProperty().map(this::selectScreenshot);
|
this.screenshot = applicationStyle.appliedAppThemeProperty().map(this::selectScreenshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +92,9 @@ public class ChooseExistingVaultController implements FxController {
|
|||||||
Vault newVault = vaultListManager.add(vaultPath.get());
|
Vault newVault = vaultListManager.add(vaultPath.get());
|
||||||
vault.set(newVault);
|
vault.set(newVault);
|
||||||
window.setScene(successScene.get());
|
window.setScene(successScene.get());
|
||||||
|
} catch (NotAVaultDirectoryException e) {
|
||||||
|
LOG.warn("Selected folder is not a vault directory: {}", e.getMessage());
|
||||||
|
dialogs.prepareNotAVaultDirectoryDialog(window, e).build().showAndWait();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Failed to open existing vault.", e);
|
LOG.error("Failed to open existing vault.", e);
|
||||||
appWindows.showErrorWindow(e, window, window.getScene());
|
appWindows.showErrorWindow(e, window, window.getScene());
|
||||||
|
|||||||
@@ -58,8 +58,6 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Vault vault;
|
private final Vault vault;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final List<Path> initialList;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public TableColumn<CipherAndCleartext, String> ciphertextColumn;
|
public TableColumn<CipherAndCleartext, String> ciphertextColumn;
|
||||||
@FXML
|
@FXML
|
||||||
@@ -68,12 +66,11 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
public TableView<CipherAndCleartext> cipherToCleartextTable;
|
public TableView<CipherAndCleartext> cipherToCleartextTable;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, @DecryptNameWindow List<Path> pathsToDecrypt, ResourceBundle resourceBundle) {
|
public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, ResourceBundle resourceBundle) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList());
|
this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
this.initialList = pathsToDecrypt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -97,8 +94,7 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
});
|
});
|
||||||
cipherToCleartextTable.setOnDragDropped(event -> {
|
cipherToCleartextTable.setOnDragDropped(event -> {
|
||||||
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
|
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
|
||||||
checkAndDecrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
|
decrypt(event.getDragboard().getFiles().stream().map(File::toPath).toList());
|
||||||
cipherToCleartextTable.setItems(mapping);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cipherToCleartextTable.setOnDragExited(_ -> cipherToCleartextTable.setItems(mapping));
|
cipherToCleartextTable.setOnDragExited(_ -> cipherToCleartextTable.setItems(mapping));
|
||||||
@@ -124,9 +120,7 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!initialList.isEmpty()) {
|
window.setOnHidden(_ -> mapping.clear());
|
||||||
checkAndDecrypt(initialList);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copySingleCelltoClipboard() {
|
private void copySingleCelltoClipboard() {
|
||||||
@@ -149,10 +143,18 @@ public class DecryptFileNamesViewController implements FxController {
|
|||||||
fileChooser.setInitialDirectory(vault.getPath().toFile());
|
fileChooser.setInitialDirectory(vault.getPath().toFile());
|
||||||
var ciphertextNodes = fileChooser.showOpenMultipleDialog(window);
|
var ciphertextNodes = fileChooser.showOpenMultipleDialog(window);
|
||||||
if (ciphertextNodes != null) {
|
if (ciphertextNodes != null) {
|
||||||
checkAndDecrypt(ciphertextNodes.stream().map(File::toPath).toList());
|
decrypt(ciphertextNodes.stream().map(File::toPath).toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void decrypt(List<Path> pathsToDecrypt) {
|
||||||
|
if (pathsToDecrypt.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkAndDecrypt(pathsToDecrypt);
|
||||||
|
cipherToCleartextTable.setItems(mapping);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkAndDecrypt(List<Path> pathsToDecrypt) {
|
private void checkAndDecrypt(List<Path> pathsToDecrypt) {
|
||||||
mapping.clear();
|
mapping.clear();
|
||||||
//Assumption: All files are in the same directory
|
//Assumption: All files are in the same directory
|
||||||
|
|||||||
@@ -28,23 +28,28 @@ public interface DecryptNameComponent {
|
|||||||
@FxmlScene(FxmlFile.DECRYPTNAMES)
|
@FxmlScene(FxmlFile.DECRYPTNAMES)
|
||||||
Lazy<Scene> decryptNamesView();
|
Lazy<Scene> decryptNamesView();
|
||||||
|
|
||||||
|
DecryptFileNamesViewController controller();
|
||||||
|
|
||||||
@DecryptNameWindow
|
@DecryptNameWindow
|
||||||
Vault vault();
|
Vault vault();
|
||||||
|
|
||||||
default void showDecryptFileNameWindow() {
|
default void showDecryptFileNameWindow(List<Path> pathsToDecrypt) {
|
||||||
Stage s = window();
|
Stage s = window();
|
||||||
s.setScene(decryptNamesView().get());
|
s.setScene(decryptNamesView().get());
|
||||||
s.sizeToScene();
|
s.sizeToScene();
|
||||||
if (vault().isUnlocked()) {
|
if (vault().isUnlocked()) {
|
||||||
|
controller().decrypt(pathsToDecrypt);
|
||||||
s.show();
|
s.show();
|
||||||
|
s.requestFocus();
|
||||||
} else {
|
} else {
|
||||||
LOG.error("Aborted showing DecryptFileName window: vault state is not {}, but {}.", VaultState.Value.UNLOCKED, vault().getState());
|
LOG.error("Aborted showing DecryptFileName window: vault state is not {}, but {}.", VaultState.Value.UNLOCKED, vault().getState());
|
||||||
|
s.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subcomponent.Factory
|
@Subcomponent.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
||||||
DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner, @BindsInstance @DecryptNameWindow List<Path> pathsToDecrypt);
|
DecryptNameComponent create(@BindsInstance @DecryptNameWindow Vault vault, @BindsInstance @Named("windowOwner") Stage owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.cryptomator.ui.dialogs;
|
package org.cryptomator.ui.dialogs;
|
||||||
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.vaults.NotAVaultDirectoryException;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||||
import org.cryptomator.ui.common.StageFactory;
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
@@ -61,9 +62,9 @@ public class Dialogs {
|
|||||||
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
.setOkButtonKey(BUTTON_KEY_CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleDialog.Builder prepareHubVaultArchived(Stage window, Vault vault) {
|
public SimpleDialog.Builder prepareHubVaultArchived(Stage window, String vaultDisplayName) {
|
||||||
return createDialogBuilder().setOwner(window) //
|
return createDialogBuilder().setOwner(window) //
|
||||||
.setTitleKey("unlock.title", vault.getDisplayName()) //
|
.setTitleKey("unlock.title", vaultDisplayName) //
|
||||||
.setMessageKey("hub.archived.message") //
|
.setMessageKey("hub.archived.message") //
|
||||||
.setDescriptionKey("hub.archived.description") //
|
.setDescriptionKey("hub.archived.description") //
|
||||||
.setIcon(FontAwesome5Icon.BAN)//
|
.setIcon(FontAwesome5Icon.BAN)//
|
||||||
@@ -139,6 +140,24 @@ public class Dialogs {
|
|||||||
.setCancelAction(Stage::close);
|
.setCancelAction(Stage::close);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SimpleDialog.Builder prepareNotAVaultDirectoryDialog(Stage window, NotAVaultDirectoryException e) {
|
||||||
|
String descriptionKey = switch (e.notAVaultReason()) {
|
||||||
|
case MISSING_DATA_DIR -> "addvaultwizard.existing.notAVault.description.missingDataDir";
|
||||||
|
case DATA_NOT_A_DIRECTORY -> "addvaultwizard.existing.notAVault.description.dataNotADirectory";
|
||||||
|
case MISSING_VAULT_CONFIG -> "addvaultwizard.existing.notAVault.description.missingVaultConfig";
|
||||||
|
case VAULT_CONFIG_ACCESS_DENIED -> "addvaultwizard.existing.notAVault.description.vaultConfigAccessDenied";
|
||||||
|
case UNSUPPORTED_STRUCTURE -> "addvaultwizard.existing.notAVault.description.unsupportedStructure";
|
||||||
|
};
|
||||||
|
return createDialogBuilder() //
|
||||||
|
.setOwner(window) //
|
||||||
|
.setTitleKey("addvaultwizard.existing.notAVault.title") //
|
||||||
|
.setMessageKey("addvaultwizard.existing.notAVault.message") //
|
||||||
|
.setDescriptionKey(descriptionKey, e.path().getFileName() != null ? e.path().getFileName().toString() : e.path().toString()) //
|
||||||
|
.setIcon(FontAwesome5Icon.EXCLAMATION) //
|
||||||
|
.setOkButtonKey(BUTTON_KEY_CLOSE) //
|
||||||
|
.setOkAction(Stage::close);
|
||||||
|
}
|
||||||
|
|
||||||
public SimpleDialog.Builder prepareNoDDirectorySelectedDialog(Stage window) {
|
public SimpleDialog.Builder prepareNoDDirectorySelectedDialog(Stage window) {
|
||||||
return createDialogBuilder() //
|
return createDialogBuilder() //
|
||||||
.setOwner(window) //
|
.setOwner(window) //
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
package org.cryptomator.ui.fxapp;
|
package org.cryptomator.ui.fxapp;
|
||||||
|
|
||||||
|
import org.cryptomator.common.vaults.NotAVaultDirectoryException;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultListManager;
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
import org.cryptomator.launcher.AppLaunchEvent;
|
import org.cryptomator.launcher.AppLaunchEvent;
|
||||||
|
import org.cryptomator.launcher.OpenFileEvent;
|
||||||
|
import org.cryptomator.launcher.OpenHubVaultEvent;
|
||||||
|
import org.cryptomator.launcher.RevealRunningEvent;
|
||||||
import org.cryptomator.ui.common.VaultService;
|
import org.cryptomator.ui.common.VaultService;
|
||||||
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
|
import org.cryptomator.ui.keyloading.hub.HubVaults;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.stage.Stage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -28,15 +36,21 @@ class AppLaunchEventHandler {
|
|||||||
private final ExecutorService executorService;
|
private final ExecutorService executorService;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
private final VaultListManager vaultListManager;
|
private final VaultListManager vaultListManager;
|
||||||
|
private final ObservableList<Vault> vaults;
|
||||||
private final VaultService vaultService;
|
private final VaultService vaultService;
|
||||||
|
private final Stage primaryStage;
|
||||||
|
private final Dialogs dialogs;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService) {
|
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, ObservableList<Vault> vaults, VaultService vaultService, @PrimaryStage Stage primaryStage, Dialogs dialogs) {
|
||||||
this.launchEventQueue = launchEventQueue;
|
this.launchEventQueue = launchEventQueue;
|
||||||
this.executorService = executorService;
|
this.executorService = executorService;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
this.vaultListManager = vaultListManager;
|
this.vaultListManager = vaultListManager;
|
||||||
|
this.vaults = vaults;
|
||||||
this.vaultService = vaultService;
|
this.vaultService = vaultService;
|
||||||
|
this.primaryStage = primaryStage;
|
||||||
|
this.dialogs = dialogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startHandlingLaunchEvents() {
|
public void startHandlingLaunchEvents() {
|
||||||
@@ -56,33 +70,57 @@ class AppLaunchEventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleLaunchEvent(AppLaunchEvent event) {
|
private void handleLaunchEvent(AppLaunchEvent event) {
|
||||||
switch (event.type()) {
|
switch (event) {
|
||||||
case REVEAL_APP -> appWindows.showMainWindow();
|
case RevealRunningEvent _ -> appWindows.showMainWindow();
|
||||||
case OPEN_FILE -> Platform.runLater(() -> {
|
case OpenFileEvent openFileEvent -> openFileEvent.pathsToOpen().forEach(this::openPotentialVault);
|
||||||
event.pathsToOpen().forEach(this::openPotentialVault);
|
case OpenHubVaultEvent openHubVaultEvent -> openHubVault(openHubVaultEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a hub vault is set up on this machine is a purely local question - hub manages the vault's key, not where
|
||||||
|
* it lives. Only if it is not set up here do we need to ask hub about it.
|
||||||
|
*/
|
||||||
|
private void openHubVault(OpenHubVaultEvent event) {
|
||||||
|
var existing = HubVaults.findByVaultId(vaults, event.vaultId());
|
||||||
|
if (existing.isPresent()) {
|
||||||
|
var vault = existing.get();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (vault.isUnlocked()) {
|
||||||
|
vaultService.reveal(vault);
|
||||||
|
} else if (vault.isLocked()) {
|
||||||
|
appWindows.startUnlockWorkflow(vault, null);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
default -> LOG.warn("Unsupported event type: {}", event.type());
|
} else {
|
||||||
|
//TODO: authenticate, ask hub for the vault's details and offer to add it, see docs/hub-vault-open-deeplink-plan.md
|
||||||
|
LOG.info("Hub vault {} is not set up on this machine.", event.vaultId());
|
||||||
|
appWindows.showMainWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO deduplicate MainWindowController...
|
// TODO deduplicate MainWindowController...
|
||||||
private void openPotentialVault(Path path) {
|
private void openPotentialVault(Path path) {
|
||||||
assert Platform.isFxApplicationThread();
|
Path potentialVaultPath = path.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT) ? path.getParent() : path;
|
||||||
try {
|
Optional<Vault> existing = vaultListManager.get(potentialVaultPath.normalize().toAbsolutePath());
|
||||||
Path potentialVaultPath = path.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT) ? path.getParent() : path;
|
if (existing.isPresent()) {
|
||||||
final Optional<Vault> v = vaultListManager.get(potentialVaultPath);
|
Platform.runLater(() -> {
|
||||||
if (v.isPresent()) {
|
if (existing.get().isUnlocked()) {
|
||||||
if (v.get().isUnlocked()) {
|
vaultService.reveal(existing.get());
|
||||||
vaultService.reveal(v.get());
|
} else if (existing.get().isLocked()) {
|
||||||
} else if (v.get().isLocked()) {
|
appWindows.startUnlockWorkflow(existing.get(), null);
|
||||||
appWindows.startUnlockWorkflow(v.get(), null);
|
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
vaultListManager.add(potentialVaultPath);
|
return;
|
||||||
LOG.debug("Added vault {}", potentialVaultPath);
|
}
|
||||||
}
|
try {
|
||||||
|
vaultListManager.add(potentialVaultPath);
|
||||||
|
LOG.debug("Added vault {}", potentialVaultPath);
|
||||||
|
} catch (NotAVaultDirectoryException e) {
|
||||||
|
LOG.warn("Cannot add {}: {}", potentialVaultPath, e.getMessage());
|
||||||
|
Platform.runLater(() -> dialogs.prepareNotAVaultDirectoryDialog(primaryStage, e).build().showAndWait());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Failed to add vault " + path, e);
|
LOG.error("Failed to add vault {}", potentialVaultPath, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.cryptomator.ui.common.FxmlLoaderFactory;
|
|||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.common.StageFactory;
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingComponent;
|
import org.cryptomator.ui.keyloading.KeyLoadingComponent;
|
||||||
|
import org.cryptomator.ui.keyloading.KeyLoadingRef;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
@@ -64,7 +65,11 @@ abstract class HealthCheckModule {
|
|||||||
@HealthCheckWindow
|
@HealthCheckWindow
|
||||||
@HealthCheckScoped
|
@HealthCheckScoped
|
||||||
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @HealthCheckWindow Vault vault, @Named("unlockWindow") Stage window ) {
|
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @HealthCheckWindow Vault vault, @Named("unlockWindow") Stage window ) {
|
||||||
return compFactory.create(vault, window).keyloadingStrategy();
|
try {
|
||||||
|
return compFactory.create(KeyLoadingRef.forVault(vault), vault, window).keyloadingStrategy();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return KeyLoadingStrategy.failed(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.cryptomator.ui.keyloading;
|
|||||||
|
|
||||||
import dagger.BindsInstance;
|
import dagger.BindsInstance;
|
||||||
import dagger.Subcomponent;
|
import dagger.Subcomponent;
|
||||||
|
import org.cryptomator.common.Nullable;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
@@ -16,7 +17,14 @@ public interface KeyLoadingComponent {
|
|||||||
@Subcomponent.Factory
|
@Subcomponent.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
||||||
KeyLoadingComponent create(@BindsInstance @KeyLoading Vault vault, @KeyLoading @BindsInstance Stage window);
|
/**
|
||||||
|
* @param vaultRef the {@link KeyLoadingRef} containing the info to load the key
|
||||||
|
* @param vault the local vault, or {@code null} if it is not set up on this machine.
|
||||||
|
* @param window the window to show the key loading scenes in
|
||||||
|
*/
|
||||||
|
KeyLoadingComponent create(@BindsInstance @KeyLoading KeyLoadingRef vaultRef, //
|
||||||
|
@BindsInstance @KeyLoading @Nullable Vault vault, //
|
||||||
|
@BindsInstance @KeyLoading Stage window);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.cryptomator.ui.keyloading;
|
|||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
|
||||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||||
@@ -10,7 +9,6 @@ import org.cryptomator.ui.keyloading.hub.HubKeyLoadingModule;
|
|||||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingModule;
|
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingModule;
|
||||||
|
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@@ -27,14 +25,10 @@ abstract class KeyLoadingModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@KeyLoading
|
@KeyLoading
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
static KeyLoadingStrategy provideKeyLoadingStrategy(@KeyLoading Vault vault, Map<String, Provider<KeyLoadingStrategy>> strategies) {
|
static KeyLoadingStrategy provideKeyLoadingStrategy(@KeyLoading KeyLoadingRef vaultRef, Map<String, Provider<KeyLoadingStrategy>> strategies) {
|
||||||
try {
|
String scheme = vaultRef.keyId().getScheme();
|
||||||
String scheme = vault.getVaultConfigCache().get().getKeyId().getScheme();
|
var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme));
|
||||||
var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme));
|
return strategies.getOrDefault(scheme, () -> fallback).get();
|
||||||
return strategies.getOrDefault(scheme, () -> fallback).get();
|
|
||||||
} catch (IOException e) {
|
|
||||||
return KeyLoadingStrategy.failed(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package org.cryptomator.ui.keyloading;
|
||||||
|
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.cryptofs.VaultConfig;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies the vault a key is being loaded for, independently of whether that vault exists on this machine.
|
||||||
|
* <p>
|
||||||
|
* Key loading needs the (unverified) vault config selecting the strategy and addresses
|
||||||
|
* the vault within the strategy, and the display name titles the windows.
|
||||||
|
* <p>
|
||||||
|
* The config is <em>unverified</em>: its signature is keyed on the masterkey, which is exactly what key loading is
|
||||||
|
* about to obtain.
|
||||||
|
*
|
||||||
|
* @param vaultConfig the vault's unverified config
|
||||||
|
* @param displayName the vault's name, as shown to the user
|
||||||
|
*/
|
||||||
|
public record KeyLoadingRef(VaultConfig.UnverifiedVaultConfig vaultConfig, String displayName) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a vault that is already set up on this machine.
|
||||||
|
*
|
||||||
|
* @param vault the vault to load a key for
|
||||||
|
* @throws IOException if the vault's config cannot be read
|
||||||
|
*/
|
||||||
|
public static KeyLoadingRef forVault(Vault vault) throws IOException {
|
||||||
|
return new KeyLoadingRef(vault.getVaultConfigCache().get(), vault.getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key id, whose scheme selects the key loading strategy.
|
||||||
|
*/
|
||||||
|
public URI keyId() {
|
||||||
|
return vaultConfig.getKeyId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The vault's id, i.e. how the vault is addressed within its Hub instance.
|
||||||
|
* <p>
|
||||||
|
* Read from the config's {@code jti} claim, which is the authoritative source: the key id carries the same id in its
|
||||||
|
* trailing path segment, but only its <em>scheme</em> is a source of truth here.
|
||||||
|
*/
|
||||||
|
public String vaultId() {
|
||||||
|
return vaultConfig.allegedVaultId();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ import dagger.Provides;
|
|||||||
import dagger.multibindings.IntoMap;
|
import dagger.multibindings.IntoMap;
|
||||||
import dagger.multibindings.StringKey;
|
import dagger.multibindings.StringKey;
|
||||||
import org.cryptomator.common.settings.DeviceKey;
|
import org.cryptomator.common.settings.DeviceKey;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
|
||||||
import org.cryptomator.cryptolib.common.MessageDigestSupplier;
|
import org.cryptomator.cryptolib.common.MessageDigestSupplier;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxControllerKey;
|
import org.cryptomator.ui.common.FxControllerKey;
|
||||||
@@ -15,13 +14,12 @@ import org.cryptomator.ui.common.FxmlFile;
|
|||||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||||
|
import org.cryptomator.ui.keyloading.KeyLoadingRef;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UncheckedIOException;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@@ -32,19 +30,15 @@ public abstract class HubKeyLoadingModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
static HubConfig provideHubConfig(@KeyLoading Vault vault) {
|
static HubConfig provideHubConfig(@KeyLoading KeyLoadingRef vaultRef) {
|
||||||
try {
|
return vaultRef.vaultConfig().getHeader("hub", HubConfig.class);
|
||||||
return vault.getVaultConfigCache().get().getHeader("hub", HubConfig.class);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new UncheckedIOException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
@Named("windowTitle")
|
@Named("windowTitle")
|
||||||
static String provideWindowTitle(@KeyLoading Vault vault, ResourceBundle resourceBundle) {
|
static String provideWindowTitle(@KeyLoading KeyLoadingRef vaultRef, ResourceBundle resourceBundle) {
|
||||||
return String.format(resourceBundle.getString("unlock.title"), vault.getDisplayName());
|
return String.format(resourceBundle.getString("unlock.title"), vaultRef.displayName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package org.cryptomator.ui.keyloading.hub;
|
||||||
|
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.cryptofs.VaultConfig;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates Hub vaults among the vaults set up on this machine.
|
||||||
|
* <p>
|
||||||
|
* Hub manages a vault's key, not where it lives or how it is laid out, so the local vault list is the only place that
|
||||||
|
* can answer whether a given Hub vault is already set up here.
|
||||||
|
*/
|
||||||
|
public final class HubVaults {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(HubVaults.class);
|
||||||
|
|
||||||
|
private HubVaults() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the Hub vault with the given id.
|
||||||
|
* <p>
|
||||||
|
* A vault whose config cannot be read - e.g. because it sits on storage that is currently unavailable - is skipped
|
||||||
|
* rather than failing the lookup: one unreachable vault must not prevent finding a different one.
|
||||||
|
*
|
||||||
|
* @param vaults the vaults set up on this machine
|
||||||
|
* @param hubVaultId the vault's id within its Hub instance
|
||||||
|
* @return the local vault, or empty if none of them is that Hub vault
|
||||||
|
*/
|
||||||
|
public static Optional<Vault> findByVaultId(Collection<Vault> vaults, UUID hubVaultId) {
|
||||||
|
return vaults.stream() //
|
||||||
|
.filter(vault -> hasVaultId(vault, hubVaultId)) //
|
||||||
|
.findAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasVaultId(Vault vault, UUID hubVaultId) {
|
||||||
|
try {
|
||||||
|
return hasVaultId(vault.getVaultConfigCache().get(), hubVaultId);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.debug("Skipping vault {} while looking for hub vault {}, its config is not readable.", vault.getPath(), hubVaultId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasVaultId(VaultConfig.UnverifiedVaultConfig config, UUID hubVaultId) {
|
||||||
|
var keyIdScheme = config.getKeyId().getScheme();
|
||||||
|
if (keyIdScheme == null || !keyIdScheme.startsWith(HubKeyLoadingStrategy.SCHEME_PREFIX)) {
|
||||||
|
return false; //not a hub vault, so it cannot be the one we are looking for
|
||||||
|
}
|
||||||
|
return hubVaultId.toString().equalsIgnoreCase(config.allegedVaultId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,12 +7,12 @@ import com.google.common.base.Preconditions;
|
|||||||
import com.nimbusds.jose.JWEObject;
|
import com.nimbusds.jose.JWEObject;
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.cryptomator.common.Constants;
|
import org.cryptomator.common.Constants;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.dialogs.Dialogs;
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||||
|
import org.cryptomator.ui.keyloading.KeyLoadingRef;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -60,10 +60,10 @@ public class ReceiveKeyController implements FxController {
|
|||||||
private final Lazy<Scene> invalidLicenseScene;
|
private final Lazy<Scene> invalidLicenseScene;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
private final Dialogs dialogs;
|
private final Dialogs dialogs;
|
||||||
private final Vault vault;
|
private final KeyLoadingRef vaultRef;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ReceiveKeyController(@KeyLoading Vault vault, //
|
public ReceiveKeyController(@KeyLoading KeyLoadingRef vaultRef, //
|
||||||
ExecutorService executor, //
|
ExecutorService executor, //
|
||||||
@KeyLoading Stage window, //
|
@KeyLoading Stage window, //
|
||||||
HubConfig hubConfig, //
|
HubConfig hubConfig, //
|
||||||
@@ -79,7 +79,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
Dialogs dialogs) {
|
Dialogs dialogs) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.hubConfig = hubConfig;
|
this.hubConfig = hubConfig;
|
||||||
this.vaultId = extractVaultId(vault.getVaultConfigCache().getUnchecked().getKeyId()); // TODO: access vault config's JTI directly (requires changes in cryptofs)
|
this.vaultId = vaultRef.vaultId();
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.bearerToken = Objects.requireNonNull(tokenRef.get());
|
this.bearerToken = Objects.requireNonNull(tokenRef.get());
|
||||||
this.fsOwnerId = fsOwnerId;
|
this.fsOwnerId = fsOwnerId;
|
||||||
@@ -92,7 +92,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed);
|
||||||
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
this.httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).executor(executor).build();
|
||||||
this.dialogs = dialogs;
|
this.dialogs = dialogs;
|
||||||
this.vault = vault;
|
this.vaultRef = vaultRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -313,7 +313,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
|
|
||||||
private void accessGoneVaultArchived() {
|
private void accessGoneVaultArchived() {
|
||||||
window.close();
|
window.close();
|
||||||
dialogs.prepareHubVaultArchived((Stage)window.getOwner(), vault).build().showAndWait();
|
dialogs.prepareHubVaultArchived((Stage)window.getOwner(), vaultRef.displayName()).build().showAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void accountInitializationRequired() {
|
private void accountInitializationRequired() {
|
||||||
@@ -343,12 +343,6 @@ public class ReceiveKeyController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String extractVaultId(URI vaultKeyUri) {
|
|
||||||
assert vaultKeyUri.getScheme().startsWith(HubKeyLoadingStrategy.SCHEME_PREFIX);
|
|
||||||
var path = vaultKeyUri.getPath();
|
|
||||||
return path.substring(path.lastIndexOf('/') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
private record UserDto(@JsonProperty(value = "name", required = true) String name) {}
|
private record UserDto(@JsonProperty(value = "name", required = true) String name) {}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -1,5 +1,6 @@
|
|||||||
package org.cryptomator.ui.keyloading.masterkeyfile;
|
package org.cryptomator.ui.keyloading.masterkeyfile;
|
||||||
|
|
||||||
|
import org.cryptomator.common.Nullable;
|
||||||
import org.cryptomator.common.recovery.RecoveryActionType;
|
import org.cryptomator.common.recovery.RecoveryActionType;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
@@ -18,6 +19,7 @@ import javafx.stage.Stage;
|
|||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@@ -41,12 +43,12 @@ public class ChooseMasterkeyFileController implements FxController {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ChooseMasterkeyFileController(@KeyLoading Stage window, //
|
public ChooseMasterkeyFileController(@KeyLoading Stage window, //
|
||||||
@KeyLoading Vault vault, //
|
@KeyLoading @Nullable Vault vault, //
|
||||||
CompletableFuture<Path> result, //
|
CompletableFuture<Path> result, //
|
||||||
RecoveryKeyComponent.Factory recoveryKeyWindow, //
|
RecoveryKeyComponent.Factory recoveryKeyWindow, //
|
||||||
ResourceBundle resourceBundle) {
|
ResourceBundle resourceBundle) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.vault = vault;
|
this.vault = Objects.requireNonNull(vault, MasterkeyFileLoadingModule.NO_LOCAL_VAULT);
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.recoveryKeyWindow = recoveryKeyWindow;
|
this.recoveryKeyWindow = recoveryKeyWindow;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
|
|||||||
+10
-2
@@ -5,6 +5,7 @@ import dagger.Module;
|
|||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import dagger.multibindings.IntoMap;
|
import dagger.multibindings.IntoMap;
|
||||||
import dagger.multibindings.StringKey;
|
import dagger.multibindings.StringKey;
|
||||||
|
import org.cryptomator.common.Nullable;
|
||||||
import org.cryptomator.common.keychain.KeychainManager;
|
import org.cryptomator.common.keychain.KeychainManager;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
@@ -15,20 +16,27 @@ import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Module(subcomponents = {ForgetPasswordComponent.class, PassphraseEntryComponent.class, ChooseMasterkeyFileComponent.class})
|
@Module(subcomponents = {ForgetPasswordComponent.class, PassphraseEntryComponent.class, ChooseMasterkeyFileComponent.class})
|
||||||
public interface MasterkeyFileLoadingModule {
|
public interface MasterkeyFileLoadingModule {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key loading may run for a yet-to-setup vault (i.e. deeplink with only a config) - Masterkey loading requires in the current implementation
|
||||||
|
* an already setup vault.
|
||||||
|
*/
|
||||||
|
String NO_LOCAL_VAULT = "masterkey file loading requires a local vault";
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Named("savedPassword")
|
@Named("savedPassword")
|
||||||
@KeyLoadingScoped
|
@KeyLoadingScoped
|
||||||
static Optional<char[]> provideStoredPassword(KeychainManager keychain, @KeyLoading Vault vault) {
|
static Optional<char[]> provideStoredPassword(KeychainManager keychain, @KeyLoading @Nullable Vault vault) {
|
||||||
if (!keychain.isSupported() || keychain.isLocked()) {
|
if (!keychain.isSupported() || keychain.isLocked()) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return Optional.ofNullable(keychain.loadPassphrase(vault.getId()));
|
return Optional.ofNullable(keychain.loadPassphrase(Objects.requireNonNull(vault, NO_LOCAL_VAULT).getId()));
|
||||||
} catch (KeychainAccessException e) {
|
} catch (KeychainAccessException e) {
|
||||||
LoggerFactory.getLogger(MasterkeyFileLoadingModule.class).error("Failed to load entry from system keychain.", e);
|
LoggerFactory.getLogger(MasterkeyFileLoadingModule.class).error("Failed to load entry from system keychain.", e);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
|||||||
+4
-2
@@ -2,6 +2,7 @@ package org.cryptomator.ui.keyloading.masterkeyfile;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.cryptomator.common.Constants;
|
import org.cryptomator.common.Constants;
|
||||||
|
import org.cryptomator.common.Nullable;
|
||||||
import org.cryptomator.common.Passphrase;
|
import org.cryptomator.common.Passphrase;
|
||||||
import org.cryptomator.common.keychain.KeychainManager;
|
import org.cryptomator.common.keychain.KeychainManager;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
@@ -25,6 +26,7 @@ import java.io.IOException;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
@@ -48,8 +50,8 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
|
|||||||
private boolean wrongPassphrase;
|
private boolean wrongPassphrase;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MasterkeyFileLoadingStrategy(@KeyLoading Vault vault, MasterkeyFileAccess masterkeyFileAccess, @KeyLoading Stage window, @Named("savedPassword") Optional<char[]> savedPassphrase, PassphraseEntryComponent.Builder passphraseEntry, ChooseMasterkeyFileComponent.Builder masterkeyFileChoice, KeychainManager keychain, ResourceBundle resourceBundle) {
|
public MasterkeyFileLoadingStrategy(@KeyLoading @Nullable Vault vault, MasterkeyFileAccess masterkeyFileAccess, @KeyLoading Stage window, @Named("savedPassword") Optional<char[]> savedPassphrase, PassphraseEntryComponent.Builder passphraseEntry, ChooseMasterkeyFileComponent.Builder masterkeyFileChoice, KeychainManager keychain, ResourceBundle resourceBundle) {
|
||||||
this.vault = vault;
|
this.vault = Objects.requireNonNull(vault, MasterkeyFileLoadingModule.NO_LOCAL_VAULT);
|
||||||
this.masterkeyFileAccess = masterkeyFileAccess;
|
this.masterkeyFileAccess = masterkeyFileAccess;
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.passphraseEntry = passphraseEntry;
|
this.passphraseEntry = passphraseEntry;
|
||||||
|
|||||||
+3
-2
@@ -35,6 +35,7 @@ import javafx.scene.transform.Translate;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
@@ -66,9 +67,9 @@ public class PassphraseEntryController implements FxController {
|
|||||||
public Animation unlockAnimation;
|
public Animation unlockAnimation;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public PassphraseEntryController(@KeyLoading Stage window, @KeyLoading Vault vault, CompletableFuture<PassphraseEntryResult> result, @Nullable @Named("savedPassword") Passphrase savedPassword, ForgetPasswordComponent.Builder forgetPassword, KeychainManager keychain, ExecutorService backgroundExecutorService) {
|
public PassphraseEntryController(@KeyLoading Stage window, @KeyLoading @Nullable Vault vault, CompletableFuture<PassphraseEntryResult> result, @Nullable @Named("savedPassword") Passphrase savedPassword, ForgetPasswordComponent.Builder forgetPassword, KeychainManager keychain, ExecutorService backgroundExecutorService) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.vault = vault;
|
this.vault = Objects.requireNonNull(vault, MasterkeyFileLoadingModule.NO_LOCAL_VAULT);
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.savedPassword = savedPassword;
|
this.savedPassword = savedPassword;
|
||||||
this.forgetPassword = forgetPassword;
|
this.forgetPassword = forgetPassword;
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package org.cryptomator.ui.mainwindow;
|
package org.cryptomator.ui.mainwindow;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import com.google.common.cache.CacheLoader;
|
|
||||||
import com.google.common.cache.LoadingCache;
|
|
||||||
import com.tobiasdiez.easybind.EasyBind;
|
import com.tobiasdiez.easybind.EasyBind;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Nullable;
|
import org.cryptomator.common.Nullable;
|
||||||
@@ -58,6 +57,7 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
private final DecryptNameComponent.Factory decryptNameWindowFactory;
|
private final DecryptNameComponent.Factory decryptNameWindowFactory;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
|
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
|
||||||
|
private final LoadingCache<Vault, DecryptNameComponent> decryptNameWindows;
|
||||||
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
|
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
|
||||||
private final ObservableValue<Boolean> accessibleViaPath;
|
private final ObservableValue<Boolean> accessibleViaPath;
|
||||||
private final ObservableValue<Boolean> accessibleViaUri;
|
private final ObservableValue<Boolean> accessibleViaUri;
|
||||||
@@ -89,7 +89,8 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
this.revealPathService = revealPathService;
|
this.revealPathService = revealPathService;
|
||||||
this.decryptNameWindowFactory = decryptNameWindowFactory;
|
this.decryptNameWindowFactory = decryptNameWindowFactory;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
|
this.vaultStats = Caffeine.newBuilder().weakValues().build(this::buildVaultStats);
|
||||||
|
this.decryptNameWindows = Caffeine.newBuilder().weakValues().build(this::buildDecryptNameWindow);
|
||||||
this.vaultStatsBuilder = vaultStatsBuilder;
|
this.vaultStatsBuilder = vaultStatsBuilder;
|
||||||
var mp = vault.flatMap(Vault::mountPointProperty);
|
var mp = vault.flatMap(Vault::mountPointProperty);
|
||||||
this.accessibleViaPath = mp.map(m -> m instanceof Mountpoint.WithPath).orElse(false);
|
this.accessibleViaPath = mp.map(m -> m instanceof Mountpoint.WithPath).orElse(false);
|
||||||
@@ -161,7 +162,7 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showDecryptNameWindow(List<Path> pathsToDecrypt) {
|
private void showDecryptNameWindow(List<Path> pathsToDecrypt) {
|
||||||
decryptNameWindowFactory.create(vault.get(), mainWindow, pathsToDecrypt).showDecryptFileNameWindow();
|
decryptNameWindows.get(vault.get()).showDecryptFileNameWindow(pathsToDecrypt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean startsWithVaultAccessPoint(Path path) {
|
private boolean startsWithVaultAccessPoint(Path path) {
|
||||||
@@ -198,6 +199,10 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
return vaultStatsBuilder.vault(vault).build();
|
return vaultStatsBuilder.vault(vault).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DecryptNameComponent buildDecryptNameWindow(Vault vault) {
|
||||||
|
return decryptNameWindowFactory.create(vault, mainWindow);
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void revealAccessLocation() {
|
public void revealAccessLocation() {
|
||||||
vaultService.reveal(vault.get());
|
vaultService.reveal(vault.get());
|
||||||
@@ -217,7 +222,7 @@ public class VaultDetailUnlockedController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void showVaultStatistics() {
|
public void showVaultStatistics() {
|
||||||
vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
|
vaultStats.get(vault.get()).showVaultStatisticsWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import org.apache.commons.lang3.SystemUtils;
|
|||||||
import org.cryptomator.common.recovery.RecoveryActionType;
|
import org.cryptomator.common.recovery.RecoveryActionType;
|
||||||
import org.cryptomator.common.recovery.VaultPreparator;
|
import org.cryptomator.common.recovery.VaultPreparator;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.vaults.NotAVaultDirectoryException;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultComponent;
|
import org.cryptomator.common.vaults.VaultComponent;
|
||||||
import org.cryptomator.common.vaults.VaultListManager;
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
@@ -23,6 +24,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.BooleanBinding;
|
import javafx.beans.binding.BooleanBinding;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
@@ -55,6 +57,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
|
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
|
||||||
@@ -90,6 +93,7 @@ public class VaultListController implements FxController {
|
|||||||
private final VaultComponent.Factory vaultComponentFactory;
|
private final VaultComponent.Factory vaultComponentFactory;
|
||||||
private final RecoveryKeyComponent.Factory recoveryKeyWindow;
|
private final RecoveryKeyComponent.Factory recoveryKeyWindow;
|
||||||
private final List<MountService> mountServices;
|
private final List<MountService> mountServices;
|
||||||
|
private final ExecutorService executor;
|
||||||
|
|
||||||
public ListView<Vault> vaultList;
|
public ListView<Vault> vaultList;
|
||||||
public StackPane root;
|
public StackPane root;
|
||||||
@@ -113,7 +117,8 @@ public class VaultListController implements FxController {
|
|||||||
RecoveryKeyComponent.Factory recoveryKeyWindow, //
|
RecoveryKeyComponent.Factory recoveryKeyWindow, //
|
||||||
VaultComponent.Factory vaultComponentFactory, //
|
VaultComponent.Factory vaultComponentFactory, //
|
||||||
List<MountService> mountServices, //
|
List<MountService> mountServices, //
|
||||||
FxFSEventList fxFSEventList) {
|
FxFSEventList fxFSEventList, //
|
||||||
|
ExecutorService executor) {
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
this.vaults = vaults;
|
this.vaults = vaults;
|
||||||
this.selectedVault = selectedVault;
|
this.selectedVault = selectedVault;
|
||||||
@@ -127,6 +132,7 @@ public class VaultListController implements FxController {
|
|||||||
this.recoveryKeyWindow = recoveryKeyWindow;
|
this.recoveryKeyWindow = recoveryKeyWindow;
|
||||||
this.vaultComponentFactory = vaultComponentFactory;
|
this.vaultComponentFactory = vaultComponentFactory;
|
||||||
this.mountServices = mountServices;
|
this.mountServices = mountServices;
|
||||||
|
this.executor = executor;
|
||||||
|
|
||||||
this.emptyVaultList = Bindings.isEmpty(vaults);
|
this.emptyVaultList = Bindings.isEmpty(vaults);
|
||||||
this.unreadEvents = fxFSEventList.unreadEventsProperty();
|
this.unreadEvents = fxFSEventList.unreadEventsProperty();
|
||||||
@@ -324,15 +330,18 @@ public class VaultListController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addVault(Path pathToVault) {
|
private void addVault(Path pathToVault) {
|
||||||
try {
|
Path target = pathToVault.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT) ? pathToVault.getParent() : pathToVault;
|
||||||
if (pathToVault.getFileName().toString().endsWith(CRYPTOMATOR_FILENAME_EXT)) {
|
executor.execute(() -> {
|
||||||
vaultListManager.add(pathToVault.getParent());
|
try {
|
||||||
} else {
|
vaultListManager.add(target);
|
||||||
vaultListManager.add(pathToVault);
|
} catch (NotAVaultDirectoryException e) {
|
||||||
|
LOG.warn("Cannot add {}: {}", target, e.getMessage());
|
||||||
|
Platform.runLater(() -> dialogs.prepareNotAVaultDirectoryDialog(mainWindow, e).build().showAndWait());
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.warn("Failed to add vault {}", target, e);
|
||||||
|
Platform.runLater(() -> appWindows.showErrorWindow(e, mainWindow, null));
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
});
|
||||||
LOG.debug("Not a vault: {}", pathToVault);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ import java.net.URISyntaxException;
|
|||||||
public class ShareVaultController implements FxController {
|
public class ShareVaultController implements FxController {
|
||||||
|
|
||||||
private static final String SCHEME_PREFIX = "hub+";
|
private static final String SCHEME_PREFIX = "hub+";
|
||||||
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/";
|
private static final String VISIT_HUB_URL = "https://cryptomator.org/hub/" //
|
||||||
|
+ "?utm_source=cryptomator-desktop" //
|
||||||
|
+ "&utm_medium=app" //
|
||||||
|
+ "&utm_campaign=share-vault";
|
||||||
private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/security/best-practices/#sharing-of-vaults";
|
private static final String BEST_PRACTICES_URL = "https://docs.cryptomator.org/security/best-practices/#sharing-of-vaults";
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ public class TrayMenuBuilder {
|
|||||||
vaults.addListener(this::vaultListChanged);
|
vaults.addListener(this::vaultListChanged);
|
||||||
vaults.forEach(v -> {
|
vaults.forEach(v -> {
|
||||||
v.displayNameProperty().addListener(this::vaultListChanged);
|
v.displayNameProperty().addListener(this::vaultListChanged);
|
||||||
v.stateProperty().addListener(this::vaultListChanged);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -77,9 +76,7 @@ public class TrayMenuBuilder {
|
|||||||
for (Vault vault : vaults) {
|
for (Vault vault : vaults) {
|
||||||
VaultListManager.redetermineVaultState(vault);
|
VaultListManager.redetermineVaultState(vault);
|
||||||
}
|
}
|
||||||
rebuildMenu();
|
|
||||||
});
|
});
|
||||||
vaults.forEach(VaultListManager::redetermineVaultState);
|
|
||||||
rebuildMenu();
|
rebuildMenu();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
} catch (TrayMenuException e) {
|
} catch (TrayMenuException e) {
|
||||||
@@ -125,27 +122,18 @@ public class TrayMenuBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<TrayMenuItem> buildSubmenu(Vault vault) {
|
private List<TrayMenuItem> buildSubmenu(Vault vault) {
|
||||||
return switch (vault.getState()) {
|
if (vault.isLocked()) {
|
||||||
case LOCKED -> List.of( //
|
return List.of( //
|
||||||
new ActionItem(resourceBundle.getString("traymenu.vault.unlock"), () -> this.unlockVault(vault)) //
|
new ActionItem(resourceBundle.getString("traymenu.vault.unlock"), () -> this.unlockVault(vault)) //
|
||||||
);
|
);
|
||||||
case UNLOCKED -> List.of( //
|
} else if (vault.isUnlocked()) {
|
||||||
|
return List.of( //
|
||||||
new ActionItem(resourceBundle.getString("traymenu.vault.lock"), () -> this.lockVault(vault)), //
|
new ActionItem(resourceBundle.getString("traymenu.vault.lock"), () -> this.lockVault(vault)), //
|
||||||
new ActionItem(resourceBundle.getString("traymenu.vault.reveal"), () -> this.revealVault(vault)) //
|
new ActionItem(resourceBundle.getString("traymenu.vault.reveal"), () -> this.revealVault(vault)) //
|
||||||
);
|
);
|
||||||
case PROCESSING -> List.of( //
|
} else {
|
||||||
new ActionItem(resourceBundle.getString("vault.state.processing"), () -> {}, false) //
|
return List.of();
|
||||||
);
|
}
|
||||||
case MISSING, VAULT_CONFIG_MISSING, ALL_MISSING -> List.of( //
|
|
||||||
new ActionItem(resourceBundle.getString("vault.state.missing"), () -> {}, false) //
|
|
||||||
);
|
|
||||||
case NEEDS_MIGRATION -> List.of( //
|
|
||||||
new ActionItem(resourceBundle.getString("vault.state.migrationNeeded"), () -> {}, false) //
|
|
||||||
);
|
|
||||||
case ERROR -> List.of( //
|
|
||||||
new ActionItem(resourceBundle.getString("vault.state.error"), () -> {}, false) //
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* action listeners: */
|
/* action listeners: */
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.cryptomator.ui.common.FxmlLoaderFactory;
|
|||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.common.StageFactory;
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingComponent;
|
import org.cryptomator.ui.keyloading.KeyLoadingComponent;
|
||||||
|
import org.cryptomator.ui.keyloading.KeyLoadingRef;
|
||||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -25,6 +26,7 @@ import javafx.beans.property.SimpleObjectProperty;
|
|||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@@ -58,7 +60,11 @@ abstract class UnlockModule {
|
|||||||
@UnlockWindow
|
@UnlockWindow
|
||||||
@UnlockScoped
|
@UnlockScoped
|
||||||
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @UnlockWindow Vault vault, @UnlockWindow Stage window) {
|
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @UnlockWindow Vault vault, @UnlockWindow Stage window) {
|
||||||
return compFactory.create(vault, window).keyloadingStrategy();
|
try {
|
||||||
|
return compFactory.create(KeyLoadingRef.forVault(vault), vault, window).keyloadingStrategy();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return KeyLoadingStrategy.failed(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
</ImageView>
|
</ImageView>
|
||||||
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
|
<VBox spacing="3" HBox.hgrow="ALWAYS" alignment="CENTER_LEFT">
|
||||||
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
|
<FormattedLabel styleClass="label-extra-large" format="Cryptomator %s" arg1="${controller.fullApplicationVersion}"/>
|
||||||
<Label text="© 2016 – 2025 Skymatic GmbH"/>
|
<Label text="© 2016 – 2026 Skymatic GmbH"/>
|
||||||
</VBox>
|
</VBox>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
||||||
|
|||||||
@@ -9,90 +9,94 @@
|
|||||||
<?import javafx.scene.control.Hyperlink?>
|
<?import javafx.scene.control.Hyperlink?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.RadioButton?>
|
<?import javafx.scene.control.RadioButton?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
<?import javafx.scene.control.TextField?>
|
<?import javafx.scene.control.TextField?>
|
||||||
<?import javafx.scene.control.ToggleGroup?>
|
<?import javafx.scene.control.ToggleGroup?>
|
||||||
<?import javafx.scene.control.Tooltip?>
|
<?import javafx.scene.control.Tooltip?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<ScrollPane xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
|
fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
|
||||||
spacing="6">
|
fitToWidth="true"
|
||||||
<fx:define>
|
hbarPolicy="NEVER">
|
||||||
<ToggleGroup fx:id="mountPointToggleGroup"/>
|
<VBox spacing="6">
|
||||||
</fx:define>
|
<fx:define>
|
||||||
<padding>
|
<ToggleGroup fx:id="mountPointToggleGroup"/>
|
||||||
<Insets topRightBottomLeft="12"/>
|
</fx:define>
|
||||||
</padding>
|
<padding>
|
||||||
<children>
|
<Insets topRightBottomLeft="12"/>
|
||||||
<HBox spacing="12" alignment="CENTER_LEFT">
|
</padding>
|
||||||
<Label text="%vaultOptions.mount.volume.type" labelFor="$vaultVolumeTypeChoiceBox"/>
|
<children>
|
||||||
<ChoiceBox fx:id="vaultVolumeTypeChoiceBox"/>
|
<HBox spacing="12" alignment="CENTER_LEFT">
|
||||||
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}" accessibleText="%vaultOptions.mount.info">
|
<Label text="%vaultOptions.mount.volume.type" labelFor="$vaultVolumeTypeChoiceBox"/>
|
||||||
<graphic>
|
<ChoiceBox fx:id="vaultVolumeTypeChoiceBox"/>
|
||||||
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
|
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openVolumePreferences" visible="${controller.defaultMountServiceSelected}" managed="${controller.defaultMountServiceSelected}" accessibleText="%vaultOptions.mount.info">
|
||||||
</graphic>
|
|
||||||
<tooltip>
|
|
||||||
<Tooltip text="%vaultOptions.mount.info" showDelay="100ms"/>
|
|
||||||
</tooltip>
|
|
||||||
</Hyperlink>
|
|
||||||
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" visible="${!controller.defaultMountServiceSelected}" managed="${!controller.defaultMountServiceSelected}" accessibleText="%preferences.volume.docsTooltip">
|
|
||||||
<graphic>
|
|
||||||
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
|
|
||||||
</graphic>
|
|
||||||
<tooltip>
|
|
||||||
<Tooltip text="%preferences.volume.docsTooltip" showDelay="100ms"/>
|
|
||||||
</tooltip>
|
|
||||||
</Hyperlink>
|
|
||||||
</HBox>
|
|
||||||
|
|
||||||
<Label styleClass="label-red" text="%vaultOptions.mount.volumeType.restartRequired" visible="${controller.selectedMountServiceRequiresRestart}" managed="${controller.selectedMountServiceRequiresRestart}"/>
|
|
||||||
|
|
||||||
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortChangeable}" managed="${controller.loopbackPortChangeable}">
|
|
||||||
<Label text="%vaultOptions.mount.volume.tcp.port" labelFor="$vaultLoopbackPortField"/>
|
|
||||||
<NumericTextField fx:id="vaultLoopbackPortField"/>
|
|
||||||
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
|
|
||||||
</HBox>
|
|
||||||
|
|
||||||
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlyOptionAllowed}" managed="${controller.readOnlyOptionAllowed}"/>
|
|
||||||
|
|
||||||
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
|
|
||||||
<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/>
|
|
||||||
<TextField fx:id="mountFlagsField" HBox.hgrow="ALWAYS" maxWidth="Infinity" accessibleText="%vaultOptions.mount.customMountFlags">
|
|
||||||
<VBox.margin>
|
|
||||||
<Insets left="24"/>
|
|
||||||
</VBox.margin>
|
|
||||||
</TextField>
|
|
||||||
</VBox>
|
|
||||||
|
|
||||||
<Label text="%vaultOptions.mount.mountPoint">
|
|
||||||
<VBox.margin>
|
|
||||||
<Insets top="9"/>
|
|
||||||
</VBox.margin>
|
|
||||||
</Label>
|
|
||||||
|
|
||||||
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointAutoBtn" text="%vaultOptions.mount.mountPoint.auto"/>
|
|
||||||
|
|
||||||
<HBox spacing="6" visible="${controller.mountpointDriveLetterSupported}" managed="${controller.mountpointDriveLetterSupported}">
|
|
||||||
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointDriveLetterBtn" text="%vaultOptions.mount.mountPoint.driveLetter"/>
|
|
||||||
<ChoiceBox fx:id="driveLetterSelection" disable="${!mountPointDriveLetterBtn.selected}" accessibleText="%vaultOptions.mount.mountPoint.driveLetter"/>
|
|
||||||
</HBox>
|
|
||||||
|
|
||||||
<VBox spacing="6" visible="${controller.mountpointDirSupported}" managed="${controller.mountpointDirSupported}">
|
|
||||||
<HBox spacing="6" alignment="CENTER_LEFT">
|
|
||||||
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointDirBtn" text="%vaultOptions.mount.mountPoint.custom"/>
|
|
||||||
<Button text="%vaultOptions.mount.mountPoint.directoryPickerButton" onAction="#chooseCustomMountPoint" contentDisplay="LEFT" disable="${!mountPointDirBtn.selected}">
|
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="FOLDER_OPEN" glyphSize="15"/>
|
<FontAwesome5IconView glyph="COGS" styleClass="glyph-icon-muted"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
</Button>
|
<tooltip>
|
||||||
|
<Tooltip text="%vaultOptions.mount.info" showDelay="100ms"/>
|
||||||
|
</tooltip>
|
||||||
|
</Hyperlink>
|
||||||
|
<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs" visible="${!controller.defaultMountServiceSelected}" managed="${!controller.defaultMountServiceSelected}" accessibleText="%preferences.volume.docsTooltip">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
|
||||||
|
</graphic>
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%preferences.volume.docsTooltip" showDelay="100ms"/>
|
||||||
|
</tooltip>
|
||||||
|
</Hyperlink>
|
||||||
</HBox>
|
</HBox>
|
||||||
<TextField fx:id="directoryPathField" text="${controller.directoryPath}" visible="${mountPointDirBtn.selected}" managed="${mountPointDirBtn.managed}" maxWidth="Infinity" editable="false" accessibleText="%vaultOptions.mount.mountPoint.custom">
|
|
||||||
<VBox.margin>
|
|
||||||
<Insets left="24"/>
|
|
||||||
</VBox.margin>
|
|
||||||
</TextField>
|
|
||||||
</VBox>
|
|
||||||
</children>
|
|
||||||
|
|
||||||
</VBox>
|
<Label styleClass="label-red" text="%vaultOptions.mount.volumeType.restartRequired" visible="${controller.selectedMountServiceRequiresRestart}" managed="${controller.selectedMountServiceRequiresRestart}"/>
|
||||||
|
|
||||||
|
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortChangeable}" managed="${controller.loopbackPortChangeable}">
|
||||||
|
<Label text="%vaultOptions.mount.volume.tcp.port" labelFor="$vaultLoopbackPortField"/>
|
||||||
|
<NumericTextField fx:id="vaultLoopbackPortField"/>
|
||||||
|
<Button text="%generic.button.apply" fx:id="vaultLoopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlyOptionAllowed}" managed="${controller.readOnlyOptionAllowed}"/>
|
||||||
|
|
||||||
|
<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">
|
||||||
|
<CheckBox fx:id="customMountFlagsCheckbox" text="%vaultOptions.mount.customMountFlags" onAction="#toggleUseCustomMountFlags"/>
|
||||||
|
<TextField fx:id="mountFlagsField" HBox.hgrow="ALWAYS" maxWidth="Infinity" accessibleText="%vaultOptions.mount.customMountFlags">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets left="24"/>
|
||||||
|
</VBox.margin>
|
||||||
|
</TextField>
|
||||||
|
</VBox>
|
||||||
|
|
||||||
|
<Label text="%vaultOptions.mount.mountPoint">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets top="9"/>
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointAutoBtn" text="%vaultOptions.mount.mountPoint.auto"/>
|
||||||
|
|
||||||
|
<HBox spacing="6" visible="${controller.mountpointDriveLetterSupported}" managed="${controller.mountpointDriveLetterSupported}">
|
||||||
|
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointDriveLetterBtn" text="%vaultOptions.mount.mountPoint.driveLetter"/>
|
||||||
|
<ChoiceBox fx:id="driveLetterSelection" disable="${!mountPointDriveLetterBtn.selected}" accessibleText="%vaultOptions.mount.mountPoint.driveLetter"/>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<VBox spacing="6" visible="${controller.mountpointDirSupported}" managed="${controller.mountpointDirSupported}">
|
||||||
|
<HBox spacing="6" alignment="CENTER_LEFT">
|
||||||
|
<RadioButton toggleGroup="${mountPointToggleGroup}" fx:id="mountPointDirBtn" text="%vaultOptions.mount.mountPoint.custom"/>
|
||||||
|
<Button text="%vaultOptions.mount.mountPoint.directoryPickerButton" onAction="#chooseCustomMountPoint" contentDisplay="LEFT" disable="${!mountPointDirBtn.selected}">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="FOLDER_OPEN" glyphSize="15"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</HBox>
|
||||||
|
<TextField fx:id="directoryPathField" text="${controller.directoryPath}" visible="${mountPointDirBtn.selected}" managed="${mountPointDirBtn.managed}" maxWidth="Infinity" editable="false" accessibleText="%vaultOptions.mount.mountPoint.custom">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets left="24"/>
|
||||||
|
</VBox.margin>
|
||||||
|
</TextField>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
|
||||||
|
</VBox>
|
||||||
|
</ScrollPane>
|
||||||
|
|||||||
@@ -110,6 +110,13 @@ addvaultwizard.existing.restore=Restore…
|
|||||||
addvaultwizard.existing.chooseBtn=Choose…
|
addvaultwizard.existing.chooseBtn=Choose…
|
||||||
addvaultwizard.existing.filePickerTitle=Select Vault File
|
addvaultwizard.existing.filePickerTitle=Select Vault File
|
||||||
addvaultwizard.existing.filePickerMimeDesc=Cryptomator Vault
|
addvaultwizard.existing.filePickerMimeDesc=Cryptomator Vault
|
||||||
|
addvaultwizard.existing.notAVault.title=Not a Vault
|
||||||
|
addvaultwizard.existing.notAVault.message=The selected folder is not a Cryptomator vault
|
||||||
|
addvaultwizard.existing.notAVault.description.missingDataDir=The required "d" subdirectory is missing inside "%s".
|
||||||
|
addvaultwizard.existing.notAVault.description.dataNotADirectory=The "d" entry inside "%s" is not a directory.
|
||||||
|
addvaultwizard.existing.notAVault.description.missingVaultConfig=The required "vault.cryptomator" file is missing inside "%s".
|
||||||
|
addvaultwizard.existing.notAVault.description.vaultConfigAccessDenied=File "vault.cryptomator" inside "%s" cannot be read due to insufficient access rights.
|
||||||
|
addvaultwizard.existing.notAVault.description.unsupportedStructure=The directory structure of "%s" is not supported.
|
||||||
## Success
|
## Success
|
||||||
addvaultwizard.success.nextStepsInstructions=Added vault "%s".\nYou need to unlock this vault to access or add contents. Alternatively you can unlock it at any later point in time.
|
addvaultwizard.success.nextStepsInstructions=Added vault "%s".\nYou need to unlock this vault to access or add contents. Alternatively you can unlock it at any later point in time.
|
||||||
addvaultwizard.success.unlockNow=Unlock Now
|
addvaultwizard.success.unlockNow=Unlock Now
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
## Success
|
## Success
|
||||||
## Failure
|
## Failure
|
||||||
## Hub
|
## Hub
|
||||||
|
### Check Host Authenticity
|
||||||
### Waiting
|
### Waiting
|
||||||
### Receive Key
|
### Receive Key
|
||||||
### Register Device
|
### Register Device
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
### Registration Failed
|
### Registration Failed
|
||||||
### Archived
|
### Archived
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
### License Exceeded
|
### License Exceeded
|
||||||
|
|
||||||
@@ -154,4 +156,4 @@
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ unlock.error.title=فشل فتح "%s"
|
|||||||
hub.noKeychain.message=غير قادر على الوصول إلى مفتاح الجهاز
|
hub.noKeychain.message=غير قادر على الوصول إلى مفتاح الجهاز
|
||||||
hub.noKeychain.description=من أجل فتح مركز مخازن، مطلوب مفتاح جهاز، يتم تأمينه باستخدام سلسلة مفاتيح. للمضي قدما، قم بتمكين "%s" واختر سلسلة مفاتيح في التفضيلات.
|
hub.noKeychain.description=من أجل فتح مركز مخازن، مطلوب مفتاح جهاز، يتم تأمينه باستخدام سلسلة مفاتيح. للمضي قدما، قم بتمكين "%s" واختر سلسلة مفاتيح في التفضيلات.
|
||||||
hub.noKeychain.openBtn=فتح التفضيلات
|
hub.noKeychain.openBtn=فتح التفضيلات
|
||||||
|
### Check Host Authenticity
|
||||||
|
hub.checkHostTrust.trustBtn=ثق
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=جاري الانتظار للمصادقة…
|
hub.auth.message=جاري الانتظار للمصادقة…
|
||||||
hub.auth.description=يجب أن يتم إعادة توجيهك تلقائياً إلى صفحة تسجيل الدخول.
|
hub.auth.description=يجب أن يتم إعادة توجيهك تلقائياً إلى صفحة تسجيل الدخول.
|
||||||
@@ -183,6 +185,7 @@ hub.registerFailed.description.deviceAlreadyExists=هذا الجهاز مسجل
|
|||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=تم رفض الوصول
|
hub.unauthorized.message=تم رفض الوصول
|
||||||
hub.unauthorized.description=غير مسموح لك بفتح هذا المستودع. اتصل بمالك المستودع لطلب الوصول.
|
hub.unauthorized.description=غير مسموح لك بفتح هذا المستودع. اتصل بمالك المستودع لطلب الوصول.
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=مطلوب اتخاذ إجراء
|
hub.requireAccountInit.message=مطلوب اتخاذ إجراء
|
||||||
hub.requireAccountInit.description.0=للمتابعة، يرجى إكمال الخطوات المطلوبة في
|
hub.requireAccountInit.description.0=للمتابعة، يرجى إكمال الخطوات المطلوبة في
|
||||||
@@ -645,4 +648,4 @@ eventView.entry.inUse.showEncrypted=عرض ملَف المشفر
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ unlock.error.title="%s" биге асылманы
|
|||||||
hub.noKeychain.message=Йыһаз асҡысына инеү рөхсәте юҡ
|
hub.noKeychain.message=Йыһаз асҡысына инеү рөхсәте юҡ
|
||||||
hub.noKeychain.description=Хаб һаҡлағыстарын асыу өсөн йыһаз асҡысы кәрәк, ул иһә асҡыс сылбырында һаҡлана. Артабан “%s” мөмкинлеге бирегеҙ һәм көйләүҙәрҙә асҡыс сылбырын һайлағыҙ.
|
hub.noKeychain.description=Хаб һаҡлағыстарын асыу өсөн йыһаз асҡысы кәрәк, ул иһә асҡыс сылбырында һаҡлана. Артабан “%s” мөмкинлеге бирегеҙ һәм көйләүҙәрҙә асҡыс сылбырын һайлағыҙ.
|
||||||
hub.noKeychain.openBtn=Көйләүҙәрҙе ас
|
hub.noKeychain.openBtn=Көйләүҙәрҙе ас
|
||||||
|
### Check Host Authenticity
|
||||||
|
hub.checkHostTrust.trustBtn=Ышан
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=Аутентиклау көтөлә…
|
hub.auth.message=Аутентиклау көтөлә…
|
||||||
hub.auth.description=Автоматик рәүештә инеү битенә йүнәлтелергә тейешһегеҙ.
|
hub.auth.description=Автоматик рәүештә инеү битенә йүнәлтелергә тейешһегеҙ.
|
||||||
@@ -170,6 +172,7 @@ hub.registerSuccess.unlockBtn=Биген ас
|
|||||||
### Archived
|
### Archived
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Инеү кире ҡағылды
|
hub.unauthorized.message=Инеү кире ҡағылды
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Эш-хәрәкәт кәрәкле
|
hub.requireAccountInit.message=Эш-хәрәкәт кәрәкле
|
||||||
hub.requireAccountInit.description.0=Дауам итер өсөн, кәрәкле аҙымдар тамамланырға тейеш урын:
|
hub.requireAccountInit.description.0=Дауам итер өсөн, кәрәкле аҙымдар тамамланырға тейеш урын:
|
||||||
@@ -555,4 +558,4 @@ dokanySupportEnd.preferencesBtn=Көйләүҙәрҙе ас
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ unlock.error.customPath.description.inUse=Дыскавая літара або
|
|||||||
hub.noKeychain.message=Няма доступу да ключа прылады
|
hub.noKeychain.message=Няма доступу да ключа прылады
|
||||||
hub.noKeychain.description=Каб разамкнуць скарбніцы Hub, патрэбны ключ прылады, які захаваны ў звязку ключоў. Каб працягнуць, уключы "%s" ды абяры звязак ключоў у наладах.
|
hub.noKeychain.description=Каб разамкнуць скарбніцы Hub, патрэбны ключ прылады, які захаваны ў звязку ключоў. Каб працягнуць, уключы "%s" ды абяры звязак ключоў у наладах.
|
||||||
hub.noKeychain.openBtn=Адчыніць налады
|
hub.noKeychain.openBtn=Адчыніць налады
|
||||||
|
### Check Host Authenticity
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=Чаканне спраўджання…
|
hub.auth.message=Чаканне спраўджання…
|
||||||
hub.auth.description=Ты мусіш аўтаматычна перанакіравацца на старонку ўваходу.
|
hub.auth.description=Ты мусіш аўтаматычна перанакіравацца на старонку ўваходу.
|
||||||
@@ -162,6 +163,7 @@ hub.registerSuccess.unlockBtn=Адамкнуць
|
|||||||
### Archived
|
### Archived
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Адмова ў доступе
|
hub.unauthorized.message=Адмова ў доступе
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
### License Exceeded
|
### License Exceeded
|
||||||
hub.invalidLicense.message=Несапраўдная ліцэнзія Hub
|
hub.invalidLicense.message=Несапраўдная ліцэнзія Hub
|
||||||
@@ -532,4 +534,4 @@ dokanySupportEnd.preferencesBtn=Адчыніць налады
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ unlock.error.title=Неуспешно отключване на „%s“
|
|||||||
hub.noKeychain.message=Няма достъп до ключа на устройството
|
hub.noKeychain.message=Няма достъп до ключа на устройството
|
||||||
hub.noKeychain.description=За да отключите хранилищата в Hub е необходим ключ за устройството, който се защитава с помощта на ключодържател. За да продължите, разрешете „%s“ и изберете ключодържателя в настройките.
|
hub.noKeychain.description=За да отключите хранилищата в Hub е необходим ключ за устройството, който се защитава с помощта на ключодържател. За да продължите, разрешете „%s“ и изберете ключодържателя в настройките.
|
||||||
hub.noKeychain.openBtn=Към настройките
|
hub.noKeychain.openBtn=Към настройките
|
||||||
|
### Check Host Authenticity
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=Изчакване на удостоверяване…
|
hub.auth.message=Изчакване на удостоверяване…
|
||||||
hub.auth.description=Автоматично ще бъдете пренасочени към страницата за вход.
|
hub.auth.description=Автоматично ще бъдете пренасочени към страницата за вход.
|
||||||
@@ -177,6 +178,7 @@ hub.registerSuccess.unlockBtn=Отключване
|
|||||||
### Archived
|
### Archived
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Отказан достъп
|
hub.unauthorized.message=Отказан достъп
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Необходимо е действие
|
hub.requireAccountInit.message=Необходимо е действие
|
||||||
hub.requireAccountInit.description.0=За да продължите завършете необходимите стъпки в
|
hub.requireAccountInit.description.0=За да продължите завършете необходимите стъпки в
|
||||||
@@ -563,4 +565,4 @@ dokanySupportEnd.preferencesBtn=Към настройките
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ unlock.unlockBtn=আনলক করুন
|
|||||||
## Success
|
## Success
|
||||||
## Failure
|
## Failure
|
||||||
## Hub
|
## Hub
|
||||||
|
### Check Host Authenticity
|
||||||
### Waiting
|
### Waiting
|
||||||
### Receive Key
|
### Receive Key
|
||||||
### Register Device
|
### Register Device
|
||||||
@@ -101,6 +102,7 @@ hub.registerSuccess.unlockBtn=আনলক করুন
|
|||||||
### Registration Failed
|
### Registration Failed
|
||||||
### Archived
|
### Archived
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
### License Exceeded
|
### License Exceeded
|
||||||
|
|
||||||
@@ -221,4 +223,4 @@ vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন ক
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ unlock.success.rememberChoice=Zapamtite izbor, ne pokazujte ovo ponovo
|
|||||||
unlock.success.revealBtn=Otkrij pogon
|
unlock.success.revealBtn=Otkrij pogon
|
||||||
## Failure
|
## Failure
|
||||||
## Hub
|
## Hub
|
||||||
|
### Check Host Authenticity
|
||||||
|
hub.checkHostTrust.trustBtn=Povjerenje
|
||||||
### Waiting
|
### Waiting
|
||||||
### Receive Key
|
### Receive Key
|
||||||
### Register Device
|
### Register Device
|
||||||
@@ -113,6 +115,7 @@ hub.registerSuccess.unlockBtn=Otključaj
|
|||||||
### Registration Failed
|
### Registration Failed
|
||||||
### Archived
|
### Archived
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
### License Exceeded
|
### License Exceeded
|
||||||
|
|
||||||
@@ -356,4 +359,4 @@ quit.lockAndQuitBtn=Zaključaj i zatvori
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -155,6 +155,8 @@ unlock.error.title=El desblocatge de "%s" ha fallat
|
|||||||
hub.noKeychain.message=No es pot accedir a la clau del dispositiu
|
hub.noKeychain.message=No es pot accedir a la clau del dispositiu
|
||||||
hub.noKeychain.description=Per poder desblocar caixes fortes del Hub es requereix la clau d'un dispositiu, que s'emmagatzema de forma segura en un clauer. Per continuar, habiliti "%s" i seleccioni un clauer en les Preferències.
|
hub.noKeychain.description=Per poder desblocar caixes fortes del Hub es requereix la clau d'un dispositiu, que s'emmagatzema de forma segura en un clauer. Per continuar, habiliti "%s" i seleccioni un clauer en les Preferències.
|
||||||
hub.noKeychain.openBtn=Obrir les Preferències
|
hub.noKeychain.openBtn=Obrir les Preferències
|
||||||
|
### Check Host Authenticity
|
||||||
|
hub.checkHostTrust.trustBtn=Confia-hi
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=S’està esperant l’autenticació…
|
hub.auth.message=S’està esperant l’autenticació…
|
||||||
hub.auth.description=Hauríeu de ser redirigits a la pàgina d'accés.
|
hub.auth.description=Hauríeu de ser redirigits a la pàgina d'accés.
|
||||||
@@ -184,6 +186,7 @@ hub.registerFailed.description.deviceAlreadyExists=El dispositiu ja ha estat reg
|
|||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Accés denegat
|
hub.unauthorized.message=Accés denegat
|
||||||
hub.unauthorized.description=No estàs autoritzat a obrir aquesta caixa forta. Contacta amb el seu propietari per obtenir accés.
|
hub.unauthorized.description=No estàs autoritzat a obrir aquesta caixa forta. Contacta amb el seu propietari per obtenir accés.
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Acció necessària
|
hub.requireAccountInit.message=Acció necessària
|
||||||
hub.requireAccountInit.description.0=Per a continuar, si us plau, seguiu els passos necessaris en el vostre
|
hub.requireAccountInit.description.0=Per a continuar, si us plau, seguiu els passos necessaris en el vostre
|
||||||
@@ -611,4 +614,4 @@ shareVault.hub.openHub=Obre Cryptomator Hub
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -150,6 +150,8 @@ unlock.error.title=Odemknutí "%s" selhalo
|
|||||||
hub.noKeychain.message=Nelze získat přístup ke klíči zařízení
|
hub.noKeychain.message=Nelze získat přístup ke klíči zařízení
|
||||||
hub.noKeychain.description=Chcete-li odemknout Hub trezory, je potřeba použít klíč zařízení, který je zabezpečen pomocí klíčenky. Chcete-li pokračovat, povolte „%s“ a vyberte klíčenku v nastavení.
|
hub.noKeychain.description=Chcete-li odemknout Hub trezory, je potřeba použít klíč zařízení, který je zabezpečen pomocí klíčenky. Chcete-li pokračovat, povolte „%s“ a vyberte klíčenku v nastavení.
|
||||||
hub.noKeychain.openBtn=Otevřít předvolby
|
hub.noKeychain.openBtn=Otevřít předvolby
|
||||||
|
### Check Host Authenticity
|
||||||
|
hub.checkHostTrust.trustBtn=Důvěřovat
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=Čekání na ověření…
|
hub.auth.message=Čekání na ověření…
|
||||||
hub.auth.description=Měli byste být automaticky přesměrováni na přihlašovací stránku.
|
hub.auth.description=Měli byste být automaticky přesměrováni na přihlašovací stránku.
|
||||||
@@ -178,6 +180,7 @@ hub.registerFailed.description.deviceAlreadyExists=Toto zařízení je již regi
|
|||||||
### Archived
|
### Archived
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Přístup odepřen
|
hub.unauthorized.message=Přístup odepřen
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Požadována akce
|
hub.requireAccountInit.message=Požadována akce
|
||||||
hub.requireAccountInit.description.0=Chcete-li pokračovat, vyplňte prosím požadované kroky ve vašem
|
hub.requireAccountInit.description.0=Chcete-li pokračovat, vyplňte prosím požadované kroky ve vašem
|
||||||
@@ -572,4 +575,4 @@ shareVault.hub.openHub=Otevřít Cryptomator Hub
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -109,6 +109,13 @@ addvaultwizard.existing.restore=Gendan…
|
|||||||
addvaultwizard.existing.chooseBtn=Vælg…
|
addvaultwizard.existing.chooseBtn=Vælg…
|
||||||
addvaultwizard.existing.filePickerTitle=Vælg boks-fil
|
addvaultwizard.existing.filePickerTitle=Vælg boks-fil
|
||||||
addvaultwizard.existing.filePickerMimeDesc=Cryptomator boks
|
addvaultwizard.existing.filePickerMimeDesc=Cryptomator boks
|
||||||
|
addvaultwizard.existing.notAVault.title=Ikke en boks
|
||||||
|
addvaultwizard.existing.notAVault.message=Den valgte mappe skal være en gyldig Cryptomator boks
|
||||||
|
addvaultwizard.existing.notAVault.description.missingDataDir=Den krævede "d" undermappe mangler inde i "%s".
|
||||||
|
addvaultwizard.existing.notAVault.description.dataNotADirectory=Indgangen "d" inde i "%s" er ikke en mappe.
|
||||||
|
addvaultwizard.existing.notAVault.description.missingVaultConfig=Den påkrævede "vault.cryptomator"-fil mangler inde i "%s".
|
||||||
|
addvaultwizard.existing.notAVault.description.vaultConfigAccessDenied=Filen "vault.cryptomator" inde i "%s" kan ikke læses på grund af utilstrækkelige adgangsrettigheder.
|
||||||
|
addvaultwizard.existing.notAVault.description.unsupportedStructure=Mappestrukturen for "%s" understøttes ikke.
|
||||||
## Success
|
## Success
|
||||||
addvaultwizard.success.nextStepsInstructions=Tilføjede boks "%s".\nDu skal låse op for denne boks for at tilgå indholdet. Alternativt kan du låse den op på et senere tidspunkt.
|
addvaultwizard.success.nextStepsInstructions=Tilføjede boks "%s".\nDu skal låse op for denne boks for at tilgå indholdet. Alternativt kan du låse den op på et senere tidspunkt.
|
||||||
addvaultwizard.success.unlockNow=Lås op nu
|
addvaultwizard.success.unlockNow=Lås op nu
|
||||||
@@ -161,6 +168,12 @@ unlock.error.title=Oplåsning af "%s" mislykkedes
|
|||||||
hub.noKeychain.message=Kan ikke tilgå enhedsnøgle
|
hub.noKeychain.message=Kan ikke tilgå enhedsnøgle
|
||||||
hub.noKeychain.description=En enhedsnøgle er påkrævet for at låse Hub bokse op. Enhedsnøglen er sikret i en nøglering. For at fortsætte, aktivér “%s” og vælg en nøglering i indstillingerne.
|
hub.noKeychain.description=En enhedsnøgle er påkrævet for at låse Hub bokse op. Enhedsnøglen er sikret i en nøglering. For at fortsætte, aktivér “%s” og vælg en nøglering i indstillingerne.
|
||||||
hub.noKeychain.openBtn=Åbn Indstillinger
|
hub.noKeychain.openBtn=Åbn Indstillinger
|
||||||
|
### Check Host Authenticity
|
||||||
|
hub.checkHostTrust.message.check=Kontrollerer konfiguration…
|
||||||
|
hub.checkHostTrust.message.ask=Stol på denne vært?
|
||||||
|
hub.checkHostTrust.message.ask.plural=Stol på disse værter?
|
||||||
|
hub.checkHostTrust.trustBtn=Hav tillid
|
||||||
|
hub.checkHostTrust.denyBtn=Afvis
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=Afventer godkendelse…
|
hub.auth.message=Afventer godkendelse…
|
||||||
hub.auth.description=Du burde automatisk blive omdirigeret til login-siden.
|
hub.auth.description=Du burde automatisk blive omdirigeret til login-siden.
|
||||||
@@ -192,6 +205,9 @@ hub.archived.description=Denne boks er blevet arkiveret og er ikke længere tilg
|
|||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Adgang nægtet
|
hub.unauthorized.message=Adgang nægtet
|
||||||
hub.unauthorized.description=Du har ikke tilladelse til at åbne denne boks. Kontant ejeren af boksen for at anmode om adgang.
|
hub.unauthorized.description=Du har ikke tilladelse til at åbne denne boks. Kontant ejeren af boksen for at anmode om adgang.
|
||||||
|
### Untrusted Host
|
||||||
|
hub.untrustedHost.message=Vært Ikke pålidelig
|
||||||
|
hub.untrustedHost.description=Forbindelsen til Hub blev blokeret for din sikkerhed. Hvis du mener, at Hub-værten er sikker, så kontakt din Hub-administrator eller prøv igen.
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Handling påkrævet
|
hub.requireAccountInit.message=Handling påkrævet
|
||||||
hub.requireAccountInit.description.0=For at fortsætte, skal du fuldføre de nødvendige trin i din
|
hub.requireAccountInit.description.0=For at fortsætte, skal du fuldføre de nødvendige trin i din
|
||||||
@@ -305,6 +321,7 @@ preferences.general.debugDirectory=Vis logfiler
|
|||||||
preferences.general.autoStart=Start Cryptomator automatisk ved opstart
|
preferences.general.autoStart=Start Cryptomator automatisk ved opstart
|
||||||
preferences.general.keychainBackend=Gem adgangskoder med
|
preferences.general.keychainBackend=Gem adgangskoder med
|
||||||
preferences.general.quickAccessService=Tilføj oplåste bokse til området hurtig adgang
|
preferences.general.quickAccessService=Tilføj oplåste bokse til området hurtig adgang
|
||||||
|
preferences.general.resetTrustedHosts=Nulstil betroede værter
|
||||||
## Interface
|
## Interface
|
||||||
preferences.interface=Brugerflade
|
preferences.interface=Brugerflade
|
||||||
preferences.interface.theme=Udseende
|
preferences.interface.theme=Udseende
|
||||||
@@ -716,4 +733,4 @@ eventView.entry.inUse.ignoreLock=Ignorér anvendelsesstatus
|
|||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.message=Filen er i brug på en anden enhed
|
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.description=Filen er åbnet af %s på %s. Bed dem om at lukke filen og lade synkroniseringen afslutte. Du kan ignorere status for at åbne den nu, men det kan forårsage konflikter eller overskrive nyere ændringer.
|
||||||
notification.inUse.action=Ignorér anvendelsesstatus
|
notification.inUse.action=Ignorér anvendelsesstatus
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ addvaultwizard.existing.restore=Wiederherstellen…
|
|||||||
addvaultwizard.existing.chooseBtn=Durchsuchen …
|
addvaultwizard.existing.chooseBtn=Durchsuchen …
|
||||||
addvaultwizard.existing.filePickerTitle=Tresordatei auswählen
|
addvaultwizard.existing.filePickerTitle=Tresordatei auswählen
|
||||||
addvaultwizard.existing.filePickerMimeDesc=Cryptomator-Tresor
|
addvaultwizard.existing.filePickerMimeDesc=Cryptomator-Tresor
|
||||||
|
addvaultwizard.existing.notAVault.description.unsupportedStructure=Die Verzeichnisstruktur von "%s" wird nicht unterstützt.
|
||||||
## Success
|
## Success
|
||||||
addvaultwizard.success.nextStepsInstructions=Tresor „%s“ wurde hinzugefügt.\nUm auf Inhalte zuzugreifen oder welche hinzuzufügen, musst du den Tresor entsperren. Du kannst ihn aber auch zu jedem späteren Zeitpunkt entsperren.
|
addvaultwizard.success.nextStepsInstructions=Tresor „%s“ wurde hinzugefügt.\nUm auf Inhalte zuzugreifen oder welche hinzuzufügen, musst du den Tresor entsperren. Du kannst ihn aber auch zu jedem späteren Zeitpunkt entsperren.
|
||||||
addvaultwizard.success.unlockNow=Jetzt entsperren
|
addvaultwizard.success.unlockNow=Jetzt entsperren
|
||||||
@@ -156,6 +157,8 @@ unlock.error.title=„%s“ konnte nicht entsperrt werden
|
|||||||
hub.noKeychain.message=Zugriff auf Geräteschlüssel nicht möglich
|
hub.noKeychain.message=Zugriff auf Geräteschlüssel nicht möglich
|
||||||
hub.noKeychain.description=Zum Entsperren von Hub-Tresoren wird ein Geräteschlüssel benötigt, der in einem Schlüsselbund gesichert ist. Um fortzufahren, aktiviere „%s“ und wähle in den Einstellungen einen Schlüsselbund.
|
hub.noKeychain.description=Zum Entsperren von Hub-Tresoren wird ein Geräteschlüssel benötigt, der in einem Schlüsselbund gesichert ist. Um fortzufahren, aktiviere „%s“ und wähle in den Einstellungen einen Schlüsselbund.
|
||||||
hub.noKeychain.openBtn=Einstellungen öffnen
|
hub.noKeychain.openBtn=Einstellungen öffnen
|
||||||
|
### Check Host Authenticity
|
||||||
|
hub.checkHostTrust.trustBtn=Vertrauen
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=Warten auf Authentifizierung …
|
hub.auth.message=Warten auf Authentifizierung …
|
||||||
hub.auth.description=Du solltest automatisch zur Anmeldeseite weitergeleitet werden.
|
hub.auth.description=Du solltest automatisch zur Anmeldeseite weitergeleitet werden.
|
||||||
@@ -187,6 +190,7 @@ hub.archived.description=Dieser Tresor wurde archiviert, daher ist ein Zugriff d
|
|||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Zugriff verweigert
|
hub.unauthorized.message=Zugriff verweigert
|
||||||
hub.unauthorized.description=Du bist nicht berechtigt, diesen Tresor zu öffnen. Wende dich an den Tresoreigentümer, um Zugriff zu erhalten.
|
hub.unauthorized.description=Du bist nicht berechtigt, diesen Tresor zu öffnen. Wende dich an den Tresoreigentümer, um Zugriff zu erhalten.
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Aktion erforderlich
|
hub.requireAccountInit.message=Aktion erforderlich
|
||||||
hub.requireAccountInit.description.0=Um fortzufahren, führe bitte die erforderlichen Schritte in deinem
|
hub.requireAccountInit.description.0=Um fortzufahren, führe bitte die erforderlichen Schritte in deinem
|
||||||
@@ -298,6 +302,7 @@ preferences.general.debugDirectory=Protokolldateien anzeigen
|
|||||||
preferences.general.autoStart=Cryptomator beim Systemstart starten
|
preferences.general.autoStart=Cryptomator beim Systemstart starten
|
||||||
preferences.general.keychainBackend=Passwörter speichern mit
|
preferences.general.keychainBackend=Passwörter speichern mit
|
||||||
preferences.general.quickAccessService=Entsperrte Tresore zum Schnellzugriff hinzufügen
|
preferences.general.quickAccessService=Entsperrte Tresore zum Schnellzugriff hinzufügen
|
||||||
|
preferences.general.resetTrustedHosts=Vertrauenswürdige Hosts zurücksetzen
|
||||||
## Interface
|
## Interface
|
||||||
preferences.interface=Benutzeroberfläche
|
preferences.interface=Benutzeroberfläche
|
||||||
preferences.interface.theme=Erscheinungsbild
|
preferences.interface.theme=Erscheinungsbild
|
||||||
@@ -701,4 +706,4 @@ eventView.entry.inUse.copyUserAndDevice=Benutzer- und Gerätenamen der Sperre ko
|
|||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.action=Verwendungsstatus ignorieren
|
notification.inUse.action=Verwendungsstatus ignorieren
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ unlock.error.title=Ξεκλείδωμα "%s" απέτυχε
|
|||||||
hub.noKeychain.message=Δεν είναι δυνατή η πρόσβαση στο κλειδί της συσκευής
|
hub.noKeychain.message=Δεν είναι δυνατή η πρόσβαση στο κλειδί της συσκευής
|
||||||
hub.noKeychain.description=Για να ξεκλειδώσετε τις κρύπτες Hub, απαιτείται ένα κλειδί συσκευής, το οποίο ασφαλίζεται με χρήση μπρελόκ. Για να συνεχίσετε, ενεργοποιήστε το "%s" και επιλέξτε ένα keychain στις προτιμήσεις.
|
hub.noKeychain.description=Για να ξεκλειδώσετε τις κρύπτες Hub, απαιτείται ένα κλειδί συσκευής, το οποίο ασφαλίζεται με χρήση μπρελόκ. Για να συνεχίσετε, ενεργοποιήστε το "%s" και επιλέξτε ένα keychain στις προτιμήσεις.
|
||||||
hub.noKeychain.openBtn=Άνοιγμα Προτιμήσεων
|
hub.noKeychain.openBtn=Άνοιγμα Προτιμήσεων
|
||||||
|
### Check Host Authenticity
|
||||||
|
hub.checkHostTrust.trustBtn=Εμπιστευτείτε
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=Αναμονή για επαλήθευση…
|
hub.auth.message=Αναμονή για επαλήθευση…
|
||||||
hub.auth.description=Θα πρέπει να ανακατευθυνθείτε αυτόματα στη σελίδα σύνδεσης.
|
hub.auth.description=Θα πρέπει να ανακατευθυνθείτε αυτόματα στη σελίδα σύνδεσης.
|
||||||
@@ -183,6 +185,7 @@ hub.registerFailed.description.deviceAlreadyExists=Αυτή η συσκευή ε
|
|||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Δεν επιτρέπεται η πρόσβαση
|
hub.unauthorized.message=Δεν επιτρέπεται η πρόσβαση
|
||||||
hub.unauthorized.description=Δεν είστε εξουσιοδοτημένοι να ανοίξετε αυτή την κρύπτη. Επικοινωνήστε με τον ιδιοκτήτη τς κρύπτης για να ζητήσετε πρόσβαση.
|
hub.unauthorized.description=Δεν είστε εξουσιοδοτημένοι να ανοίξετε αυτή την κρύπτη. Επικοινωνήστε με τον ιδιοκτήτη τς κρύπτης για να ζητήσετε πρόσβαση.
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Απαιτείται ενέργεια
|
hub.requireAccountInit.message=Απαιτείται ενέργεια
|
||||||
hub.requireAccountInit.description.0=Για να συνεχίσετε, παρακαλούμε ολοκληρώστε τα βήματα που απαιτούνται στο δικό σας
|
hub.requireAccountInit.description.0=Για να συνεχίσετε, παρακαλούμε ολοκληρώστε τα βήματα που απαιτούνται στο δικό σας
|
||||||
@@ -645,4 +648,4 @@ eventView.entry.inUse.showEncrypted=Εμφάνιση κρυπτογραφημέ
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ generic.button.print=Imprimir
|
|||||||
generic.button.remove=Eliminar
|
generic.button.remove=Eliminar
|
||||||
|
|
||||||
## Vault state
|
## Vault state
|
||||||
|
vault.state.locked=Bloqueada
|
||||||
|
vault.state.unlocked=Desbloqueada
|
||||||
|
vault.state.missing=Faltante
|
||||||
|
vault.state.migrationNeeded=Migración obligatoria
|
||||||
|
vault.state.processing=Procesando
|
||||||
vault.state.error=Error
|
vault.state.error=Error
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
@@ -104,6 +109,13 @@ addvaultwizard.existing.restore=…
|
|||||||
addvaultwizard.existing.chooseBtn=Elegir…
|
addvaultwizard.existing.chooseBtn=Elegir…
|
||||||
addvaultwizard.existing.filePickerTitle=Seleccionar archivo de bóveda
|
addvaultwizard.existing.filePickerTitle=Seleccionar archivo de bóveda
|
||||||
addvaultwizard.existing.filePickerMimeDesc=Bóveda de Cryptomator
|
addvaultwizard.existing.filePickerMimeDesc=Bóveda de Cryptomator
|
||||||
|
addvaultwizard.existing.notAVault.title=No es una bóveda
|
||||||
|
addvaultwizard.existing.notAVault.message=La carpeta seleccionada no es una bóveda de Cryptomator
|
||||||
|
addvaultwizard.existing.notAVault.description.missingDataDir=Falta el subdirectorio "d" necesario dentro de "%s".
|
||||||
|
addvaultwizard.existing.notAVault.description.dataNotADirectory=La entrada "d" dentro de "%s" no es un directorio.
|
||||||
|
addvaultwizard.existing.notAVault.description.missingVaultConfig=Falta el archivo "vault.cryptomator" necesario dentro de "%s".
|
||||||
|
addvaultwizard.existing.notAVault.description.vaultConfigAccessDenied=El archivo "vault.cryptomator" dentro de "%s" no se puede leer debido a la falta de permisos de acceso.
|
||||||
|
addvaultwizard.existing.notAVault.description.unsupportedStructure=La estructura de directorios de "%s" no está soportada.
|
||||||
## Success
|
## Success
|
||||||
addvaultwizard.success.nextStepsInstructions=Bóveda "%s" añadida.\nSe necesita desbloquear esta bóveda para acceder o añadir contenido. Alternativamente se \n puede desbloquear en otro momento.
|
addvaultwizard.success.nextStepsInstructions=Bóveda "%s" añadida.\nSe necesita desbloquear esta bóveda para acceder o añadir contenido. Alternativamente se \n puede desbloquear en otro momento.
|
||||||
addvaultwizard.success.unlockNow=Desbloquear ahora
|
addvaultwizard.success.unlockNow=Desbloquear ahora
|
||||||
@@ -156,6 +168,12 @@ unlock.error.title=Error al desbloquear "%s"
|
|||||||
hub.noKeychain.message=No se puede acceder a la clave del dispositivo
|
hub.noKeychain.message=No se puede acceder a la clave del dispositivo
|
||||||
hub.noKeychain.description=Para desbloquear las bóvedas de Hub, se requiere una clave de dispositivo que se asegura con un llavero. Para continuar, habilite "%s" y seleccione un llavero en las preferencias.
|
hub.noKeychain.description=Para desbloquear las bóvedas de Hub, se requiere una clave de dispositivo que se asegura con un llavero. Para continuar, habilite "%s" y seleccione un llavero en las preferencias.
|
||||||
hub.noKeychain.openBtn=Abrir preferencias
|
hub.noKeychain.openBtn=Abrir preferencias
|
||||||
|
### Check Host Authenticity
|
||||||
|
hub.checkHostTrust.message.check=Comprobando configuración…
|
||||||
|
hub.checkHostTrust.message.ask=¿Confiar en este host?
|
||||||
|
hub.checkHostTrust.message.ask.plural=¿Confiar en estos hosts?
|
||||||
|
hub.checkHostTrust.trustBtn=Confiar
|
||||||
|
hub.checkHostTrust.denyBtn=Denegar
|
||||||
### Waiting
|
### Waiting
|
||||||
hub.auth.message=Esperando la autenticación…
|
hub.auth.message=Esperando la autenticación…
|
||||||
hub.auth.description=Debería ser redirigido automáticamente a la página de inicio de sesión.
|
hub.auth.description=Debería ser redirigido automáticamente a la página de inicio de sesión.
|
||||||
@@ -182,9 +200,14 @@ hub.registerFailed.message=El registro del dispositivo ha fallado
|
|||||||
hub.registerFailed.description.generic=Ha ocurrido un error al intentar registrarse. Para más detalles, consulte el registro de la aplicación.
|
hub.registerFailed.description.generic=Ha ocurrido un error al intentar registrarse. Para más detalles, consulte el registro de la aplicación.
|
||||||
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo ya se ha registrado para otro usuario. Intente cambiar la cuenta de usuario o utilice un dispositivo diferente.
|
hub.registerFailed.description.deviceAlreadyExists=Este dispositivo ya se ha registrado para otro usuario. Intente cambiar la cuenta de usuario o utilice un dispositivo diferente.
|
||||||
### Archived
|
### Archived
|
||||||
|
hub.archived.message=Bóveda archivada
|
||||||
|
hub.archived.description=Esta bóveda ha sido archivada y ya no es accesible. Póngase en contacto con el propietario de la bóveda.
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
hub.unauthorized.message=Acceso denegado
|
hub.unauthorized.message=Acceso denegado
|
||||||
hub.unauthorized.description=No tiene autorización para abrir esta bóveda. Contacta al propietario de la bóveda para solicitar acceso.
|
hub.unauthorized.description=No tiene autorización para abrir esta bóveda. Contacta al propietario de la bóveda para solicitar acceso.
|
||||||
|
### Untrusted Host
|
||||||
|
hub.untrustedHost.message=Host no confiable
|
||||||
|
hub.untrustedHost.description=La conexión al Hub fue bloqueada por su seguridad. Si cree que el Hub es seguro, póngase en contacto con la administración de Hub o vuelva a intentarlo.
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
hub.requireAccountInit.message=Acción requerida
|
hub.requireAccountInit.message=Acción requerida
|
||||||
hub.requireAccountInit.description.0=Para continuar, por favor complete los pasos necesarios en su
|
hub.requireAccountInit.description.0=Para continuar, por favor complete los pasos necesarios en su
|
||||||
@@ -298,6 +321,7 @@ preferences.general.debugDirectory=Revelar archivos de registro
|
|||||||
preferences.general.autoStart=Cargar Cryptomator al iniciar el sistema
|
preferences.general.autoStart=Cargar Cryptomator al iniciar el sistema
|
||||||
preferences.general.keychainBackend=Guardar contraseñas con
|
preferences.general.keychainBackend=Guardar contraseñas con
|
||||||
preferences.general.quickAccessService=Añadir bóvedas desbloqueadas al área de acceso rápido
|
preferences.general.quickAccessService=Añadir bóvedas desbloqueadas al área de acceso rápido
|
||||||
|
preferences.general.resetTrustedHosts=Restablecer hosts de confianza
|
||||||
## Interface
|
## Interface
|
||||||
preferences.interface=Interfaz
|
preferences.interface=Interfaz
|
||||||
preferences.interface.theme=Apariencia
|
preferences.interface.theme=Apariencia
|
||||||
@@ -403,6 +427,7 @@ stats.access.total=Accesos totales: %d
|
|||||||
# Main Window
|
# Main Window
|
||||||
## Vault List
|
## Vault List
|
||||||
main.vaultlist=Bóvedas
|
main.vaultlist=Bóvedas
|
||||||
|
main.vaultlist.listEntry=Bóveda %s (%s)
|
||||||
main.vaultlist.emptyList.onboardingInstruction=Haga clic aquí para añadir una bóveda
|
main.vaultlist.emptyList.onboardingInstruction=Haga clic aquí para añadir una bóveda
|
||||||
main.vaultlist.contextMenu.remove=Eliminar…
|
main.vaultlist.contextMenu.remove=Eliminar…
|
||||||
main.vaultlist.contextMenu.lock=Bloquear
|
main.vaultlist.contextMenu.lock=Bloquear
|
||||||
@@ -420,9 +445,11 @@ main.vaultlist.showPreferencesButton.tooltip=Mostrar preferencias
|
|||||||
##Notification
|
##Notification
|
||||||
main.notification.updateAvailable=Existen actualizaciones disponibles.
|
main.notification.updateAvailable=Existen actualizaciones disponibles.
|
||||||
main.notification.support=Soporte de Cryptomator.
|
main.notification.support=Soporte de Cryptomator.
|
||||||
|
main.notification.closeButton.tooltip=Cerrar barra de información
|
||||||
## Vault Detail
|
## Vault Detail
|
||||||
### Welcome
|
### Welcome
|
||||||
main.vaultDetail.welcomeOnboarding=Gracias por elegir Cryptomator para proteger sus archivos. En caso de necesitar ayuda, revise nuestras guías:
|
main.vaultDetail.welcomeOnboarding=Gracias por elegir Cryptomator para proteger sus archivos. En caso de necesitar ayuda, revise nuestras guías:
|
||||||
|
main.vaultDetail.storageLocation=Ubicación de almacenamiento en bóveda
|
||||||
### Locked
|
### Locked
|
||||||
main.vaultDetail.lockedStatus=BLOQUEADA
|
main.vaultDetail.lockedStatus=BLOQUEADA
|
||||||
main.vaultDetail.unlockBtn=Desbloquear…
|
main.vaultDetail.unlockBtn=Desbloquear…
|
||||||
@@ -511,6 +538,7 @@ vaultOptions.masterkey.forgetSavedPasswordBtn=Olvidar contraseña guardada
|
|||||||
vaultOptions.masterkey.recoveryKeyExplanation=Una clave de recuperación es el único medio para restaurar el acceso a una bóveda si pierde su contraseña.
|
vaultOptions.masterkey.recoveryKeyExplanation=Una clave de recuperación es el único medio para restaurar el acceso a una bóveda si pierde su contraseña.
|
||||||
vaultOptions.masterkey.showRecoveryKeyBtn=Mostrar clave de recuperación
|
vaultOptions.masterkey.showRecoveryKeyBtn=Mostrar clave de recuperación
|
||||||
vaultOptions.masterkey.recoverPasswordBtn=Restablecer contraseña
|
vaultOptions.masterkey.recoverPasswordBtn=Restablecer contraseña
|
||||||
|
vaultOptions.masterkey.missingMasterkeyFile=Estas opciones sólo están disponibles si el archivo masterkey está presente en el directorio de la bóveda.
|
||||||
## Hub
|
## Hub
|
||||||
vaultOptions.hub=Recuperación
|
vaultOptions.hub=Recuperación
|
||||||
vaultOptions.hub.convertInfo=Puede utilizar la clave de recuperación para convertir esta bóveda de Hub en una bóveda con contraseña en una emergencia.
|
vaultOptions.hub.convertInfo=Puede utilizar la clave de recuperación para convertir esta bóveda de Hub en una bóveda con contraseña en una emergencia.
|
||||||
@@ -697,6 +725,7 @@ eventView.entry.brokenFileNode.copyDecrypted=Copiar ruta descifrada
|
|||||||
eventView.entry.inUse.message=Fichero en uso
|
eventView.entry.inUse.message=Fichero en uso
|
||||||
eventView.entry.inUse.showDecrypted=Mostrar archivo descifrado
|
eventView.entry.inUse.showDecrypted=Mostrar archivo descifrado
|
||||||
eventView.entry.inUse.showEncrypted=Mostrar archivo cifrado
|
eventView.entry.inUse.showEncrypted=Mostrar archivo cifrado
|
||||||
|
eventView.entry.inUse.copyUserAndDevice=Copiar nombre de usuario y dispositivo de bloqueo
|
||||||
eventView.entry.inUse.ignoreLock=Ignorar el estado de uso
|
eventView.entry.inUse.ignoreLock=Ignorar el estado de uso
|
||||||
|
|
||||||
|
|
||||||
@@ -704,4 +733,4 @@ eventView.entry.inUse.ignoreLock=Ignorar el estado de uso
|
|||||||
## FileIsInUse Notification
|
## FileIsInUse Notification
|
||||||
notification.inUse.message=El archivo está en uso en otro dispositivo
|
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.description=El archivo está abierto por %s en %s. Solicítale que cierre el archivo y deje que finalice la sincronización. Puede ignorar el estado para abrirlo ahora, pero esto puede causar conflictos o sobrescribir cambios más recientes.
|
||||||
notification.inUse.action=Ignorar el Estado de Uso
|
notification.inUse.action=Ignorar el Estado de Uso
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ unlock.success.revealBtn=نمایش درایو
|
|||||||
## Failure
|
## Failure
|
||||||
## Hub
|
## Hub
|
||||||
hub.noKeychain.openBtn=باز کردن تنظیمات
|
hub.noKeychain.openBtn=باز کردن تنظیمات
|
||||||
|
### Check Host Authenticity
|
||||||
### Waiting
|
### Waiting
|
||||||
### Receive Key
|
### Receive Key
|
||||||
### Register Device
|
### Register Device
|
||||||
@@ -124,6 +125,7 @@ hub.registerSuccess.unlockBtn=بازکردن قفل
|
|||||||
### Registration Failed
|
### Registration Failed
|
||||||
### Archived
|
### Archived
|
||||||
### Unauthorized
|
### Unauthorized
|
||||||
|
### Untrusted Host
|
||||||
### Requires Account Initialization
|
### Requires Account Initialization
|
||||||
### License Exceeded
|
### License Exceeded
|
||||||
|
|
||||||
@@ -256,4 +258,4 @@ dokanySupportEnd.preferencesBtn=باز کردن تنظیمات
|
|||||||
|
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
## FileIsInUse Notification
|
## 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