check for update for dev-builds, handle semantic version

This commit is contained in:
Stephen Birarda 2018-06-08 14:47:20 -07:00
parent 112abc9d7e
commit 9506e7edbe
6 changed files with 220 additions and 43 deletions

View file

@ -905,7 +905,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<DiscoverabilityManager>(); DependencyManager::set<DiscoverabilityManager>();
DependencyManager::set<SceneScriptingInterface>(); DependencyManager::set<SceneScriptingInterface>();
DependencyManager::set<OffscreenUi>(); DependencyManager::set<OffscreenUi>();
DependencyManager::set<AutoUpdater>();
DependencyManager::set<Midi>(); DependencyManager::set<Midi>();
DependencyManager::set<PathUtils>(); DependencyManager::set<PathUtils>();
DependencyManager::set<InterfaceDynamicFactory>(); DependencyManager::set<InterfaceDynamicFactory>();
@ -1784,10 +1783,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// If launched from Steam, let it handle updates // If launched from Steam, let it handle updates
const QString HIFI_NO_UPDATER_COMMAND_LINE_KEY = "--no-updater"; const QString HIFI_NO_UPDATER_COMMAND_LINE_KEY = "--no-updater";
bool noUpdater = arguments().indexOf(HIFI_NO_UPDATER_COMMAND_LINE_KEY) != -1; bool noUpdater = arguments().indexOf(HIFI_NO_UPDATER_COMMAND_LINE_KEY) != -1;
if (!noUpdater) { bool buildCanUpdate = BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable
|| BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Master;
if (!noUpdater && buildCanUpdate) {
constexpr auto INSTALLER_TYPE_CLIENT_ONLY = "client_only"; constexpr auto INSTALLER_TYPE_CLIENT_ONLY = "client_only";
auto applicationUpdater = DependencyManager::get<AutoUpdater>(); auto applicationUpdater = DependencyManager::set<AutoUpdater>();
AutoUpdater::InstallerType type = installerType == INSTALLER_TYPE_CLIENT_ONLY AutoUpdater::InstallerType type = installerType == INSTALLER_TYPE_CLIENT_ONLY
? AutoUpdater::InstallerType::CLIENT_ONLY : AutoUpdater::InstallerType::FULL; ? AutoUpdater::InstallerType::CLIENT_ONLY : AutoUpdater::InstallerType::FULL;

View file

@ -21,20 +21,32 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) :
OffscreenQmlDialog(parent) OffscreenQmlDialog(parent)
{ {
auto applicationUpdater = DependencyManager::get<AutoUpdater>(); auto applicationUpdater = DependencyManager::get<AutoUpdater>();
int currentVersion = QCoreApplication::applicationVersion().toInt(); if (applicationUpdater) {
int latestVersion = applicationUpdater.data()->getBuildData().lastKey();
_updateAvailableDetails = "v" + QString::number(latestVersion) + " released on " auto buildData = applicationUpdater.data()->getBuildData();
+ QString(applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]).replace(" ", " "); ApplicationVersion latestVersion = buildData.lastKey();
_updateAvailableDetails = "v" + latestVersion.versionString + " released on "
+ QString(buildData[latestVersion]["releaseTime"]).replace(" ", " ");
_releaseNotes = ""; _releaseNotes = "";
for (int i = latestVersion; i > currentVersion; i--) {
if (applicationUpdater.data()->getBuildData().contains(i)) { auto it = buildData.end();
QString releaseNotes = applicationUpdater.data()->getBuildData()[i]["releaseNotes"]; while (it != buildData.begin()) {
--it;
if (applicationUpdater->getCurrentVersion() < it.key()) {
// grab the release notes for this later version
QString releaseNotes = it.value()["releaseNotes"];
releaseNotes.remove("<br />"); releaseNotes.remove("<br />");
releaseNotes.remove(QRegExp("^\n+")); releaseNotes.remove(QRegExp("^\n+"));
_releaseNotes += "\n" + QString().sprintf("%d", i) + "\n" + releaseNotes + "\n"; _releaseNotes += "\n" + it.key().versionString + "\n" + releaseNotes + "\n";
} else {
break;
} }
} }
}
} }
const QString& UpdateDialog::updateAvailableDetails() const { const QString& UpdateDialog::updateAvailableDetails() const {
@ -47,5 +59,5 @@ const QString& UpdateDialog::releaseNotes() const {
void UpdateDialog::triggerUpgrade() { void UpdateDialog::triggerUpgrade() {
auto applicationUpdater = DependencyManager::get<AutoUpdater>(); auto applicationUpdater = DependencyManager::get<AutoUpdater>();
applicationUpdater.data()->performAutoUpdate(applicationUpdater.data()->getBuildData().lastKey()); applicationUpdater.data()->openLatestUpdateURL();
} }

View file

@ -11,13 +11,16 @@
#include "AutoUpdater.h" #include "AutoUpdater.h"
#include <BuildInfo.h>
#include <NetworkAccessManager.h>
#include <SharedUtil.h>
#include <unordered_map> #include <unordered_map>
AutoUpdater::AutoUpdater() { #include <ApplicationVersion.h>
#include <BuildInfo.h>
#include <NetworkAccessManager.h>
#include <SharedUtil.h>
AutoUpdater::AutoUpdater() :
_currentVersion(BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? BuildInfo::VERSION : BuildInfo::BUILD_NUMBER)
{
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
_operatingSystem = "windows"; _operatingSystem = "windows";
#elif defined Q_OS_MAC #elif defined Q_OS_MAC
@ -33,9 +36,22 @@ void AutoUpdater::checkForUpdate() {
this->getLatestVersionData(); this->getLatestVersionData();
} }
const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml");
const QUrl MASTER_BUILDS_XML_URL("https://highfidelity.com/dev-builds.xml");
void AutoUpdater::getLatestVersionData() { void AutoUpdater::getLatestVersionData() {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest latestVersionRequest(BUILDS_XML_URL);
QUrl buildsURL;
if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable) {
buildsURL = BUILDS_XML_URL;
} else if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Master) {
buildsURL = MASTER_BUILDS_XML_URL;
}
QNetworkRequest latestVersionRequest(buildsURL);
latestVersionRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); latestVersionRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(latestVersionRequest); QNetworkReply* reply = networkAccessManager.get(latestVersionRequest);
@ -52,13 +68,23 @@ void AutoUpdater::parseLatestVersionData() {
QString clientOnly; QString clientOnly;
}; };
int version { 0 }; QString version;
QString downloadUrl; QString downloadUrl;
QString releaseTime; QString releaseTime;
QString releaseNotes; QString releaseNotes;
QString commitSha; QString commitSha;
QString pullRequestNumber; QString pullRequestNumber;
QString versionKey;
// stable builds look at the stable_version node (semantic version)
// master builds look at the version node (build number)
if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable) {
versionKey = "stable_version";
} else if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Master) {
versionKey = "version";
}
while (xml.readNextStartElement()) { while (xml.readNextStartElement()) {
if (xml.name() == "projects") { if (xml.name() == "projects") {
while (xml.readNextStartElement()) { while (xml.readNextStartElement()) {
@ -77,8 +103,8 @@ void AutoUpdater::parseLatestVersionData() {
QHash<QString, InstallerURLs> campaignInstallers; QHash<QString, InstallerURLs> campaignInstallers;
while (xml.readNextStartElement()) { while (xml.readNextStartElement()) {
if (xml.name() == "version") { if (xml.name() == versionKey) {
version = xml.readElementText().toInt(); version = xml.readElementText();
} else if (xml.name() == "url") { } else if (xml.name() == "url") {
downloadUrl = xml.readElementText(); downloadUrl = xml.readElementText();
} else if (xml.name() == "installers") { } else if (xml.name() == "installers") {
@ -159,31 +185,31 @@ void AutoUpdater::parseLatestVersionData() {
} }
void AutoUpdater::checkVersionAndNotify() { void AutoUpdater::checkVersionAndNotify() {
if (BuildInfo::BUILD_TYPE != BuildInfo::BuildType::Stable || _builds.empty()) { if (_builds.empty()) {
// No version checking is required in nightly/PR/dev builds or when no build // no build data was found for this platform
// data was found for the platform
return; return;
} }
int latestVersionAvailable = _builds.lastKey();
if (QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { qDebug() << "Checking if update version" << _builds.lastKey().versionString
<< "is newer than current version" << _currentVersion.versionString;
if (_builds.lastKey() > _currentVersion) {
emit newVersionIsAvailable(); emit newVersionIsAvailable();
} }
} }
void AutoUpdater::performAutoUpdate(int version) { void AutoUpdater::openLatestUpdateURL() {
// NOTE: This is not yet auto updating - however this is a checkpoint towards that end const QMap<QString, QString>& chosenVersion = _builds.last();
// Next PR will handle the automatic download, upgrading and application restart
const QMap<QString, QString>& chosenVersion = _builds.value(version);
const QUrl& downloadUrl = chosenVersion.value("downloadUrl"); const QUrl& downloadUrl = chosenVersion.value("downloadUrl");
QDesktopServices::openUrl(downloadUrl); QDesktopServices::openUrl(downloadUrl);
QCoreApplication::quit(); QCoreApplication::quit();
} }
void AutoUpdater::downloadUpdateVersion(int version) { void AutoUpdater::downloadUpdateVersion(const QString& version) {
emit newVersionIsDownloaded(); emit newVersionIsDownloaded();
} }
void AutoUpdater::appendBuildData(int versionNumber, void AutoUpdater::appendBuildData(const QString& versionNumber,
const QString& downloadURL, const QString& downloadURL,
const QString& releaseTime, const QString& releaseTime,
const QString& releaseNotes, const QString& releaseNotes,
@ -194,6 +220,6 @@ void AutoUpdater::appendBuildData(int versionNumber,
thisBuildDetails.insert("releaseTime", releaseTime); thisBuildDetails.insert("releaseTime", releaseTime);
thisBuildDetails.insert("releaseNotes", releaseNotes); thisBuildDetails.insert("releaseNotes", releaseNotes);
thisBuildDetails.insert("pullRequestNumber", pullRequestNumber); thisBuildDetails.insert("pullRequestNumber", pullRequestNumber);
_builds.insert(versionNumber, thisBuildDetails); _builds.insert(ApplicationVersion(versionNumber), thisBuildDetails);
} }

View file

@ -26,10 +26,9 @@
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <ApplicationVersion.h>
#include <DependencyManager.h> #include <DependencyManager.h>
const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml");
class AutoUpdater : public QObject, public Dependency { class AutoUpdater : public QObject, public Dependency {
Q_OBJECT Q_OBJECT
SINGLETON_DEPENDENCY SINGLETON_DEPENDENCY
@ -43,25 +42,29 @@ public:
}; };
void checkForUpdate(); void checkForUpdate();
const QMap<int, QMap<QString, QString>>& getBuildData() { return _builds; } const QMap<ApplicationVersion, QMap<QString, QString>>& getBuildData() { return _builds; }
void performAutoUpdate(int version); void openLatestUpdateURL();
void setInstallerType(InstallerType type) { _installerType = type; } void setInstallerType(InstallerType type) { _installerType = type; }
void setInstallerCampaign(QString campaign) { _installerCampaign = campaign; } void setInstallerCampaign(QString campaign) { _installerCampaign = campaign; }
const ApplicationVersion& getCurrentVersion() const { return _currentVersion; }
signals: signals:
void latestVersionDataParsed(); void latestVersionDataParsed();
void newVersionIsAvailable(); void newVersionIsAvailable();
void newVersionIsDownloaded(); void newVersionIsDownloaded();
private: private:
QMap<int, QMap<QString, QString>> _builds; QMap<ApplicationVersion, QMap<QString, QString>> _builds;
QString _operatingSystem; QString _operatingSystem;
InstallerType _installerType { InstallerType::FULL }; InstallerType _installerType { InstallerType::FULL };
QString _installerCampaign { "" }; QString _installerCampaign { "" };
ApplicationVersion _currentVersion;
void getLatestVersionData(); void getLatestVersionData();
void downloadUpdateVersion(int version); void downloadUpdateVersion(const QString& version);
void appendBuildData(int versionNumber, void appendBuildData(const QString& versionNumber,
const QString& downloadURL, const QString& downloadURL,
const QString& releaseTime, const QString& releaseTime,
const QString& releaseNotes, const QString& releaseNotes,

View file

@ -0,0 +1,94 @@
//
// ApplicationVersion.cpp
// libraries/shared/src
//
// Created by Stephen Birarda on 6/8/18.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "ApplicationVersion.h"
#include <cassert>
#include <QtCore/QDebug>
#include <QtCore/QRegExp>
#include <QtCore/QStringList>
ApplicationVersion::ApplicationVersion(const QString& versionString) :
versionString(versionString)
{
// attempt to regex out a semantic version from the string
// handling both x.y.z and x.y formats
QRegExp semanticRegex("([\\d]+)\\.([\\d]+)(?:\\.([\\d]+))?");
int pos = semanticRegex.indexIn(versionString);
if (pos != -1) {
isSemantic = true;
auto captures = semanticRegex.capturedTexts();
major = captures[1].toInt();
minor = captures[2].toInt();
if (captures.length() > 3) {
patch = captures[3].toInt();
} else {
// the patch is implictly 0 if it was not included
patch = 0;
}
} else {
// if we didn't have a sematic style, we assume that we just have a build number
build = versionString.toInt();
}
}
bool ApplicationVersion::operator==(const ApplicationVersion& other) const {
if (isSemantic && other.isSemantic) {
return major == other.major && minor == other.minor && patch == other.patch;
} else if (!isSemantic && !other.isSemantic) {
return build == other.build;
} else {
assert(isSemantic == other.isSemantic);
return false;
}
}
bool ApplicationVersion::operator<(const ApplicationVersion& other) const {
if (isSemantic && other.isSemantic) {
if (major == other.major) {
if (minor == other.minor) {
return patch < other.patch;
} else {
return minor < other.minor;
}
} else {
return major < other.major;
}
} else if (!isSemantic && !other.isSemantic) {
return build < other.build;
} else {
assert(isSemantic == other.isSemantic);
return false;
}
}
bool ApplicationVersion::operator>(const ApplicationVersion& other) const {
if (isSemantic && other.isSemantic) {
if (major == other.major) {
if (minor == other.minor) {
return patch > other.patch;
} else {
return minor > other.minor;
}
} else {
return major > other.major;
}
} else if (!isSemantic && !other.isSemantic) {
return build > other.build;
} else {
assert(isSemantic == other.isSemantic);
return false;
}
}

View file

@ -0,0 +1,41 @@
//
// ApplicationVersion.h
// libraries/shared/src
//
// Created by Stephen Birarda on 6/8/18.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_ApplicationVersion_h
#define hifi_ApplicationVersion_h
#include <QtCore/QString>
class ApplicationVersion {
public:
ApplicationVersion(const QString& versionString);
bool operator==(const ApplicationVersion& other) const;
bool operator!=(const ApplicationVersion& other) const { return !(*this == other); }
bool operator <(const ApplicationVersion& other) const;
bool operator >(const ApplicationVersion& other) const;
bool operator >=(const ApplicationVersion& other) const { return (*this == other) || (*this > other); }
bool operator <=(const ApplicationVersion& other) const { return (*this == other) || (*this < other); }
int major = -1;
int minor = -1;
int patch = -1;
int build = -1;
bool isSemantic { false };
QString versionString;
};
#endif // hifi_ApplicationVersion_h