mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-14 08:41:28 +00:00
moved update mechanism classes to integrations-*
This commit is contained in:
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
2
.idea/runConfigurations/Cryptomator_macOS.xml
generated
@@ -5,7 +5,7 @@
|
||||
</envs>
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath="@{userhome}/Library/Application Support/Cryptomator/settings.json" -Dcryptomator.p12Path="@{userhome}/Library/Application Support/Cryptomator/key.p12" -Dcryptomator.ipcSocketPath="@{userhome}/Library/Application Support/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{userhome}/Library/Logs/Cryptomator" -Dcryptomator.pluginDir="@{userhome}/Library/Application Support/Cryptomator/Plugins" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Dcryptomator.integrationsMac.keychainServiceName=Cryptomator -Dcryptomator.updateMechanism=org.cryptomator.updater.MacOsDmgUpdateMechanism -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 -Xss2m -Xmx512m -ea --enable-preview --enable-native-access=org.cryptomator.jfuse.mac,javafx.graphics" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
2
dist/mac/dmg/build.sh
vendored
2
dist/mac/dmg/build.sh
vendored
@@ -123,7 +123,7 @@ ${JAVA_HOME}/bin/jpackage \
|
||||
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
|
||||
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support${APP_NAME}/mnt\"" \
|
||||
--java-options "-Dcryptomator.showTrayIcon=true" \
|
||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.updater.MacOsDmgUpdateMechanism" \
|
||||
--java-options "-Dcryptomator.updateMechanism=org.cryptomator.macos.update.DmgUpdateMechanism" \
|
||||
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
|
||||
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
|
||||
--resource-dir ../resources
|
||||
|
||||
18
pom.xml
18
pom.xml
@@ -34,9 +34,9 @@
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.9.0</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.7.0-SNAPSHOT</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.version>1.8.0-SNAPSHOT</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.5.1</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.4.1</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.mac.version>1.5.0-SNAPSHOT</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.7.0-SNAPSHOT</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>5.1.0</cryptomator.fuse.version>
|
||||
<cryptomator.webdav.version>3.0.0</cryptomator.webdav.version>
|
||||
@@ -75,6 +75,20 @@
|
||||
<surefire.jacoco.args></surefire.jacoco.args>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<name>Central Portal Snapshots</name>
|
||||
<id>central-portal-snapshots</id>
|
||||
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- Cryptomator Libs -->
|
||||
<dependency>
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.cryptomator.networking.SSLContextWithWindowsCertStore;
|
||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.logging.LogbackConfiguratorFactory;
|
||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||
import org.cryptomator.updater.MacOsDmgUpdateMechanism;
|
||||
|
||||
open module org.cryptomator.desktop {
|
||||
requires static org.jetbrains.annotations;
|
||||
@@ -64,9 +63,6 @@ open module org.cryptomator.desktop {
|
||||
uses SSLContextProvider;
|
||||
uses org.cryptomator.event.NotificationHandler;
|
||||
|
||||
// opens org.cryptomator.updater to org.cryptomator.integrations.api;
|
||||
provides UpdateMechanism with MacOsDmgUpdateMechanism;
|
||||
|
||||
provides TrayMenuController with AwtTrayMenuController;
|
||||
provides Configurator with LogbackConfiguratorFactory;
|
||||
provides SSLContextProvider with SSLContextWithWindowsCertStore, SSLContextWithMacKeychain, SSLContextWithPKCS12TrustStore;
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package org.cryptomator.updater;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.cryptomator.integrations.update.UpdateInfo;
|
||||
import org.cryptomator.integrations.update.UpdateMechanism;
|
||||
import org.jetbrains.annotations.Blocking;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class DownloadUpdateMechanism implements UpdateMechanism<DownloadUpdateMechanism.DownloadUpdateInfo> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory .getLogger(DownloadUpdateMechanism.class);
|
||||
private static final String LATEST_VERSION_API_URL = "https://api.cryptomator.org/connect/apps/desktop/latest-version?format=1";
|
||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
|
||||
public record DownloadUpdateInfo(
|
||||
DownloadUpdateMechanism updateMechanism,
|
||||
String version,
|
||||
Asset asset
|
||||
) implements UpdateInfo<DownloadUpdateInfo> {}
|
||||
|
||||
@Override
|
||||
public DownloadUpdateInfo checkForUpdate(String currentVersion, HttpClient httpClient) {
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(LATEST_VERSION_API_URL)).build();
|
||||
HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
if (response.statusCode() != 200) {
|
||||
throw new RuntimeException("Failed to fetch release: " + response.statusCode());
|
||||
}
|
||||
var release = MAPPER.readValue(response.body(), LatestVersionResponse.class);
|
||||
return checkForUpdate(currentVersion, release);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
LOG.debug("Update check interrupted.");
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Update check failed", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Blocking
|
||||
abstract DownloadUpdateInfo checkForUpdate(String currentVersion, LatestVersionResponse response);
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record LatestVersionResponse(
|
||||
@JsonProperty("latestVersion") LatestVersion latestVersion,
|
||||
@JsonProperty("assets") List<Asset> assets
|
||||
) {}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record LatestVersion(
|
||||
@JsonProperty("mac") String macVersion,
|
||||
@JsonProperty("win") String winVersion,
|
||||
@JsonProperty("linux") String linuxVersion
|
||||
) {}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public record Asset(
|
||||
@JsonProperty("name") String name,
|
||||
@JsonProperty("digest") String digest,
|
||||
@JsonProperty("size") long size,
|
||||
@JsonProperty("downloadUrl") String downloadUrl
|
||||
) {}
|
||||
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package org.cryptomator.updater;
|
||||
|
||||
import org.cryptomator.integrations.common.DisplayName;
|
||||
import org.cryptomator.integrations.common.OperatingSystem;
|
||||
import org.cryptomator.integrations.common.Priority;
|
||||
import org.cryptomator.integrations.update.DownloadUpdateStep;
|
||||
import org.cryptomator.integrations.update.UpdateFailedException;
|
||||
import org.cryptomator.integrations.update.UpdateMechanism;
|
||||
import org.cryptomator.integrations.update.UpdateStep;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
@Priority(1000)
|
||||
@OperatingSystem(OperatingSystem.Value.MAC)
|
||||
@DisplayName("download .dmg file") // TODO: localize
|
||||
public class MacOsDmgUpdateMechanism extends DownloadUpdateMechanism {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MacOsDmgUpdateMechanism.class);
|
||||
|
||||
@Override
|
||||
DownloadUpdateInfo checkForUpdate(String currentVersion, LatestVersionResponse response) {
|
||||
String suffix = switch (System.getProperty("os.arch")) {
|
||||
case "aarch64", "arm64" -> "arm64.dmg";
|
||||
default -> "x64.dmg";
|
||||
};
|
||||
var updateVersion = response.latestVersion().macVersion();
|
||||
var asset = response.assets().stream().filter(a -> a.name().endsWith(suffix)).findAny().orElse(null);
|
||||
if (UpdateMechanism.isUpdateAvailable(updateVersion, currentVersion) && asset != null) {
|
||||
return new DownloadUpdateMechanism.DownloadUpdateInfo(this, updateVersion, asset);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateStep firstStep(DownloadUpdateInfo updateInfo) throws UpdateFailedException {
|
||||
try {
|
||||
Path workDir = Files.createTempDirectory("cryptomator-update");
|
||||
return new UpdateProcessImpl(workDir, updateInfo);
|
||||
} catch (IOException e) {
|
||||
throw new UpdateFailedException("Failed to create temporary directory for update", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class UpdateProcessImpl extends DownloadUpdateStep {
|
||||
|
||||
private final Path workDir;
|
||||
|
||||
public UpdateProcessImpl(Path workDir, DownloadUpdateInfo updateInfo) {
|
||||
var destination = workDir.resolve("update.dmg");
|
||||
var downloadUri = URI.create(updateInfo.asset().downloadUrl());
|
||||
var checksum = HexFormat.of().withLowerCase().parseHex(updateInfo.asset().digest().substring(7)); // remove "sha256:" prefix
|
||||
super(downloadUri, destination, checksum, 60_000_000L); // initially assume 60 MB for the update size
|
||||
this.workDir = workDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UpdateStep nextStep() throws IllegalStateException, IOException {
|
||||
if (!isDone()) {
|
||||
throw new IllegalStateException("Update not yet downloaded");
|
||||
} else if (downloadException != null) {
|
||||
throw new UpdateFailedException("Download failed", downloadException);
|
||||
}
|
||||
return UpdateStep.of("Mounting...", this::mount);
|
||||
}
|
||||
|
||||
private UpdateStep mount() throws IOException {
|
||||
// Extract Cryptomator.app from the .dmg file
|
||||
String script = """
|
||||
hdiutil attach 'update.dmg' -mountpoint "/Volumes/Cryptomator_${MOUNT_ID}" -nobrowse -quiet &&
|
||||
cp -R "/Volumes/Cryptomator_${MOUNT_ID}/Cryptomator.app" 'Cryptomator.app' &&
|
||||
hdiutil detach "/Volumes/Cryptomator_${MOUNT_ID}" -quiet
|
||||
""";
|
||||
var command = List.of("bash", "-c", script);
|
||||
var processBuilder = new ProcessBuilder(command);
|
||||
processBuilder.directory(workDir.toFile());
|
||||
processBuilder.environment().put("MOUNT_ID", UUID.randomUUID().toString());
|
||||
Process p = processBuilder.start();
|
||||
try {
|
||||
if (p.waitFor() != 0) {
|
||||
LOG.error("Failed to extract DMG, exit code: {}, output: {}", p.exitValue(), new String(p.getErrorStream().readAllBytes()));
|
||||
throw new IOException("Failed to extract DMG, exit code: " + p.exitValue());
|
||||
}
|
||||
LOG.debug("Update ready: {}", workDir.resolve("Cryptomator.app"));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new InterruptedIOException("Failed to extract DMG, interrupted");
|
||||
}
|
||||
return UpdateStep.of("Restarting...", this::restart);
|
||||
}
|
||||
|
||||
public UpdateStep restart() throws IllegalStateException, IOException {
|
||||
String selfPath = ProcessHandle.current().info().command().orElse("");
|
||||
String installPath;
|
||||
if (selfPath.startsWith("/Applications/Cryptomator.app")) {
|
||||
installPath = "/Applications/Cryptomator.app";
|
||||
} else if (selfPath.contains("/Cryptomator.app/")) {
|
||||
installPath = selfPath.substring(0, selfPath.indexOf("/Cryptomator.app/")) + "/Cryptomator.app";
|
||||
} else {
|
||||
throw new UpdateFailedException("Cannot determine destination path for Cryptomator.app, current path: " + selfPath);
|
||||
}
|
||||
LOG.info("Restarting to apply Update in {} now...", workDir);
|
||||
String script = """
|
||||
while kill -0 ${CRYPTOMATOR_PID} 2> /dev/null; do sleep 0.5; done;
|
||||
if [ -d "${CRYPTOMATOR_INSTALL_PATH}" ]; then
|
||||
echo "Removing old installation at ${CRYPTOMATOR_INSTALL_PATH}";
|
||||
rm -rf "${CRYPTOMATOR_INSTALL_PATH}"
|
||||
fi
|
||||
mv 'Cryptomator.app' "${CRYPTOMATOR_INSTALL_PATH}";
|
||||
open -a "${CRYPTOMATOR_INSTALL_PATH}";
|
||||
""";
|
||||
Files.writeString(workDir.resolve("install.sh"), script, StandardCharsets.US_ASCII, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
|
||||
var command = List.of("bash", "-c", "nohup bash install.sh >install.log 2>&1 &");
|
||||
var processBuilder = new ProcessBuilder(command);
|
||||
processBuilder.directory(workDir.toFile());
|
||||
processBuilder.environment().put("CRYPTOMATOR_PID", String.valueOf(ProcessHandle.current().pid()));
|
||||
processBuilder.environment().put("CRYPTOMATOR_INSTALL_PATH", installPath);
|
||||
processBuilder.start();
|
||||
|
||||
return UpdateStep.EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user