Improve installer campaign+type for updates and user activity data

This commit is contained in:
Ryan Huffman 2018-04-17 15:38:40 -07:00
parent a0e1d2e54e
commit 4c8b8d8442
3 changed files with 148 additions and 78 deletions

View file

@ -1272,49 +1272,48 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup // Make sure we don't time out during slow operations at startup
updateHeartbeat(); updateHeartbeat();
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. constexpr auto INSTALLER_INI_NAME = "installer.ini";
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug. auto iniPath = QDir(applicationDirPath()).filePath(INSTALLER_INI_NAME);
static const QString TESTER = "HIFI_TESTER"; QFile installerFile { iniPath };
auto gpuIdent = GPUIdent::getInstance(); std::unordered_map<QString, QString> installerKeyValues;
auto glContextData = getGLContextData(); if (installerFile.open(QIODevice::ReadOnly)) {
QJsonObject properties = { while (!installerFile.atEnd()) {
{ "version", applicationVersion() }, auto line = installerFile.readLine();
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) }, if (!line.isEmpty()) {
{ "previousSessionCrashed", _previousSessionCrashed }, auto index = line.indexOf("=");
{ "previousSessionRuntime", sessionRunTime.get() }, if (index >= 0) {
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() }, installerKeyValues[line.mid(0, index).trimmed()] = line.mid(index + 1).trimmed();
{ "kernel_type", QSysInfo::kernelType() }, }
{ "kernel_version", QSysInfo::kernelVersion() }, }
{ "os_type", QSysInfo::productType() }, }
{ "os_version", QSysInfo::productVersion() },
{ "gpu_name", gpuIdent->getName() },
{ "gpu_driver", gpuIdent->getDriver() },
{ "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },
{ "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },
{ "gl_version", glContextData["version"] },
{ "gl_vender", glContextData["vendor"] },
{ "gl_sl_version", glContextData["sl_version"] },
{ "gl_renderer", glContextData["renderer"] },
{ "ideal_thread_count", QThread::idealThreadCount() }
};
auto macVersion = QSysInfo::macVersion();
if (macVersion != QSysInfo::MV_None) {
properties["os_osx_version"] = QSysInfo::macVersion();
}
auto windowsVersion = QSysInfo::windowsVersion();
if (windowsVersion != QSysInfo::WV_None) {
properties["os_win_version"] = QSysInfo::windowsVersion();
} }
ProcessorInfo procInfo; // In practice we shouldn't run across installs that don't have a known installer type.
if (getProcessorInfo(procInfo)) { // Client or Client+Server installs should always have the installer.ini next to their
properties["processor_core_count"] = procInfo.numProcessorCores; // respective interface.exe, and Steam installs will be detected as such. If a user were
properties["logical_processor_count"] = procInfo.numLogicalProcessors; // to delete the installer.ini, though, and as an example, we won't know the context of the
properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1; // original install.
properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2; constexpr auto INSTALLER_KEY_TYPE = "type";
properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3; constexpr auto INSTALLER_KEY_CAMPAIGN = "campaign";
constexpr auto INSTALLER_TYPE_UNKNOWN = "unknown";
constexpr auto INSTALLER_TYPE_STEAM = "steam";
auto typeIt = installerKeyValues.find(INSTALLER_KEY_TYPE);
QString installerType = INSTALLER_TYPE_UNKNOWN;
if (typeIt == installerKeyValues.end()) {
if (property(hifi::properties::STEAM).toBool()) {
installerType = INSTALLER_TYPE_STEAM;
}
} else {
installerType = typeIt->second;
} }
auto campaignIt = installerKeyValues.find(INSTALLER_KEY_CAMPAIGN);
QString installerCampaign = campaignIt != installerKeyValues.end() ? campaignIt->second : "";
qDebug() << "Detected installer type:" << installerType;
qDebug() << "Detected installer campaign:" << installerCampaign;
// add firstRun flag from settings to launch event // add firstRun flag from settings to launch event
Setting::Handle<bool> firstRun { Settings::firstRun, true }; Setting::Handle<bool> firstRun { Settings::firstRun, true };
@ -1336,6 +1335,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
QJsonObject properties = { QJsonObject properties = {
{ "version", applicationVersion() }, { "version", applicationVersion() },
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) }, { "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) },
{ "installer_campaign", installerCampaign },
{ "installer_type", installerType },
{ "previousSessionCrashed", _previousSessionCrashed }, { "previousSessionCrashed", _previousSessionCrashed },
{ "previousSessionRuntime", sessionRunTime.get() }, { "previousSessionRuntime", sessionRunTime.get() },
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() }, { "cpu_architecture", QSysInfo::currentCpuArchitecture() },
@ -1654,7 +1655,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
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) { if (!noUpdater) {
constexpr auto INSTALLER_TYPE_CLIENT_ONLY = "client_only";
auto applicationUpdater = DependencyManager::get<AutoUpdater>(); auto applicationUpdater = DependencyManager::get<AutoUpdater>();
AutoUpdater::InstallerType type = installerType == INSTALLER_TYPE_CLIENT_ONLY
? AutoUpdater::InstallerType::CLIENT_ONLY : AutoUpdater::InstallerType::FULL;
applicationUpdater->setInstallerType(type);
applicationUpdater->setInstallerCampaign(installerCampaign);
connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);
applicationUpdater->checkForUpdate(); applicationUpdater->checkForUpdate();
} }

View file

@ -13,6 +13,7 @@
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <unordered_map>
AutoUpdater::AutoUpdater() { AutoUpdater::AutoUpdater() {
#if defined Q_OS_WIN32 #if defined Q_OS_WIN32
@ -43,63 +44,114 @@ void AutoUpdater::parseLatestVersionData() {
QNetworkReply* sender = qobject_cast<QNetworkReply*>(QObject::sender()); QNetworkReply* sender = qobject_cast<QNetworkReply*>(QObject::sender());
QXmlStreamReader xml(sender); QXmlStreamReader xml(sender);
struct InstallerURLs {
QString full;
QString clientOnly;
};
int version; int version { 0 };
QString downloadUrl; QString downloadUrl;
QString releaseTime; QString releaseTime;
QString releaseNotes; QString releaseNotes;
QString commitSha; QString commitSha;
QString pullRequestNumber; QString pullRequestNumber;
while (!xml.atEnd() && !xml.hasError()) { while (xml.readNextStartElement()) {
if (xml.name().toString() == "project" && if (xml.name() == "projects") {
xml.attributes().hasAttribute("name") && while (xml.readNextStartElement()) {
xml.attributes().value("name").toString() == "interface") { if (xml.name().toString() == "project" &&
xml.readNext();
while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "project") {
if (xml.name().toString() == "platform" &&
xml.attributes().hasAttribute("name") && xml.attributes().hasAttribute("name") &&
xml.attributes().value("name").toString() == _operatingSystem) { xml.attributes().value("name").toString() == "interface") {
xml.readNext();
while (!xml.atEnd() && !xml.hasError() && while (xml.readNextStartElement()) {
xml.name().toString() != "platform") {
if (xml.name().toString() == "platform" &&
if (xml.name().toString() == "build" && xml.tokenType() != QXmlStreamReader::EndElement) { xml.attributes().hasAttribute("name") &&
xml.readNext(); xml.attributes().value("name").toString() == _operatingSystem) {
version = xml.readElementText().toInt();
xml.readNext(); while (xml.readNextStartElement()) {
downloadUrl = xml.readElementText(); if (xml.name() == "build") {
xml.readNext(); QHash<QString, InstallerURLs> campaignInstallers;
releaseTime = xml.readElementText();
xml.readNext(); while (xml.readNextStartElement()) {
if (xml.name().toString() == "notes" && xml.tokenType() != QXmlStreamReader::EndElement) { if (xml.name() == "version") {
xml.readNext(); version = xml.readElementText().toInt();
while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "notes") { } else if (xml.name() == "url") {
if (xml.name().toString() == "note" && xml.tokenType() != QXmlStreamReader::EndElement) { downloadUrl = xml.readElementText();
releaseNotes = releaseNotes + "\n" + xml.readElementText(); } else if (xml.name() == "installers") {
while (xml.readNextStartElement()) {
QString campaign = xml.name().toString();
QString full;
QString clientOnly;
while (xml.readNextStartElement()) {
if (xml.name() == "full") {
full = xml.readElementText();
} else if (xml.name() == "client_only") {
clientOnly = xml.readElementText();
} else {
xml.skipCurrentElement();
}
}
campaignInstallers[campaign] = { full, clientOnly };
}
} else if (xml.name() == "timestamp") {
releaseTime = xml.readElementText();
} else if (xml.name() == "notes") {
while (xml.readNextStartElement()) {
if (xml.name() == "note") {
releaseNotes = releaseNotes + "\n" + xml.readElementText();
} else {
xml.skipCurrentElement();
}
}
} else if (xml.name() == "sha") {
commitSha = xml.readElementText();
} else if (xml.name() == "pull_request") {
pullRequestNumber = xml.readElementText();
} else {
xml.skipCurrentElement();
}
} }
xml.readNext();
static const QString DEFAULT_INSTALLER_CAMPAIGN_NAME = "standard";
for (auto& campaign : { _installerCampaign, DEFAULT_INSTALLER_CAMPAIGN_NAME }) {
auto it = campaignInstallers.find(campaign);
if (it != campaignInstallers.end()) {
auto& urls = *it;
if (_installerType == InstallerType::CLIENT_ONLY) {
if (!urls.clientOnly.isEmpty()) {
downloadUrl = urls.clientOnly;
break;
}
} else {
if (!urls.full.isEmpty()) {
downloadUrl = urls.full;
break;
}
}
}
}
appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber);
releaseNotes = "";
} else {
xml.skipCurrentElement();
} }
} }
xml.readNext(); } else {
commitSha = xml.readElementText(); xml.skipCurrentElement();
xml.readNext();
pullRequestNumber = xml.readElementText();
appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber);
releaseNotes = "";
} }
xml.readNext();
} }
} else {
xml.skipCurrentElement();
} }
xml.readNext();
} }
} else { } else {
xml.readNext(); xml.skipCurrentElement();
} }
} }
sender->deleteLater(); sender->deleteLater();
emit latestVersionDataParsed(); emit latestVersionDataParsed();
} }

View file

@ -36,10 +36,17 @@ class AutoUpdater : public QObject, public Dependency {
public: public:
AutoUpdater(); AutoUpdater();
enum class InstallerType {
CLIENT_ONLY = 0,
FULL
};
void checkForUpdate(); void checkForUpdate();
const QMap<int, QMap<QString, QString>>& getBuildData() { return _builds; } const QMap<int, QMap<QString, QString>>& getBuildData() { return _builds; }
void performAutoUpdate(int version); void performAutoUpdate(int version);
void setInstallerType(InstallerType type) { _installerType = type; }
void setInstallerCampaign(QString campaign) { _installerCampaign = campaign; }
signals: signals:
void latestVersionDataParsed(); void latestVersionDataParsed();
@ -49,6 +56,8 @@ signals:
private: private:
QMap<int, QMap<QString, QString>> _builds; QMap<int, QMap<QString, QString>> _builds;
QString _operatingSystem; QString _operatingSystem;
InstallerType _installerType { InstallerType::FULL };
QString _installerCampaign { "" };
void getLatestVersionData(); void getLatestVersionData();
void downloadUpdateVersion(int version); void downloadUpdateVersion(int version);