feat(updater): download new & back up old
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
parent
3d3acb7a92
commit
50d5eb0621
@ -1228,7 +1228,7 @@ install(TARGETS ${Launcher_Name}
|
|||||||
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32 OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) )
|
if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER) )
|
||||||
# Updater
|
# Updater
|
||||||
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
|
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
|
||||||
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "PrismUpdater.h"
|
#include "PrismUpdater.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -136,7 +137,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
|
|||||||
{ { "F", "force" }, tr("Force an update, even if one is not needed.") },
|
{ { "F", "force" }, tr("Force an update, even if one is not needed.") },
|
||||||
{ { "l", "list" }, tr("List avalible releases.") },
|
{ { "l", "list" }, tr("List avalible releases.") },
|
||||||
{ "debug", tr("Log debug to console.") },
|
{ "debug", tr("Log debug to console.") },
|
||||||
{ { "L", "latest" }, tr("Update to the latest avalible version.") },
|
{ { "S", "select-ui" }, tr("Select the version to install with a GUI.") },
|
||||||
{ { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } });
|
{ { "D", "allow-downgrade" }, tr("Allow the updater to downgrade to previous verisons.") } });
|
||||||
|
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
@ -145,10 +146,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
|
|||||||
|
|
||||||
logToConsole = parser.isSet("debug");
|
logToConsole = parser.isSet("debug");
|
||||||
|
|
||||||
auto prism_executable = QCoreApplication::applicationDirPath() + "/" + BuildConfig.LAUNCHER_APP_BINARY_NAME;
|
auto prism_executable = QCoreApplication::applicationFilePath();
|
||||||
#if defined(Q_OS_WIN32)
|
|
||||||
prism_executable += ".exe";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (BuildConfig.BUILD_PLATFORM.toLower() == "macos")
|
if (BuildConfig.BUILD_PLATFORM.toLower() == "macos")
|
||||||
showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support instaltions on MacOS"));
|
showFatalErrorMessage(tr("MacOS Not Supported"), tr("The updater does not support instaltions on MacOS"));
|
||||||
@ -157,13 +155,15 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
|
|||||||
showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("The updater can not find the main exacutable."));
|
showFatalErrorMessage(tr("Unsupprted Instaltion"), tr("The updater can not find the main exacutable."));
|
||||||
|
|
||||||
if (prism_executable.startsWith("/tmp/.mount_")) {
|
if (prism_executable.startsWith("/tmp/.mount_")) {
|
||||||
m_appimage = true;
|
m_isAppimage = true;
|
||||||
m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
|
m_appimagePath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
|
||||||
if (m_appimagePath.isEmpty())
|
if (m_appimagePath.isEmpty())
|
||||||
showFatalErrorMessage(tr("Unsupprted Instaltion"),
|
showFatalErrorMessage(tr("Unsupprted Instaltion"),
|
||||||
tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)"));
|
tr("Updater is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_isFlatpak = DesktopServices::isFlatpak();
|
||||||
|
|
||||||
m_prismExecutable = prism_executable;
|
m_prismExecutable = prism_executable;
|
||||||
|
|
||||||
auto prism_update_url = parser.value("update-url");
|
auto prism_update_url = parser.value("update-url");
|
||||||
@ -178,7 +178,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
|
|||||||
if (!user_version.isEmpty()) {
|
if (!user_version.isEmpty()) {
|
||||||
m_userSelectedVersion = Version(user_version);
|
m_userSelectedVersion = Version(user_version);
|
||||||
}
|
}
|
||||||
m_updateLatest = parser.isSet("latest");
|
m_selectUI = parser.isSet("select-ui");
|
||||||
m_allowDowngrade = parser.isSet("allow-downgrade");
|
m_allowDowngrade = parser.isSet("allow-downgrade");
|
||||||
|
|
||||||
QString origcwdPath = QDir::currentPath();
|
QString origcwdPath = QDir::currentPath();
|
||||||
@ -217,7 +217,7 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
|
|||||||
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
||||||
dataPath = m_rootPath;
|
dataPath = m_rootPath;
|
||||||
adjustedBy = "Portable data path";
|
adjustedBy = "Portable data path";
|
||||||
m_portable = true;
|
m_isPortable = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -411,6 +411,24 @@ void PrismUpdaterApp::run()
|
|||||||
return exit(0);
|
return exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_isFlatpak) {
|
||||||
|
showFatalErrorMessage(tr("Updating flatpack not supported"), tr("Actions outside of checking if an update is avalible are not "
|
||||||
|
"supported when running the flatpak verison of Prism Launcher."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_isAppimage) {
|
||||||
|
bool result = true;
|
||||||
|
if (need_update)
|
||||||
|
result = callAppimageUpdate();
|
||||||
|
return exit(result ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BuildConfig.BUILD_PLATFORM.toLower() == "linux" && !m_isPortable) {
|
||||||
|
showFatalErrorMessage(tr("Updating Not Supported"),
|
||||||
|
tr("Updating non-portable linux instalations is not supported. Please use your system package manager"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (need_update || m_forceUpdate || !m_userSelectedVersion.isEmpty()) {
|
if (need_update || m_forceUpdate || !m_userSelectedVersion.isEmpty()) {
|
||||||
GitHubRelease update_release = latest;
|
GitHubRelease update_release = latest;
|
||||||
if (!m_userSelectedVersion.isEmpty()) {
|
if (!m_userSelectedVersion.isEmpty()) {
|
||||||
@ -428,7 +446,7 @@ void PrismUpdaterApp::run()
|
|||||||
QString("Can not find a github relase for user spesified verison %1").arg(m_userSelectedVersion.toString()));
|
QString("Can not find a github relase for user spesified verison %1").arg(m_userSelectedVersion.toString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (!m_updateLatest) {
|
} else if (m_selectUI) {
|
||||||
update_release = selectRelease();
|
update_release = selectRelease();
|
||||||
if (!update_release.isValid()) {
|
if (!update_release.isValid()) {
|
||||||
showFatalErrorMessage("No version selected.", "No version was selected.");
|
showFatalErrorMessage("No version selected.", "No version was selected.");
|
||||||
@ -497,18 +515,19 @@ QList<GitHubReleaseAsset> PrismUpdaterApp::validReleaseArtifacts(const GitHubRel
|
|||||||
{
|
{
|
||||||
QList<GitHubReleaseAsset> valid;
|
QList<GitHubReleaseAsset> valid;
|
||||||
|
|
||||||
qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM << "portable:" << m_portable;
|
qDebug() << "Selecting best asset from" << release.tag_name << "for platfom" << BuildConfig.BUILD_PLATFORM
|
||||||
|
<< "portable:" << m_isPortable;
|
||||||
if (BuildConfig.BUILD_PLATFORM.isEmpty())
|
if (BuildConfig.BUILD_PLATFORM.isEmpty())
|
||||||
qWarning() << "Build platform is not set!";
|
qWarning() << "Build platform is not set!";
|
||||||
for (auto asset : release.assets) {
|
for (auto asset : release.assets) {
|
||||||
if (!m_appimage && asset.name.toLower().endsWith("appimage"))
|
if (!m_isAppimage && asset.name.toLower().endsWith("appimage"))
|
||||||
continue;
|
continue;
|
||||||
else if (m_appimage && !asset.name.toLower().endsWith("appimage"))
|
else if (m_isAppimage && !asset.name.toLower().endsWith("appimage"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower());
|
bool for_platform = !BuildConfig.BUILD_PLATFORM.isEmpty() && asset.name.toLower().contains(BuildConfig.BUILD_PLATFORM.toLower());
|
||||||
bool for_portable = asset.name.toLower().contains("portable");
|
bool for_portable = asset.name.toLower().contains("portable");
|
||||||
if (((m_portable && for_portable) || (!m_portable && !for_portable)) && for_platform) {
|
if (((m_isPortable && for_portable) || (!m_isPortable && !for_portable)) && for_platform) {
|
||||||
valid.append(asset);
|
valid.append(asset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -536,35 +555,148 @@ void PrismUpdaterApp::performUpdate(const GitHubRelease& release)
|
|||||||
|
|
||||||
GitHubReleaseAsset selected_asset;
|
GitHubReleaseAsset selected_asset;
|
||||||
if (valid_assets.isEmpty()) {
|
if (valid_assets.isEmpty()) {
|
||||||
showFatalErrorMessage(tr("No Valid Release Assets"),
|
return showFatalErrorMessage(tr("No Valid Release Assets"),
|
||||||
tr("Github release %1 has no valid assets for this platform: %2")
|
tr("Github release %1 has no valid assets for this platform: %2")
|
||||||
.arg(release.tag_name)
|
.arg(release.tag_name)
|
||||||
.arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_portable)));
|
.arg(tr("%1 portable: %2").arg(BuildConfig.BUILD_PLATFORM).arg(m_isPortable)));
|
||||||
return;
|
|
||||||
} else if (valid_assets.length() > 1) {
|
} else if (valid_assets.length() > 1) {
|
||||||
selected_asset = selectAsset(valid_assets);
|
selected_asset = selectAsset(valid_assets);
|
||||||
} else {
|
} else {
|
||||||
selected_asset = valid_assets.takeFirst();
|
selected_asset = valid_assets.takeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! selected_asset.isValid()) {
|
if (!selected_asset.isValid()) {
|
||||||
showFatalErrorMessage("No version selected.", "No version was selected.");
|
return showFatalErrorMessage(tr("No version selected."), tr("No version was selected."));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "will intall" << selected_asset;
|
qDebug() << "will install" << selected_asset;
|
||||||
|
auto file = downloadAsset(selected_asset);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
return showFatalErrorMessage(tr("Failed to Download"), tr("Failed to download the selected asset."));
|
||||||
|
}
|
||||||
|
|
||||||
|
performInstall(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset)
|
QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset)
|
||||||
{
|
{
|
||||||
// TODO! (researching what to do with appimages)
|
auto temp_dir = QDir::tempPath();
|
||||||
|
auto file_url = QUrl(asset.browser_download_url);
|
||||||
|
auto out_file_path = FS::PathCombine(temp_dir, file_url.fileName());
|
||||||
|
|
||||||
|
auto download = Net::Download::makeFile(file_url, out_file_path);
|
||||||
|
|
||||||
|
auto progress_dialog = ProgressDialog();
|
||||||
|
|
||||||
|
if (progress_dialog.execWithTask(download.get()) == QDialog::Rejected)
|
||||||
|
showFatalErrorMessage(tr("Download Aborted"), tr("Download of %1 aborted by user").arg(file_url.toString()));
|
||||||
|
|
||||||
|
QFileInfo out_file(out_file_path);
|
||||||
|
return out_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrismUpdaterApp::callAppimageUpdate()
|
||||||
|
{
|
||||||
|
QProcess proc = QProcess();
|
||||||
|
proc.setProgram("AppImageUpdate");
|
||||||
|
proc.setArguments({ QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")) });
|
||||||
|
return proc.startDetached();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrismUpdaterApp::performInstall(QFileInfo file)
|
||||||
|
{
|
||||||
|
// TODO setup marker file
|
||||||
|
if (m_isPortable) {
|
||||||
|
unpackAndInstall(file);
|
||||||
|
} else {
|
||||||
|
QProcess proc = QProcess();
|
||||||
|
proc.setProgram(file.absoluteFilePath());
|
||||||
|
bool result = proc.startDetached();
|
||||||
|
exit(result ? 0 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrismUpdaterApp::unpackAndInstall(QFileInfo to_install_file)
|
||||||
|
{
|
||||||
|
backupAppDir();
|
||||||
|
// TODO: uppack (rename access failures)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrismUpdaterApp::backupAppDir()
|
||||||
|
{
|
||||||
|
auto manifest_path = FS::PathCombine(QCoreApplication::applicationDirPath(), "manifest.txt");
|
||||||
|
QFileInfo manifest(manifest_path);
|
||||||
|
|
||||||
|
QStringList file_list;
|
||||||
|
if (manifest.isFile()) {
|
||||||
|
// load manifest from file
|
||||||
|
try {
|
||||||
|
auto contents = QString::fromUtf8(FS::read(manifest.absoluteFilePath()));
|
||||||
|
file_list.append(contents.split('\n'));
|
||||||
|
} catch (FS::FileSystemException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_list.isEmpty()) {
|
||||||
|
// best guess
|
||||||
|
if (BuildConfig.BUILD_PLATFORM.toLower() == "linux") {
|
||||||
|
file_list.append({ "PrismLauncher", "bin/*", "share/*", "lib/*" });
|
||||||
|
} else { // windows by process of elimination
|
||||||
|
file_list.append({
|
||||||
|
"jars/*",
|
||||||
|
"prismlauncher.exe",
|
||||||
|
"prismlauncher_filelink.exe",
|
||||||
|
"qtlogging.ini",
|
||||||
|
"imageformats/*",
|
||||||
|
"iconengines/*",
|
||||||
|
"platforms/*",
|
||||||
|
"styles/*",
|
||||||
|
"styles/*",
|
||||||
|
"tls/*",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_list.append("portable.txt");
|
||||||
|
qDebug() << "manifest.txt empty or missing. makking best guess at files to back up.";
|
||||||
|
}
|
||||||
|
qDebug() << "Backing up" << file_list;
|
||||||
|
|
||||||
|
QDir app_dir = QCoreApplication::applicationDirPath();
|
||||||
|
auto backup_dir = FS::PathCombine(app_dir.absolutePath(), QStringLiteral("backup_") + m_prismVerison + ":" + m_prismGitCommit);
|
||||||
|
FS::ensureFolderPathExists(backup_dir);
|
||||||
|
|
||||||
|
for (auto glob : file_list) {
|
||||||
|
QDirIterator iter(app_dir.absolutePath(), QStringList({ glob }), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot,
|
||||||
|
QDirIterator::Subdirectories);
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
auto to_bak_file = iter.next();
|
||||||
|
auto rel_path = app_dir.relativeFilePath(to_bak_file);
|
||||||
|
auto bak_path = FS::PathCombine(backup_dir, rel_path);
|
||||||
|
|
||||||
|
if (QFileInfo(to_bak_file).isFile()) {
|
||||||
|
qDebug() << "Backing up and then removing" << to_bak_file;
|
||||||
|
FS::ensureFilePathExists(bak_path);
|
||||||
|
auto result = FS::copy(to_bak_file, bak_path)();
|
||||||
|
if (!result) {
|
||||||
|
qWarning() << "Failed to backup" << to_bak_file << "to" << bak_path;
|
||||||
|
} else {
|
||||||
|
if (!FS::deletePath(to_bak_file))
|
||||||
|
qWarning() << "Failed to remove" << to_bak_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path)
|
void PrismUpdaterApp::loadPrismVersionFromExe(const QString& exe_path)
|
||||||
{
|
{
|
||||||
QProcess proc = QProcess();
|
QProcess proc = QProcess();
|
||||||
proc.start(exe_path, { "-v" });
|
proc.start(exe_path, { "-v" });
|
||||||
proc.waitForFinished();
|
if (!proc.waitForStarted(5000)) // wait 5 seconds to start
|
||||||
|
return showFatalErrorMessage(tr("Failed to Check Version"), tr("Failed to launcher child launcher process to read verison."));
|
||||||
|
if (!proc.waitForFinished(5000))
|
||||||
|
return showFatalErrorMessage(tr("Failed to Check Version"), tr("Child launcher process failed."));
|
||||||
auto out = proc.readAll();
|
auto out = proc.readAll();
|
||||||
auto lines = out.split('\n');
|
auto lines = out.split('\n');
|
||||||
if (lines.length() < 2)
|
if (lines.length() < 2)
|
||||||
|
@ -77,8 +77,12 @@ class PrismUpdaterApp : public QApplication {
|
|||||||
QList<GitHubReleaseAsset> validReleaseArtifacts(const GitHubRelease& release);
|
QList<GitHubReleaseAsset> validReleaseArtifacts(const GitHubRelease& release);
|
||||||
GitHubReleaseAsset selectAsset(const QList<GitHubReleaseAsset>& assets);
|
GitHubReleaseAsset selectAsset(const QList<GitHubReleaseAsset>& assets);
|
||||||
void performUpdate(const GitHubRelease& release);
|
void performUpdate(const GitHubRelease& release);
|
||||||
|
void performInstall(QFileInfo file);
|
||||||
|
void unpackAndInstall(QFileInfo file);
|
||||||
|
void backupAppDir();
|
||||||
|
|
||||||
QFileInfo downloadAsset(const GitHubReleaseAsset& asset);
|
QFileInfo downloadAsset(const GitHubReleaseAsset& asset);
|
||||||
|
bool callAppimageUpdate();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void downloadError(QString reason);
|
void downloadError(QString reason);
|
||||||
@ -86,11 +90,12 @@ class PrismUpdaterApp : public QApplication {
|
|||||||
private:
|
private:
|
||||||
const QString& root() { return m_rootPath; }
|
const QString& root() { return m_rootPath; }
|
||||||
|
|
||||||
bool isPortable() { return m_portable; }
|
bool isPortable() { return m_isPortable; }
|
||||||
|
|
||||||
QString m_rootPath;
|
QString m_rootPath;
|
||||||
bool m_portable = false;
|
bool m_isPortable = false;
|
||||||
bool m_appimage = false;
|
bool m_isAppimage = false;
|
||||||
|
bool m_isFlatpak = false;
|
||||||
QString m_appimagePath;
|
QString m_appimagePath;
|
||||||
QString m_prismExecutable;
|
QString m_prismExecutable;
|
||||||
QUrl m_prismRepoUrl;
|
QUrl m_prismRepoUrl;
|
||||||
@ -98,7 +103,7 @@ class PrismUpdaterApp : public QApplication {
|
|||||||
bool m_checkOnly;
|
bool m_checkOnly;
|
||||||
bool m_forceUpdate;
|
bool m_forceUpdate;
|
||||||
bool m_printOnly;
|
bool m_printOnly;
|
||||||
bool m_updateLatest;
|
bool m_selectUI;
|
||||||
bool m_allowDowngrade;
|
bool m_allowDowngrade;
|
||||||
|
|
||||||
QString m_prismBinaryName;
|
QString m_prismBinaryName;
|
||||||
|
Loading…
Reference in New Issue
Block a user