Merge pull request #479 from kthchew/feature/sparkle-mac
This commit is contained in:
commit
ac8ee9f981
23
.github/workflows/build.yml
vendored
23
.github/workflows/build.yml
vendored
@ -7,6 +7,10 @@ on:
|
|||||||
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
||||||
type: string
|
type: string
|
||||||
default: Debug
|
default: Debug
|
||||||
|
secrets:
|
||||||
|
SPARKLE_ED25519_KEY:
|
||||||
|
description: Private key for signing Sparkle updates
|
||||||
|
required: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -219,6 +223,25 @@ jobs:
|
|||||||
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PolyMC.app/Contents/MacOS/polymc"
|
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PolyMC.app/Contents/MacOS/polymc"
|
||||||
tar -czf ../PolyMC.tar.gz *
|
tar -czf ../PolyMC.tar.gz *
|
||||||
|
|
||||||
|
- name: Make Sparkle signature (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
||||||
|
brew install openssl@3
|
||||||
|
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
||||||
|
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PolyMC.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||||
|
rm ed25519-priv.pem
|
||||||
|
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||||
|
### Artifact Information :information_source:
|
||||||
|
- :memo: Sparkle Signature (ed25519): \`$signature\`
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||||
|
### Artifact Information :information_source:
|
||||||
|
- :warning: Sparkle Signature (ed25519): No private key available (likely a pull request or fork)
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Package (Windows)
|
- name: Package (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
|
2
.github/workflows/trigger_builds.yml
vendored
2
.github/workflows/trigger_builds.yml
vendored
@ -28,3 +28,5 @@ jobs:
|
|||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
with:
|
with:
|
||||||
build_type: Debug
|
build_type: Debug
|
||||||
|
secrets:
|
||||||
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
|
@ -213,6 +213,7 @@ if(UNIX AND APPLE)
|
|||||||
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||||
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||||
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||||
|
set(FRAMEWORK_DEST_DIR "${Launcher_Name}.app/Contents/Frameworks")
|
||||||
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
|
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
|
||||||
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
|
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
|
||||||
|
|
||||||
@ -228,9 +229,15 @@ if(UNIX AND APPLE)
|
|||||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||||
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
||||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2021-2022 ${Launcher_Copyright}")
|
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2021-2022 ${Launcher_Copyright}")
|
||||||
|
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "idALcUIazingvKSSsEa9U7coDVxZVx/ORpOEE/QtJfg=")
|
||||||
|
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://polymc.org/feed/appcast.xml")
|
||||||
|
|
||||||
|
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
||||||
|
set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||||
|
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
||||||
|
|
||||||
# directories to look for dependencies
|
# directories to look for dependencies
|
||||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR})
|
||||||
|
|
||||||
# install as bundle
|
# install as bundle
|
||||||
set(INSTALL_BUNDLE "full")
|
set(INSTALL_BUNDLE "full")
|
||||||
|
@ -61,6 +61,14 @@ Config::Config()
|
|||||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||||
|
|
||||||
|
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
||||||
|
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
||||||
|
|
||||||
|
if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||||
|
{
|
||||||
|
UPDATER_ENABLED = true;
|
||||||
|
}
|
||||||
|
|
||||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||||
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
||||||
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND"))
|
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND"))
|
||||||
|
@ -74,6 +74,12 @@ class Config {
|
|||||||
/// URL for the updater's channel
|
/// URL for the updater's channel
|
||||||
QString UPDATER_BASE;
|
QString UPDATER_BASE;
|
||||||
|
|
||||||
|
/// The public key used to sign releases for the Sparkle updater appcast
|
||||||
|
QString MAC_SPARKLE_PUB_KEY;
|
||||||
|
|
||||||
|
/// URL for the Sparkle updater's appcast
|
||||||
|
QString MAC_SPARKLE_APPCAST_URL;
|
||||||
|
|
||||||
/// User-Agent to use.
|
/// User-Agent to use.
|
||||||
QString USER_AGENT;
|
QString USER_AGENT;
|
||||||
|
|
||||||
|
@ -40,5 +40,9 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
|
<key>SUPublicEDKey</key>
|
||||||
|
<string>${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY}</string>
|
||||||
|
<key>SUFeedURL</key>
|
||||||
|
<string>${MACOSX_SPARKLE_UPDATE_FEED_URL}</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -543,6 +543,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
{
|
{
|
||||||
m_settings.reset(new INISettingsObject(BuildConfig.LAUNCHER_CONFIGFILE, this));
|
m_settings.reset(new INISettingsObject(BuildConfig.LAUNCHER_CONFIGFILE, this));
|
||||||
// Updates
|
// Updates
|
||||||
|
// Multiple channels are separated by spaces
|
||||||
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
|
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
|
||||||
m_settings->registerSetting("AutoUpdate", true);
|
m_settings->registerSetting("AutoUpdate", true);
|
||||||
|
|
||||||
|
@ -157,6 +157,12 @@ set(UPDATE_SOURCES
|
|||||||
updater/UpdateChecker.cpp
|
updater/UpdateChecker.cpp
|
||||||
updater/DownloadTask.h
|
updater/DownloadTask.h
|
||||||
updater/DownloadTask.cpp
|
updater/DownloadTask.cpp
|
||||||
|
updater/ExternalUpdater.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(MAC_UPDATE_SOURCES
|
||||||
|
updater/MacSparkleUpdater.h
|
||||||
|
updater/MacSparkleUpdater.mm
|
||||||
)
|
)
|
||||||
|
|
||||||
# Backend for the news bar... there's usually no news.
|
# Backend for the news bar... there's usually no news.
|
||||||
@ -583,6 +589,10 @@ set(LOGIC_SOURCES
|
|||||||
${ATLAUNCHER_SOURCES}
|
${ATLAUNCHER_SOURCES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
|
||||||
|
endif()
|
||||||
|
|
||||||
SET(LAUNCHER_SOURCES
|
SET(LAUNCHER_SOURCES
|
||||||
# Application base
|
# Application base
|
||||||
Application.h
|
Application.h
|
||||||
@ -987,6 +997,22 @@ target_link_libraries(Launcher_logic
|
|||||||
LocalPeer
|
LocalPeer
|
||||||
Launcher_rainbow
|
Launcher_rainbow
|
||||||
)
|
)
|
||||||
|
if(APPLE)
|
||||||
|
set(CMAKE_MACOSX_RPATH 1)
|
||||||
|
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
|
||||||
|
|
||||||
|
file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256})
|
||||||
|
file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle)
|
||||||
|
|
||||||
|
find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
||||||
|
target_link_libraries(Launcher_logic
|
||||||
|
"-framework AppKit"
|
||||||
|
"-framework Carbon"
|
||||||
|
"-framework Foundation"
|
||||||
|
"-framework ApplicationServices"
|
||||||
|
)
|
||||||
|
target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK})
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(Launcher_logic)
|
target_link_libraries(Launcher_logic)
|
||||||
|
|
||||||
@ -1009,8 +1035,16 @@ install(TARGETS ${Launcher_Name}
|
|||||||
BUNDLE DESTINATION "." COMPONENT Runtime
|
BUNDLE DESTINATION "." COMPONENT Runtime
|
||||||
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
|
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
|
||||||
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
|
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
|
||||||
|
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (UNIX AND APPLE)
|
||||||
|
# Add Sparkle updater
|
||||||
|
# It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of
|
||||||
|
# the framework aren't installed
|
||||||
|
install(DIRECTORY ${MACOSX_SPARKLE_DIR}/Sparkle.framework DESTINATION ${FRAMEWORK_DEST_DIR} USE_SOURCE_PERMISSIONS)
|
||||||
|
endif()
|
||||||
|
|
||||||
#### The bundle mess! ####
|
#### The bundle mess! ####
|
||||||
# Bundle utilities are used to complete the portable packages - they add all the libraries that would otherwise be missing on the target system.
|
# Bundle utilities are used to complete the portable packages - they add all the libraries that would otherwise be missing on the target system.
|
||||||
# NOTE: it seems that this absolutely has to be here, and nowhere else.
|
# NOTE: it seems that this absolutely has to be here, and nowhere else.
|
||||||
|
@ -284,7 +284,7 @@ public:
|
|||||||
TranslatedToolbar newsToolBar;
|
TranslatedToolbar newsToolBar;
|
||||||
QVector<TranslatedToolbar *> all_toolbars;
|
QVector<TranslatedToolbar *> all_toolbars;
|
||||||
|
|
||||||
void createMainToolbarActions(QMainWindow *MainWindow)
|
void createMainToolbarActions(MainWindow *MainWindow)
|
||||||
{
|
{
|
||||||
actionAddInstance = TranslatedAction(MainWindow);
|
actionAddInstance = TranslatedAction(MainWindow);
|
||||||
actionAddInstance->setObjectName(QStringLiteral("actionAddInstance"));
|
actionAddInstance->setObjectName(QStringLiteral("actionAddInstance"));
|
||||||
@ -1029,6 +1029,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
|
|||||||
{
|
{
|
||||||
updater->checkForUpdate(APPLICATION->settings()->get("UpdateChannel").toString(), false);
|
updater->checkForUpdate(APPLICATION->settings()->get("UpdateChannel").toString(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (APPLICATION->updateChecker()->getExternalUpdater())
|
||||||
|
{
|
||||||
|
connect(APPLICATION->updateChecker()->getExternalUpdater(),
|
||||||
|
&ExternalUpdater::canCheckForUpdatesChanged,
|
||||||
|
this,
|
||||||
|
&MainWindow::updatesAllowedChanged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedInstanceById(APPLICATION->settings()->get("SelectedInstance").toString());
|
setSelectedInstanceById(APPLICATION->settings()->get("SelectedInstance").toString());
|
||||||
|
@ -90,6 +90,13 @@ LauncherPage::LauncherPage(QWidget *parent) : QWidget(parent), ui(new Ui::Launch
|
|||||||
{
|
{
|
||||||
APPLICATION->updateChecker()->updateChanList(false);
|
APPLICATION->updateChecker()->updateChanList(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (APPLICATION->updateChecker()->getExternalUpdater())
|
||||||
|
{
|
||||||
|
ui->updateChannelComboBox->setVisible(false);
|
||||||
|
ui->updateChannelDescLabel->setVisible(false);
|
||||||
|
ui->updateChannelLabel->setVisible(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -266,7 +273,16 @@ void LauncherPage::applySettings()
|
|||||||
auto s = APPLICATION->settings();
|
auto s = APPLICATION->settings();
|
||||||
|
|
||||||
// Updates
|
// Updates
|
||||||
s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked());
|
if (BuildConfig.UPDATER_ENABLED && APPLICATION->updateChecker()->getExternalUpdater())
|
||||||
|
{
|
||||||
|
APPLICATION->updateChecker()->getExternalUpdater()->setAutomaticallyChecksForUpdates(
|
||||||
|
ui->autoUpdateCheckBox->isChecked());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
s->set("UpdateChannel", m_currentUpdateChannel);
|
s->set("UpdateChannel", m_currentUpdateChannel);
|
||||||
auto original = s->get("IconTheme").toString();
|
auto original = s->get("IconTheme").toString();
|
||||||
//FIXME: make generic
|
//FIXME: make generic
|
||||||
@ -351,7 +367,16 @@ void LauncherPage::loadSettings()
|
|||||||
{
|
{
|
||||||
auto s = APPLICATION->settings();
|
auto s = APPLICATION->settings();
|
||||||
// Updates
|
// Updates
|
||||||
ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool());
|
if (BuildConfig.UPDATER_ENABLED && APPLICATION->updateChecker()->getExternalUpdater())
|
||||||
|
{
|
||||||
|
ui->autoUpdateCheckBox->setChecked(
|
||||||
|
APPLICATION->updateChecker()->getExternalUpdater()->getAutomaticallyChecksForUpdates());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool());
|
||||||
|
}
|
||||||
|
|
||||||
m_currentUpdateChannel = s->get("UpdateChannel").toString();
|
m_currentUpdateChannel = s->get("UpdateChannel").toString();
|
||||||
//FIXME: make generic
|
//FIXME: make generic
|
||||||
auto theme = s->get("IconTheme").toString();
|
auto theme = s->get("IconTheme").toString();
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="autoUpdateCheckBox">
|
<widget class="QCheckBox" name="autoUpdateCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Check for updates on start?</string>
|
<string>Check for updates automatically</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
87
launcher/updater/ExternalUpdater.h
Normal file
87
launcher/updater/ExternalUpdater.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Kenneth Chew <kenneth.c0@protonmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LAUNCHER_EXTERNALUPDATER_H
|
||||||
|
#define LAUNCHER_EXTERNALUPDATER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A base class for an updater that uses an external library.
|
||||||
|
* This class contains basic functions to control the updater.
|
||||||
|
*
|
||||||
|
* To implement the updater on a new platform, create a new class that inherits from this class and
|
||||||
|
* implement the pure virtual functions.
|
||||||
|
*
|
||||||
|
* The initializer of the new class should have the side effect of starting the automatic updater. That is,
|
||||||
|
* once the class is initialized, the program should automatically check for updates if necessary.
|
||||||
|
*/
|
||||||
|
class ExternalUpdater : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* Check for updates manually, showing the user a progress bar and an alert if no updates are found.
|
||||||
|
*/
|
||||||
|
virtual void checkForUpdates() = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Indicates whether or not to check for updates automatically.
|
||||||
|
*/
|
||||||
|
virtual bool getAutomaticallyChecksForUpdates() = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Indicates the current automatic update check interval in seconds.
|
||||||
|
*/
|
||||||
|
virtual double getUpdateCheckInterval() = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Indicates whether or not beta updates should be checked for in addition to regular releases.
|
||||||
|
*/
|
||||||
|
virtual bool getBetaAllowed() = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set whether or not to check for updates automatically.
|
||||||
|
*/
|
||||||
|
virtual void setAutomaticallyChecksForUpdates(bool check) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the current automatic update check interval in seconds.
|
||||||
|
*/
|
||||||
|
virtual void setUpdateCheckInterval(double seconds) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set whether or not beta updates should be checked for in addition to regular releases.
|
||||||
|
*/
|
||||||
|
virtual void setBetaAllowed(bool allowed) = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/*!
|
||||||
|
* Emits whenever the user's ability to check for updates changes.
|
||||||
|
*
|
||||||
|
* As per Sparkle documentation, "An update check can be made by the user when an update session isn’t in progress,
|
||||||
|
* or when an update or its progress is being shown to the user. A user cannot check for updates when data (such
|
||||||
|
* as the feed or an update) is still being downloaded automatically in the background.
|
||||||
|
*
|
||||||
|
* This property is suitable to use for menu item validation for seeing if checkForUpdates can be invoked."
|
||||||
|
*/
|
||||||
|
void canCheckForUpdatesChanged(bool canCheck);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //LAUNCHER_EXTERNALUPDATER_H
|
126
launcher/updater/MacSparkleUpdater.h
Normal file
126
launcher/updater/MacSparkleUpdater.h
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Kenneth Chew <kenneth.c0@protonmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LAUNCHER_MACSPARKLEUPDATER_H
|
||||||
|
#define LAUNCHER_MACSPARKLEUPDATER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSet>
|
||||||
|
#include "ExternalUpdater.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* An implementation for the updater on macOS that uses the Sparkle framework.
|
||||||
|
*/
|
||||||
|
class MacSparkleUpdater : public ExternalUpdater
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* Start the Sparkle updater, which automatically checks for updates if necessary.
|
||||||
|
*/
|
||||||
|
MacSparkleUpdater();
|
||||||
|
~MacSparkleUpdater() override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Check for updates manually, showing the user a progress bar and an alert if no updates are found.
|
||||||
|
*/
|
||||||
|
void checkForUpdates() override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Indicates whether or not to check for updates automatically.
|
||||||
|
*/
|
||||||
|
bool getAutomaticallyChecksForUpdates() override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Indicates the current automatic update check interval in seconds.
|
||||||
|
*/
|
||||||
|
double getUpdateCheckInterval() override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Indicates the set of Sparkle channels the updater is allowed to find new updates from.
|
||||||
|
*/
|
||||||
|
QSet<QString> getAllowedChannels();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Indicates whether or not beta updates should be checked for in addition to regular releases.
|
||||||
|
*/
|
||||||
|
bool getBetaAllowed() override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set whether or not to check for updates automatically.
|
||||||
|
*
|
||||||
|
* As per Sparkle documentation, "By default, Sparkle asks users on second launch for permission if they want
|
||||||
|
* automatic update checks enabled and sets this property based on their response. If SUEnableAutomaticChecks is
|
||||||
|
* set in the Info.plist, this permission request is not performed however.
|
||||||
|
*
|
||||||
|
* Setting this property will persist in the host bundle’s user defaults. Only set this property if you need
|
||||||
|
* dynamic behavior (e.g. user preferences).
|
||||||
|
*
|
||||||
|
* The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow
|
||||||
|
* reverting this property without kicking off a schedule change immediately."
|
||||||
|
*/
|
||||||
|
void setAutomaticallyChecksForUpdates(bool check) override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the current automatic update check interval in seconds.
|
||||||
|
*
|
||||||
|
* As per Sparkle documentation, "Setting this property will persist in the host bundle’s user defaults. For this
|
||||||
|
* reason, only set this property if you need dynamic behavior (eg user preferences). Otherwise prefer to set
|
||||||
|
* SUScheduledCheckInterval directly in your Info.plist.
|
||||||
|
*
|
||||||
|
* The update schedule cycle will be reset in a short delay after the property’s new value is set. This is to allow
|
||||||
|
* reverting this property without kicking off a schedule change immediately."
|
||||||
|
*/
|
||||||
|
void setUpdateCheckInterval(double seconds) override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Clears all allowed Sparkle channels, returning to the default updater channel behavior.
|
||||||
|
*/
|
||||||
|
void clearAllowedChannels();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set a single Sparkle channel the updater is allowed to find new updates from.
|
||||||
|
*
|
||||||
|
* Items in the default channel can always be found, regardless of this setting. If an empty string is passed,
|
||||||
|
* return to the default behavior.
|
||||||
|
*/
|
||||||
|
void setAllowedChannel(const QString& channel);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set a set of Sparkle channels the updater is allowed to find new updates from.
|
||||||
|
*
|
||||||
|
* Items in the default channel can always be found, regardless of this setting. If an empty set is passed,
|
||||||
|
* return to the default behavior.
|
||||||
|
*/
|
||||||
|
void setAllowedChannels(const QSet<QString>& channels);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set whether or not beta updates should be checked for in addition to regular releases.
|
||||||
|
*/
|
||||||
|
void setBetaAllowed(bool allowed) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Private;
|
||||||
|
|
||||||
|
Private *priv;
|
||||||
|
|
||||||
|
void loadChannelsFromSettings();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //LAUNCHER_MACSPARKLEUPDATER_H
|
222
launcher/updater/MacSparkleUpdater.mm
Normal file
222
launcher/updater/MacSparkleUpdater.mm
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Kenneth Chew <kenneth.c0@protonmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MacSparkleUpdater.h"
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
|
#include <Cocoa/Cocoa.h>
|
||||||
|
#include <Sparkle/Sparkle.h>
|
||||||
|
|
||||||
|
@interface UpdaterObserver : NSObject
|
||||||
|
|
||||||
|
@property(nonatomic, readonly) SPUUpdater* updater;
|
||||||
|
|
||||||
|
/// A callback to run when the state of `canCheckForUpdates` for the `updater` changes.
|
||||||
|
@property(nonatomic, copy) void (^callback) (bool);
|
||||||
|
|
||||||
|
- (id)initWithUpdater:(SPUUpdater*)updater;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation UpdaterObserver
|
||||||
|
|
||||||
|
- (id)initWithUpdater:(SPUUpdater*)updater
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
_updater = updater;
|
||||||
|
[self addObserver:self forKeyPath:@"updater.canCheckForUpdates" options:NSKeyValueObservingOptionNew context:nil];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||||
|
ofObject:(id)object
|
||||||
|
change:(NSDictionary<NSKeyValueChangeKey, id> *)change
|
||||||
|
context:(void *)context
|
||||||
|
{
|
||||||
|
if ([keyPath isEqualToString:@"updater.canCheckForUpdates"])
|
||||||
|
{
|
||||||
|
bool canCheck = [change[NSKeyValueChangeNewKey] boolValue];
|
||||||
|
self.callback(canCheck);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface UpdaterDelegate : NSObject <SPUUpdaterDelegate>
|
||||||
|
|
||||||
|
@property(nonatomic, copy) NSSet<NSString *> *allowedChannels;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation UpdaterDelegate
|
||||||
|
|
||||||
|
- (NSSet<NSString *> *)allowedChannelsForUpdater:(SPUUpdater *)updater
|
||||||
|
{
|
||||||
|
return _allowedChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
class MacSparkleUpdater::Private
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SPUStandardUpdaterController *updaterController;
|
||||||
|
UpdaterObserver *updaterObserver;
|
||||||
|
UpdaterDelegate *updaterDelegate;
|
||||||
|
NSAutoreleasePool *autoReleasePool;
|
||||||
|
};
|
||||||
|
|
||||||
|
MacSparkleUpdater::MacSparkleUpdater()
|
||||||
|
{
|
||||||
|
priv = new MacSparkleUpdater::Private();
|
||||||
|
|
||||||
|
// Enable Cocoa's memory management.
|
||||||
|
NSApplicationLoad();
|
||||||
|
priv->autoReleasePool = [[NSAutoreleasePool alloc] init];
|
||||||
|
|
||||||
|
// Delegate is used for setting/getting allowed update channels.
|
||||||
|
priv->updaterDelegate = [[UpdaterDelegate alloc] init];
|
||||||
|
|
||||||
|
// Controller is the interface for actually doing the updates.
|
||||||
|
priv->updaterController = [[SPUStandardUpdaterController alloc] initWithStartingUpdater:true
|
||||||
|
updaterDelegate:priv->updaterDelegate
|
||||||
|
userDriverDelegate:nil];
|
||||||
|
|
||||||
|
priv->updaterObserver = [[UpdaterObserver alloc] initWithUpdater:priv->updaterController.updater];
|
||||||
|
// Use KVO to run a callback that emits a Qt signal when `canCheckForUpdates` changes, so the UI can respond accordingly.
|
||||||
|
priv->updaterObserver.callback = ^(bool canCheck) {
|
||||||
|
emit canCheckForUpdatesChanged(canCheck);
|
||||||
|
};
|
||||||
|
|
||||||
|
loadChannelsFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
MacSparkleUpdater::~MacSparkleUpdater()
|
||||||
|
{
|
||||||
|
[priv->updaterObserver removeObserver:priv->updaterObserver forKeyPath:@"updater.canCheckForUpdates"];
|
||||||
|
|
||||||
|
[priv->updaterController release];
|
||||||
|
[priv->updaterObserver release];
|
||||||
|
[priv->updaterDelegate release];
|
||||||
|
[priv->autoReleasePool release];
|
||||||
|
delete priv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacSparkleUpdater::checkForUpdates()
|
||||||
|
{
|
||||||
|
[priv->updaterController checkForUpdates:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacSparkleUpdater::getAutomaticallyChecksForUpdates()
|
||||||
|
{
|
||||||
|
return priv->updaterController.updater.automaticallyChecksForUpdates;
|
||||||
|
}
|
||||||
|
|
||||||
|
double MacSparkleUpdater::getUpdateCheckInterval()
|
||||||
|
{
|
||||||
|
return priv->updaterController.updater.updateCheckInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> MacSparkleUpdater::getAllowedChannels()
|
||||||
|
{
|
||||||
|
// Convert NSSet<NSString> -> QSet<QString>
|
||||||
|
__block QSet<QString> channels;
|
||||||
|
[priv->updaterDelegate.allowedChannels enumerateObjectsUsingBlock:^(NSString *channel, BOOL *stop)
|
||||||
|
{
|
||||||
|
channels.insert(QString::fromNSString(channel));
|
||||||
|
}];
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacSparkleUpdater::getBetaAllowed()
|
||||||
|
{
|
||||||
|
return getAllowedChannels().contains("beta");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacSparkleUpdater::setAutomaticallyChecksForUpdates(bool check)
|
||||||
|
{
|
||||||
|
priv->updaterController.updater.automaticallyChecksForUpdates = check ? YES : NO; // make clang-tidy happy
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacSparkleUpdater::setUpdateCheckInterval(double seconds)
|
||||||
|
{
|
||||||
|
priv->updaterController.updater.updateCheckInterval = seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacSparkleUpdater::clearAllowedChannels()
|
||||||
|
{
|
||||||
|
priv->updaterDelegate.allowedChannels = [NSSet set];
|
||||||
|
APPLICATION->settings()->set("UpdateChannel", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacSparkleUpdater::setAllowedChannel(const QString &channel)
|
||||||
|
{
|
||||||
|
if (channel.isEmpty())
|
||||||
|
{
|
||||||
|
clearAllowedChannels();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSSet<NSString *> *nsChannels = [NSSet setWithObject:channel.toNSString()];
|
||||||
|
priv->updaterDelegate.allowedChannels = nsChannels;
|
||||||
|
APPLICATION->settings()->set("UpdateChannel", channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacSparkleUpdater::setAllowedChannels(const QSet<QString> &channels)
|
||||||
|
{
|
||||||
|
if (channels.isEmpty())
|
||||||
|
{
|
||||||
|
clearAllowedChannels();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString channelsConfig = "";
|
||||||
|
// Convert QSet<QString> -> NSSet<NSString>
|
||||||
|
NSMutableSet<NSString *> *nsChannels = [NSMutableSet setWithCapacity:channels.count()];
|
||||||
|
foreach (const QString channel, channels)
|
||||||
|
{
|
||||||
|
[nsChannels addObject:channel.toNSString()];
|
||||||
|
channelsConfig += channel + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->updaterDelegate.allowedChannels = nsChannels;
|
||||||
|
APPLICATION->settings()->set("UpdateChannel", channelsConfig.trimmed());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacSparkleUpdater::setBetaAllowed(bool allowed)
|
||||||
|
{
|
||||||
|
if (allowed)
|
||||||
|
{
|
||||||
|
setAllowedChannel("beta");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clearAllowedChannels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacSparkleUpdater::loadChannelsFromSettings()
|
||||||
|
{
|
||||||
|
QStringList channelList = APPLICATION->settings()->get("UpdateChannel").toString().split(" ");
|
||||||
|
QSet<QString> channels(channelList.begin(), channelList.end());
|
||||||
|
setAllowedChannels(channels);
|
||||||
|
}
|
@ -24,7 +24,6 @@
|
|||||||
#define CHANLIST_FORMAT 0
|
#define CHANLIST_FORMAT 0
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "sys.h"
|
|
||||||
|
|
||||||
UpdateChecker::UpdateChecker(shared_qobject_ptr<QNetworkAccessManager> nam, QString channelUrl, QString currentChannel, int currentBuild)
|
UpdateChecker::UpdateChecker(shared_qobject_ptr<QNetworkAccessManager> nam, QString channelUrl, QString currentChannel, int currentBuild)
|
||||||
{
|
{
|
||||||
@ -32,6 +31,10 @@ UpdateChecker::UpdateChecker(shared_qobject_ptr<QNetworkAccessManager> nam, QStr
|
|||||||
m_channelUrl = channelUrl;
|
m_channelUrl = channelUrl;
|
||||||
m_currentChannel = currentChannel;
|
m_currentChannel = currentChannel;
|
||||||
m_currentBuild = currentBuild;
|
m_currentBuild = currentBuild;
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
m_externalUpdater = new MacSparkleUpdater();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<UpdateChecker::ChannelListEntry> UpdateChecker::getChannelList() const
|
QList<UpdateChecker::ChannelListEntry> UpdateChecker::getChannelList() const
|
||||||
@ -44,71 +47,95 @@ bool UpdateChecker::hasChannels() const
|
|||||||
return !m_channels.isEmpty();
|
return !m_channels.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateChecker::checkForUpdate(QString updateChannel, bool notifyNoUpdate)
|
ExternalUpdater* UpdateChecker::getExternalUpdater()
|
||||||
{
|
{
|
||||||
qDebug() << "Checking for updates.";
|
return m_externalUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
// If the channel list hasn't loaded yet, load it and defer checking for updates until
|
void UpdateChecker::checkForUpdate(const QString& updateChannel, bool notifyNoUpdate)
|
||||||
// later.
|
{
|
||||||
if (!m_chanListLoaded)
|
if (m_externalUpdater)
|
||||||
{
|
{
|
||||||
qDebug() << "Channel list isn't loaded yet. Loading channel list and deferring update check.";
|
m_externalUpdater->setBetaAllowed(updateChannel == "beta");
|
||||||
m_checkUpdateWaiting = true;
|
if (notifyNoUpdate)
|
||||||
m_deferredUpdateChannel = updateChannel;
|
{
|
||||||
updateChanList(notifyNoUpdate);
|
qDebug() << "Checking for updates.";
|
||||||
return;
|
m_externalUpdater->checkForUpdates();
|
||||||
}
|
} else
|
||||||
|
{
|
||||||
if (m_updateChecking)
|
// The updater library already handles automatic update checks.
|
||||||
{
|
return;
|
||||||
qDebug() << "Ignoring update check request. Already checking for updates.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the desired channel within the channel list and get its repo URL. If if cannot be
|
|
||||||
// found, error.
|
|
||||||
QString stableUrl;
|
|
||||||
m_newRepoUrl = "";
|
|
||||||
for (ChannelListEntry entry : m_channels)
|
|
||||||
{
|
|
||||||
qDebug() << "channelEntry = " << entry.id;
|
|
||||||
if(entry.id == "stable") {
|
|
||||||
stableUrl = entry.url;
|
|
||||||
}
|
|
||||||
if (entry.id == updateChannel) {
|
|
||||||
m_newRepoUrl = entry.url;
|
|
||||||
qDebug() << "is intended update channel: " << entry.id;
|
|
||||||
}
|
|
||||||
if (entry.id == m_currentChannel) {
|
|
||||||
m_currentRepoUrl = entry.url;
|
|
||||||
qDebug() << "is current update channel: " << entry.id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
qDebug() << "m_repoUrl = " << m_newRepoUrl;
|
|
||||||
|
|
||||||
if (m_newRepoUrl.isEmpty()) {
|
|
||||||
qWarning() << "m_repoUrl was empty. defaulting to 'stable': " << stableUrl;
|
|
||||||
m_newRepoUrl = stableUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If nothing applies, error
|
|
||||||
if (m_newRepoUrl.isEmpty())
|
|
||||||
{
|
{
|
||||||
qCritical() << "failed to select any update repository for: " << updateChannel;
|
qDebug() << "Checking for updates.";
|
||||||
emit updateCheckFailed();
|
// If the channel list hasn't loaded yet, load it and defer checking for updates until
|
||||||
return;
|
// later.
|
||||||
|
if (!m_chanListLoaded)
|
||||||
|
{
|
||||||
|
qDebug() << "Channel list isn't loaded yet. Loading channel list and deferring update check.";
|
||||||
|
m_checkUpdateWaiting = true;
|
||||||
|
m_deferredUpdateChannel = updateChannel;
|
||||||
|
updateChanList(notifyNoUpdate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_updateChecking)
|
||||||
|
{
|
||||||
|
qDebug() << "Ignoring update check request. Already checking for updates.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the desired channel within the channel list and get its repo URL. If if cannot be
|
||||||
|
// found, error.
|
||||||
|
QString stableUrl;
|
||||||
|
m_newRepoUrl = "";
|
||||||
|
for (ChannelListEntry entry: m_channels)
|
||||||
|
{
|
||||||
|
qDebug() << "channelEntry = " << entry.id;
|
||||||
|
if (entry.id == "stable")
|
||||||
|
{
|
||||||
|
stableUrl = entry.url;
|
||||||
|
}
|
||||||
|
if (entry.id == updateChannel)
|
||||||
|
{
|
||||||
|
m_newRepoUrl = entry.url;
|
||||||
|
qDebug() << "is intended update channel: " << entry.id;
|
||||||
|
}
|
||||||
|
if (entry.id == m_currentChannel)
|
||||||
|
{
|
||||||
|
m_currentRepoUrl = entry.url;
|
||||||
|
qDebug() << "is current update channel: " << entry.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "m_repoUrl = " << m_newRepoUrl;
|
||||||
|
|
||||||
|
if (m_newRepoUrl.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning() << "m_repoUrl was empty. defaulting to 'stable': " << stableUrl;
|
||||||
|
m_newRepoUrl = stableUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If nothing applies, error
|
||||||
|
if (m_newRepoUrl.isEmpty())
|
||||||
|
{
|
||||||
|
qCritical() << "failed to select any update repository for: " << updateChannel;
|
||||||
|
emit updateCheckFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_updateChecking = true;
|
||||||
|
|
||||||
|
QUrl indexUrl = QUrl(m_newRepoUrl).resolved(QUrl("index.json"));
|
||||||
|
|
||||||
|
indexJob = new NetJob("GoUpdate Repository Index", m_network);
|
||||||
|
indexJob->addNetAction(Net::Download::makeByteArray(indexUrl, &indexData));
|
||||||
|
connect(indexJob.get(), &NetJob::succeeded, [this, notifyNoUpdate]() { updateCheckFinished(notifyNoUpdate); });
|
||||||
|
connect(indexJob.get(), &NetJob::failed, this, &UpdateChecker::updateCheckFailed);
|
||||||
|
indexJob->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_updateChecking = true;
|
|
||||||
|
|
||||||
QUrl indexUrl = QUrl(m_newRepoUrl).resolved(QUrl("index.json"));
|
|
||||||
|
|
||||||
indexJob = new NetJob("GoUpdate Repository Index", m_network);
|
|
||||||
indexJob->addNetAction(Net::Download::makeByteArray(indexUrl, &indexData));
|
|
||||||
connect(indexJob.get(), &NetJob::succeeded, [this, notifyNoUpdate](){ updateCheckFinished(notifyNoUpdate); });
|
|
||||||
connect(indexJob.get(), &NetJob::failed, this, &UpdateChecker::updateCheckFailed);
|
|
||||||
indexJob->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateChecker::updateCheckFinished(bool notifyNoUpdate)
|
void UpdateChecker::updateCheckFinished(bool notifyNoUpdate)
|
||||||
|
@ -17,6 +17,11 @@
|
|||||||
|
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
#include "GoUpdate.h"
|
#include "GoUpdate.h"
|
||||||
|
#include "ExternalUpdater.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
#include "MacSparkleUpdater.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
class UpdateChecker : public QObject
|
class UpdateChecker : public QObject
|
||||||
{
|
{
|
||||||
@ -24,7 +29,7 @@ class UpdateChecker : public QObject
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
UpdateChecker(shared_qobject_ptr<QNetworkAccessManager> nam, QString channelUrl, QString currentChannel, int currentBuild);
|
UpdateChecker(shared_qobject_ptr<QNetworkAccessManager> nam, QString channelUrl, QString currentChannel, int currentBuild);
|
||||||
void checkForUpdate(QString updateChannel, bool notifyNoUpdate);
|
void checkForUpdate(const QString& updateChannel, bool notifyNoUpdate);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake).
|
* Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake).
|
||||||
@ -54,6 +59,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool hasChannels() const;
|
bool hasChannels() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns a pointer to an object that controls the external updater, or nullptr if an external updater is not used.
|
||||||
|
*/
|
||||||
|
ExternalUpdater *getExternalUpdater();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
//! Signal emitted when an update is available. Passes the URL for the repo and the ID and name for the version.
|
//! Signal emitted when an update is available. Passes the URL for the repo and the ID and name for the version.
|
||||||
void updateAvailable(GoUpdate::Status status);
|
void updateAvailable(GoUpdate::Status status);
|
||||||
@ -117,5 +127,14 @@ private:
|
|||||||
QString m_currentRepoUrl;
|
QString m_currentRepoUrl;
|
||||||
|
|
||||||
QString m_newRepoUrl;
|
QString m_newRepoUrl;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* If not a nullptr, then the updater here will be used instead of the old updater that uses GoUpdate when
|
||||||
|
* checking for updates.
|
||||||
|
*
|
||||||
|
* As a result, signals from this class won't be emitted, and most of the functions in this class other
|
||||||
|
* than checkForUpdate are not useful. Call functions from this external updater object instead.
|
||||||
|
*/
|
||||||
|
ExternalUpdater *m_externalUpdater = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user