diff --git a/app/src/main/java/io/xpipe/app/hub/list/StoreSectionDrag.java b/app/src/main/java/io/xpipe/app/hub/list/StoreSectionDrag.java index af69afeeb..3bd11b9ce 100644 --- a/app/src/main/java/io/xpipe/app/hub/list/StoreSectionDrag.java +++ b/app/src/main/java/io/xpipe/app/hub/list/StoreSectionDrag.java @@ -157,15 +157,16 @@ public class StoreSectionDrag { && selection.stream().allMatch(wrapper -> wrapper.getPinToTop().get())) { var defParent = DataStorage.get() .getDefaultDisplayParent(selection.getFirst().getEntry()); - if (defParent.isPresent() + var allSameParent = selection.stream().allMatch(wrapper -> DataStorage.get() + .getDefaultDisplayParent(wrapper.getEntry()) + .equals(defParent)); + if (allSameParent && defParent.isPresent() && parent.get().getWrapper() != null && defParent.get().equals(parent.get().getEntry())) { - var allSameParent = selection.stream().allMatch(wrapper -> DataStorage.get() - .getDefaultDisplayParent(wrapper.getEntry()) - .equals(defParent)); - if (allSameParent) { - return true; - } + return true; + } else if (allSameParent && defParent.isPresent() + && defParent.get().equals(section.getWrapper().getEntry())) { + return true; } } diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorageCompatibilityCheck.java b/app/src/main/java/io/xpipe/app/storage/DataStorageCompatibilityCheck.java index 70dd8e124..01b2b3e5c 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorageCompatibilityCheck.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorageCompatibilityCheck.java @@ -1,9 +1,20 @@ package io.xpipe.app.storage; +import io.xpipe.app.browser.menu.impl.compress.UntarActionProvider; +import io.xpipe.app.core.AppSystemInfo; import io.xpipe.app.core.AppVersion; import io.xpipe.app.core.mode.AppOperationMode; +import io.xpipe.app.issue.ErrorAction; +import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEventFactory; +import io.xpipe.app.prefs.ExternalApplicationHelper; +import io.xpipe.app.process.CommandBuilder; +import io.xpipe.app.process.LocalShell; +import io.xpipe.app.process.ShellDialects; +import io.xpipe.app.update.AppDownloads; +import io.xpipe.app.update.AppRelease; import io.xpipe.app.util.DocumentationLink; +import io.xpipe.app.util.OsType; import java.io.IOException; import java.nio.file.Files; @@ -40,13 +51,63 @@ public class DataStorageCompatibilityCheck { ErrorEventFactory.fromMessage("The vault" + (version.isPresent() ? " from v" + version.get() + " " : " ") + "comes from an XPipe version prior to v24." - + " This legacy format is unsupported in newer versions. To migrate your data, first install and launch XPipe v23.99." - + " This will start a migration for the vault data. Afterwards, you can upgrade to XPipe v24+ and launch it as normal." - + " XPipe will now exit.") + + " This legacy format is unsupported in newer versions. To migrate your data, you need to first install and launch XPipe v23.99." + + " This will start a migration for the vault data. Afterwards, you can upgrade to XPipe v24+ and launch it as normal.") + .customAction(new ErrorAction() { + @Override + public String getName() { + return "Perform automatically"; + } + + @Override + public String getDescription() { + return "Download and launch XPipe v23.99 to perform the migration"; + } + + @Override + public boolean handle(ErrorEvent event) throws Exception { + runTransitoryBuild(); + return true; + } + }) .documentationLink(DocumentationLink.MIGRATION) .expected() - .term() .handle(); AppOperationMode.shutdown(false); } + + private static void runTransitoryBuild() throws Exception { + var downloaded = AppDownloads.downloadInstaller(AppRelease.ofPortable("23.99")); + var tempTarget = AppSystemInfo.ofCurrent().getTemp().resolve("xpipe-v23.99"); + var shell = LocalShell.get(DataStorageCompatibilityCheck.class); + switch (OsType.ofLocal()) { + case OsType.Linux ignored -> { + shell.command(CommandBuilder.of() + .add("tar", "-f") + .addFile(downloaded) + .add("--strip-components", "1") + .add("-C") + .addFile(tempTarget) + .add("-xz")).execute(); + var executable = tempTarget.resolve("bin", "xpiped"); + AppOperationMode.executeAfterShutdown(() -> { + ExternalApplicationHelper.startAsync(CommandBuilder.of().addFile(executable)); + }); + } + case OsType.MacOs ignored -> { + AppOperationMode.executeAfterShutdown(() -> { + ExternalApplicationHelper.startAsync(CommandBuilder.of().add("open").addFile(tempTarget)); + }); + } + case OsType.Windows ignored -> { + shell.enforceDialect(ShellDialects.POWERSHELL, pw -> { + var command = CommandBuilder.of().add("Expand-Archive", "-Force"); + command.add("-DestinationPath").addFile(tempTarget); + command.add("-Path").addFile(downloaded); + pw.command(command).execute(); + return null; + }); + } + } + } } diff --git a/app/src/main/java/io/xpipe/app/update/AppDownloads.java b/app/src/main/java/io/xpipe/app/update/AppDownloads.java index f8d2cc0eb..6fd52d3fe 100644 --- a/app/src/main/java/io/xpipe/app/update/AppDownloads.java +++ b/app/src/main/java/io/xpipe/app/update/AppDownloads.java @@ -21,9 +21,8 @@ import java.time.Duration; public class AppDownloads { - public static Path downloadInstaller(String version) throws Exception { + public static Path downloadInstaller(AppRelease release) throws Exception { try { - var release = AppRelease.of(version); var builder = HttpRequest.newBuilder(); var httpRequest = builder.uri(URI.create(release.getUrl())).GET().build(); var client = HttpHelper.client(); @@ -33,7 +32,7 @@ public class AppDownloads { var downloadFile = AppCache.getBasePath().resolve(release.getFile()); Files.write(downloadFile, response.body()); TrackEvent.withInfo("Downloaded asset") - .tag("version", version) + .tag("version", release.getTag()) .tag("url", release.getUrl()) .tag("size", FileUtils.byteCountToDisplaySize(response.body().length)) .tag("target", downloadFile) @@ -116,7 +115,7 @@ public class AppDownloads { Duration.ofSeconds(20)); } } - return AppRelease.of(ver); + return AppRelease.ofInstaller(ver); } catch (Exception e) { throw ErrorEventFactory.expected(e); } diff --git a/app/src/main/java/io/xpipe/app/update/AppRelease.java b/app/src/main/java/io/xpipe/app/update/AppRelease.java index 0983cf66c..a444afefb 100644 --- a/app/src/main/java/io/xpipe/app/update/AppRelease.java +++ b/app/src/main/java/io/xpipe/app/update/AppRelease.java @@ -14,7 +14,7 @@ public class AppRelease { String browserUrl; String file; - public static AppRelease of(String tag) { + public static AppRelease ofInstaller(String tag) { var type = AppInstaller.getSuitablePlatformAsset(); var os = switch (OsType.ofLocal()) { @@ -30,4 +30,26 @@ public class AppRelease { .formatted(AppNames.ofCurrent().getKebapName(), tag); return new AppRelease(tag, url, browser, name); } + + public static AppRelease ofPortable(String tag) { + var ext = + switch (OsType.ofLocal()) { + case OsType.Linux ignored -> ".dmg"; + case OsType.MacOs ignored -> ".tar.gz"; + case OsType.Windows ignored -> ".zip"; + }; + var os = + switch (OsType.ofLocal()) { + case OsType.Linux ignored -> "linux"; + case OsType.MacOs ignored -> "macos"; + case OsType.Windows ignored -> "windows"; + }; + var arch = AppProperties.get().getArch(); + var name = "xpipe-portable-%s-%s.%s".formatted(os, arch, ext); + var url = "https://github.com/xpipe-io/%s/releases/download/%s/%s" + .formatted(AppNames.ofCurrent().getKebapName(), tag, name); + var browser = "https://github.com/xpipe-io/%s/releases/%s" + .formatted(AppNames.ofCurrent().getKebapName(), tag); + return new AppRelease(tag, url, browser, name); + } } diff --git a/app/src/main/java/io/xpipe/app/update/ChocoUpdater.java b/app/src/main/java/io/xpipe/app/update/ChocoUpdater.java index c95ef7288..cff7b5493 100644 --- a/app/src/main/java/io/xpipe/app/update/ChocoUpdater.java +++ b/app/src/main/java/io/xpipe/app/update/ChocoUpdater.java @@ -96,7 +96,7 @@ public class ChocoUpdater extends UpdateHandler { if (chocoRelease.isEmpty() || (!chocoRelease.get().equals(rel.getTag()) && !chocoRelease.get().equals(rel.getTag() + ".0"))) { - rel = AppRelease.of(AppProperties.get().getVersion()); + rel = AppRelease.ofInstaller(AppProperties.get().getVersion()); } var isUpdate = isUpdate(rel.getTag()); diff --git a/app/src/main/java/io/xpipe/app/update/GitHubUpdater.java b/app/src/main/java/io/xpipe/app/update/GitHubUpdater.java index 3e3a76aa4..ff75d6243 100644 --- a/app/src/main/java/io/xpipe/app/update/GitHubUpdater.java +++ b/app/src/main/java/io/xpipe/app/update/GitHubUpdater.java @@ -50,7 +50,7 @@ public class GitHubUpdater extends UpdateHandler { public void prepareUpdateImpl() throws Exception { var downloadFile = - AppDownloads.downloadInstaller(lastUpdateCheckResult.getValue().getVersion()); + AppDownloads.downloadInstaller(AppRelease.ofInstaller(lastUpdateCheckResult.getValue().getVersion())); var changelogString = AppDownloads.downloadChangelog(lastUpdateCheckResult.getValue().getVersion()); var rel = new PreparedUpdate( diff --git a/app/src/main/java/io/xpipe/app/update/WingetUpdater.java b/app/src/main/java/io/xpipe/app/update/WingetUpdater.java index 729284bfb..7af0e5ff1 100644 --- a/app/src/main/java/io/xpipe/app/update/WingetUpdater.java +++ b/app/src/main/java/io/xpipe/app/update/WingetUpdater.java @@ -88,7 +88,7 @@ public class WingetUpdater extends UpdateHandler { var wingetRelease = getOutdatedPackageUpdateVersion(); // Use current release if the update is not available for winget yet if (wingetRelease.isPresent() && !wingetRelease.get().equals(rel.getTag())) { - rel = AppRelease.of(AppProperties.get().getVersion()); + rel = AppRelease.ofInstaller(AppProperties.get().getVersion()); } var isUpdate = isUpdate(rel.getTag()); diff --git a/dist/changelog/24.0.md b/dist/changelog/24.0.md index 31beb4de6..ab2a4a01f 100644 --- a/dist/changelog/24.0.md +++ b/dist/changelog/24.0.md @@ -128,7 +128,7 @@ The latest release of the [apple container runtime](https://github.com/apple/con Up until now, XPipe updates have automatically migrated data and supported legacy vaults since XPipe v1.0. Since then, quite a few issues have accumulated, so it is best action to fix those all at once. This will result in some incompatibilities and migration limitations when upgrading an existing install to v24+: -- Upgrading directly to v24 is only possible from v23.9. If you are running an earlier version of XPipe, please update first to v23.9 and launch it once to initiate a vault migration. After that is done, you can upgrade to v24. If you skip this step, XPipe v24 will show a warning and exit. +- Upgrading directly to v24 is only possible from v23.99. If you are running an earlier version of XPipe, please update first to v23.99 and launch it once to initiate a vault migration. After that is done, you can upgrade to v24. If you skip this step, XPipe v24 will show a warning and exit. - Team vaults with more than 1 user assigned will be removed and reset to the original state where all connections are available to all users. The team vault system has been changed to role-based access control, which means that the old user system is no longer supported. You can create a new team vault with roles, which is a much better option now in v24.