Commit a1ee21df authored by Leander Schulten's avatar Leander Schulten

Merge branch 'feature/updater' into 'master'

Add updater, does not run in IDE. Checks for new version and download if...

See merge request leander.schulten/Lichtsteuerung!6
parents 39e4c62f 061a826e
Pipeline #143975 passed with stage
in 1 minute and 25 seconds
......@@ -47,6 +47,7 @@ SOURCES += \
test/testloopprogramm.cpp \
settings.cpp \
test/DriverDummy.cpp \
updater.cpp \
usermanagment.cpp \
modules/modulemanager.cpp \
modules/programblock.cpp \
......@@ -76,7 +77,8 @@ SOURCES += \
spotify/segmentobject.cpp \
spotify/audioanalysisobject.cpp \
spotify/userobject.cpp \
sortedmodelview.cpp
sortedmodelview.cpp \
zip.cpp
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
......@@ -104,6 +106,7 @@ HEADERS += \
modules/controlpoint.hpp \
modules/scanner.hpp \
scanner.h \
updater.h \
usermanagment.h \
gui/channelprogrammeditor.h \
modelmanager.h \
......@@ -165,7 +168,8 @@ HEADERS += \
spotify/util.h \
modules/spotifyobjetcs.hpp \
modules/spotify.hpp \
sortedmodelview.h
sortedmodelview.h \
zip.h
# Default rules for deployment.
......
......@@ -44,9 +44,15 @@
#include "test/testsampleclass.h"
#include "spotify/spotify.h"
#include "modules/dmxconsumer.h"
#include "updater.h"
int main(int argc, char *argv[])
{
Updater updater;
QObject::connect(&updater,&Updater::needUpdate,[&](){
updater.update();
});
updater.checkForUpdate();
/*Test::TestModulSystem testModulSystem;
testModulSystem.runTest();
return 0;*/
......@@ -171,6 +177,7 @@ int main(int argc, char *argv[])
QFile savePath(settings.getJsonSettingsFilePath());
ApplicationData::saveData(savePath);
Driver::stopAndUnloadDriver();
updater.runUpdateInstaller();
});
settings.connect(&settings,&Settings::driverFilePathChanged,[&](){
Driver::loadAndStartDriver(settings.getDriverFilePath());
......@@ -208,6 +215,7 @@ int main(int argc, char *argv[])
engine.rootContext()->setContextProperty("UserManagment",UserManagment::get());
engine.rootContext()->setContextProperty("Settings",&settings);
engine.rootContext()->setContextProperty("spotify",&spotify);
engine.rootContext()->setContextProperty("updater",&updater);
QQmlEngine::setObjectOwnership(&Driver::dmxValueModel,QQmlEngine::CppOwnership);
engine.rootContext()->setContextProperty("dmxOutputValues",&Driver::dmxValueModel);
engine.load(QUrl(QLatin1String("qrc:/qml/main.qml")));
......
......@@ -27,6 +27,11 @@ ApplicationWindow {
}
}
ToolTip.visible: updater.progress >= 0 && updater.progress <= 100
ToolTip.delay: 0
ToolTip.timeout: 20000
ToolTip.text: updater.progress < 100 ? "Downloading update. Progress : " + updater.progress + "%" : "Downloading update finished. Restart to update."
VerticalTabBar{
id: tabBar
......
#include "updater.h"
#include "zip.h"
#include <QDir>
#include <QFileInfo>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QProcess>
#include <QTemporaryFile>
#include <thread>
Updater::Updater(){
if(!QFile::exists(QDir::currentPath() + "/Lichtsteuerung.exe")){
state = UpdaterState::IDE_ENV;
}
}
void Updater::checkForUpdate(){
if(state == UpdaterState::IDE_ENV){
return;
}
if(!QFile::exists(VERSION_FILE_NAME)){
qDebug() << "version file does not exists in application folder";
state = UpdaterState::UpdateAvailible;
emit needUpdate();
return;
}
auto redirect = http->get(QNetworkRequest(QUrl(versionDownloadURL)));
QObject::connect(redirect,&QNetworkReply::finished,[this,redirect](){
redirect->deleteLater();
auto redirectURL = redirect->header(QNetworkRequest::KnownHeaders::LocationHeader);
if(!redirectURL.isValid()){
qDebug() << "can not redirect version.zip";
return;
}
auto response = http->get(QNetworkRequest(redirectURL.toUrl()));
QObject::connect(response,&QNetworkReply::finished,[this,response](){
std::thread thread([this,response](){
QFile version(QDir::tempPath() + QStringLiteral("/version.zip"));
version.open(QFile::WriteOnly);
version.write(response->readAll());
version.close();
response->deleteLater();
if(!Zip::unzip(QFileInfo(version),QFileInfo(version).absolutePath())){
qDebug() << "not successful when unzipping";
state = UpdaterState::NoUpdateAvailible;
return;
}
if(!QFile::exists(QFileInfo(version).absolutePath() + "/" + VERSION_FILE_NAME)){
qDebug() << "version file does not exists in version.zip";
state = UpdaterState::NoUpdateAvailible;
return;
}
if(QFile(VERSION_FILE_NAME).readAll() != QFile(QFileInfo(version).absolutePath() + "/" + VERSION_FILE_NAME).readAll()){
state = UpdaterState::UpdateAvailible;
emit needUpdate();
}else{
state = UpdaterState::NoUpdateAvailible;
}
});
thread.detach();
});
});
}
void Updater::update(){
if(state != UpdaterState::UpdateAvailible) {
qDebug() << "we are not in a state to make updates";
return;
}
qDebug() << "Update";
state = UpdaterState::DownloadingUpdate;
auto redirect = http->get(QNetworkRequest(QUrl(deployDownloadURL)));
QObject::connect(redirect,&QNetworkReply::finished,[this,redirect](){
redirect->deleteLater();
auto redirectURL = redirect->header(QNetworkRequest::KnownHeaders::LocationHeader);
if(!redirectURL.isValid()){
qDebug() << "can not redirect deploy.zip";
return;
}
auto response = http->get(QNetworkRequest(redirectURL.toUrl()));
QFile * deploy = new QFile(QDir::tempPath() + QStringLiteral("/deploy.zip"));
deploy->open(QFile::WriteOnly);
QObject::connect(response,static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),[this,response,deploy](auto error){
qWarning() << "Error while downloading deploy.zip! " << error << response->errorString();
deploy->close();
deploy->deleteLater();
response->deleteLater();
state = UpdaterState::DownloadUpdateFailed;
});
QObject::connect(response,&QNetworkReply::readyRead,[response,deploy](){
deploy->write(response->readAll());
});
QObject::connect(response,&QNetworkReply::downloadProgress,[this](auto rec, auto max){
progress = 100 * rec / max;
qDebug() << progress;
emit updateProgressChanged();
});
QObject::connect(response,&QNetworkReply::finished,[this,response,deploy](){
std::thread thread([this,response,deploy](){
qDebug() << response->atEnd();
deploy->close();
std::unique_ptr<QFile> deleteMe(deploy);
response->deleteLater();
if(!Zip::unzip(QFileInfo(*deploy),QFileInfo(*deploy).absolutePath())){
qDebug() << "not successful when unzipping deploy.zip";
state = UpdaterState::DownloadUpdateFailed;
return;
}
deployPath = QFileInfo(*deploy).absolutePath() + "/" + NAME_OF_DEPLOY_FOLDER;
// all important files like QTJSONFile.json
auto entries = QDir(QDir::currentPath()).entryInfoList(QStringList() << QStringLiteral("QTJSONFile.json*"),QDir::Filter::Files);
for(const auto & e : entries){
QFile::copy(e.absoluteFilePath() , deployPath + "/" + e.fileName());
}
state = UpdaterState::UpdateDownloaded;
});
thread.detach();
});
});
}
void Updater::runUpdateInstaller(){
if(state != UpdaterState::UpdateDownloaded){
return;
}
QFile batchFile(QDir::tempPath() + "/installLichtsteuerung.bat");
batchFile.open(QFile::WriteOnly);
//https://stackoverflow.com/questions/1894967/how-to-request-administrator-access-inside-a-batch-file
auto content = "@echo off\r\n"
":: BatchGotAdmin\r\n"
":-------------------------------------\r\n"
"REM --> Check for permissions\r\n"
" IF \"%PROCESSOR_ARCHITECTURE%\" EQU \"amd64\" (\r\n"
">nul 2>&1 \"%SYSTEMROOT%\\SysWOW64\\cacls.exe\" \"%SYSTEMROOT%\\SysWOW64\\config\\system\"\r\n"
") ELSE (\r\n"
">nul 2>&1 \"%SYSTEMROOT%\\system32\\cacls.exe\" \"%SYSTEMROOT%\\system32\\config\\system\"\r\n"
")\r\n"
"\r\n"
"REM --> If error flag set, we do not have admin.\r\n"
"if '%errorlevel%' NEQ '0' (\r\n"
" echo Requesting administrative privileges...\r\n"
" goto UACPrompt\r\n"
") else ( goto gotAdmin )\r\n"
"\r\n"
":UACPrompt\r\n"
" echo Set UAC = CreateObject^(\"Shell.Application\"^) > \"%temp%\\getadmin.vbs\"\r\n"
" set params= %*\r\n"
" echo UAC.ShellExecute \"cmd.exe\", \"/c \"\"%~s0\"\" %params:\"=\"\"%\", \"\", \"runas\", 1 >> \"%temp%\\getadmin.vbs\"\r\n"
"\r\n"
" \"%temp%\\getadmin.vbs\"\r\n"
" del \"%temp%\\getadmin.vbs\"\r\n"
" exit /B\r\n"
"\r\n"
":gotAdmin\r\n"
" pushd \"%CD%\"\r\n"
" CD /D \"%~dp0\"\r\n"
":-------------------------------------- \r\n"
": Own Code\r\n"
"RMDIR /s /q \"" + QFileInfo(QDir::currentPath()).absoluteFilePath() + "\"\r\n"
"move \"" + deployPath + "\" \"" + QFileInfo(QDir::currentPath()).absoluteFilePath() + "\"\r\n";
batchFile.write(content.toUtf8());
batchFile.close();
qDebug () << "run " << QFileInfo(batchFile).absoluteFilePath();
QProcess::startDetached(QStringLiteral("cmd"), QStringList() << QStringLiteral("/c") << QFileInfo(batchFile).absoluteFilePath());
}
#ifndef UPDATER_H
#define UPDATER_H
#include <QNetworkAccessManager>
#include <QObject>
#include <QString>
#include <memory>
class Updater : public QObject{
Q_OBJECT
Q_PROPERTY(int progress READ getUpdateProgress NOTIFY updateProgressChanged)
const inline static QString VERSION_FILE_NAME = QStringLiteral("version.txt");
const inline static QString NAME_OF_DEPLOY_FOLDER = QStringLiteral("windows-release-5.12.3");
QString versionDownloadURL = QStringLiteral("https://git.rwth-aachen.de/leander.schulten/Lichtsteuerung/-/jobs/artifacts/windows-release/download?job=version");
QString deployDownloadURL = QStringLiteral("https://git.rwth-aachen.de/leander.schulten/Lichtsteuerung/-/jobs/artifacts/windows-release/download?job=deploy");
std::unique_ptr<QNetworkAccessManager> http = std::make_unique<QNetworkAccessManager>();
enum class UpdaterState {IDE_ENV, NotChecked, NoUpdateAvailible, UpdateAvailible, DownloadingUpdate, UpdateDownloaded, DownloadUpdateFailed} state = UpdaterState::NotChecked;
int progress = -1;
QString deployPath;
public:
Updater();
void checkForUpdate();
UpdaterState getState()const {return state;}
void update();
/**
* @brief runUpdateInstaller starts the installer script, call when the application is closing
*/
void runUpdateInstaller();
int getUpdateProgress()const{return progress;}
signals:
void needUpdate();
void updateProgressChanged();
};
#endif // UPDATER_H
#include "zip.h"
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
bool unzipPowershellNew(const QFileInfo& zip, const QFileInfo& unzip){
// https://www.reddit.com/r/Windows10/comments/71z96c/unzip_from_command_line/
// Expand-Archive "<PathToZIPFile>" "<FolderWhereToExtract>" -Force
QProcess p;
p.start(QStringLiteral("powershell.exe"), QStringList() << QStringLiteral("Expand-Archive") << QStringLiteral("-Force") << "\"" + zip.absoluteFilePath() + "\"" << "\"" + unzip.absoluteFilePath() + "\"");
p.waitForFinished();
if(p.exitCode() != 0){
qDebug() << "Failed to unzip " << zip << " to " << unzip << " with powershell new";
qDebug().noquote() << "stderr : " << p.readAllStandardError();
qDebug().noquote() << "stdout : " << p.readAllStandardOutput();
}
return !p.exitCode();
}
bool unzipPowershell(const QFileInfo& zip, const QFileInfo& unzip){
// from https://stackoverflow.com/questions/17546016/how-can-you-zip-or-unzip-from-the-script-using-only-windows-built-in-capabiliti/26843122#26843122
// powershell.exe -nologo -noprofile -command "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('foo.zip', 'bar'); }"
QProcess p;
p.start(QStringLiteral("powershell.exe"), QStringList() << QStringLiteral("-nologo") << QStringLiteral("-noprofile") << "& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('"+zip.absoluteFilePath()+"', '"+unzip.absoluteFilePath()+"/unzippedDir'); }");
p.waitForFinished();
if(p.exitCode() != 0){
qDebug() << "Failed to unzip " << zip << " to " << unzip << " with powershell";
qDebug().noquote() << "stderr : " << p.readAllStandardError();
qDebug().noquote() << "stdout : " << p.readAllStandardOutput();
}
return !p.exitCode();
}
bool unzipWinrar(const QFileInfo& zip, const QFileInfo& unzip){
// from https://stackoverflow.com/questions/1315662/how-to-extract-zip-files-with-winrar-command-line
// "%ProgramFiles%\WinRAR\winrar.exe" x -ibck c:\file.zip *.* c:\folder
QProcess p;
p.start(QStringLiteral("C:\\Program Files\\WinRAR\\winrar.exe"), QStringList() << QStringLiteral("x") << QStringLiteral("-ibck") << zip.absoluteFilePath() << QStringLiteral("*.*") << unzip.absoluteFilePath());
p.waitForFinished();
if(p.exitCode() != 0){
qDebug() << "Failed to unzip " << zip << " to " << unzip << " with winrar";
qDebug().noquote() << "stderr : " << p.readAllStandardError();
qDebug().noquote() << "stdout : " << p.readAllStandardOutput();
}
return !p.exitCode();
}
bool unzip7Zip(const QFileInfo& zip, const QFileInfo& unzip){
// from https://superuser.com/questions/95902/7-zip-and-unzipping-from-command-line
// 7z x example.zip -oexample
QProcess p;
p.start(QStringLiteral("C:\\Program Files\\7-Zip\\7z.exe"), QStringList() << QStringLiteral("x") << QStringLiteral("-y") << zip.absoluteFilePath() << "-o" + unzip.absoluteFilePath());
p.waitForFinished();
if(p.exitCode() != 0){
qDebug() << "Failed to unzip " << zip << " to " << unzip << " with 7zip";
qDebug().noquote() << "stderr : " << p.readAllStandardError();
qDebug().noquote() << "stdout : " << p.readAllStandardOutput();
}
return !p.exitCode();
}
bool Zip::unzip(const QFileInfo& zip, const QFileInfo& unzip){
if(!zip.exists()){
return false;
}
QDir().mkpath(unzip.absoluteFilePath());
if(unzip7Zip(zip,unzip)){
return true;
}
if(unzipWinrar(zip,unzip)){
return true;
}
if(unzipPowershellNew(zip,unzip)){
return true;
}
if(unzipPowershell(zip,unzip)){
return true;
}
return false;
}
#ifndef ZIP_H
#define UZIP_H
#include <QFileInfo>
namespace Zip {
bool unzip(const QFileInfo& zip, const QFileInfo& unzip);
} // namespace Zip
#endif // UZIP_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment