From fbcf85559eea7491a0889115084afeeaf047d569 Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Mon, 14 Sep 2020 15:20:20 +0200 Subject: [PATCH] Update: Add option to check for nightly updates Use Appveyor REST API to obtain information about latest build from master. --- Src/Lib/DownloadHelper.cpp | 135 ++++++++++++++++++++++++++++++++++++- Src/Lib/DownloadHelper.h | 1 + Src/Lib/Lib.vcxproj | 5 ++ Src/Update/Update.cpp | 22 ++++++ Src/Update/Update.rc | 22 +++--- Src/Update/resource.h | 3 +- 6 files changed, 175 insertions(+), 13 deletions(-) diff --git a/Src/Lib/DownloadHelper.cpp b/Src/Lib/DownloadHelper.cpp index 62bf6aa..19160d0 100644 --- a/Src/Lib/DownloadHelper.cpp +++ b/Src/Lib/DownloadHelper.cpp @@ -318,6 +318,7 @@ struct VersionCheckParams TSettingsComponent component; tNewVersionCallback callback; CProgressDlg *progress; + bool nightly = false; }; // 0 - fail, 1 - success, 2 - cancel @@ -342,7 +343,7 @@ static DWORD WINAPI ThreadVersionCheck( void *param ) VersionData data; { - auto load = data.Load(); + auto load = params.nightly ? data.LoadNightly() : data.Load(); #ifdef UPDATE_LOG LogToFile(UPDATE_LOG, L"Load result: %d", load); @@ -450,6 +451,21 @@ DWORD CheckForNewVersion( HWND owner, TSettingsComponent component, TVersionChec params->callback=callback; params->progress=NULL; + // check the Update setting (uses the current value in the registry, not the one from memory + { + CRegKey regSettings, regSettingsUser, regPolicy, regPolicyUser; + bool bUpgrade = OpenSettingsKeys(COMPONENT_SHARED, regSettings, regSettingsUser, regPolicy, regPolicyUser); + + CSetting settings[] = { + {L"Nightly",CSetting::TYPE_BOOL,0,0,0}, + {NULL} + }; + + settings[0].LoadValue(regSettings, regSettingsUser, regPolicy, regPolicyUser); + + params->nightly = GetSettingBool(settings[0]); + } + if (!owner) return ThreadVersionCheck(params); @@ -495,19 +511,23 @@ DWORD CheckForNewVersion( HWND owner, TSettingsComponent component, TVersionChec return 0; // check weekly // check the Update setting (uses the current value in the registry, not the one from memory + bool nightly = false; { CRegKey regSettings, regSettingsUser, regPolicy, regPolicyUser; bool bUpgrade=OpenSettingsKeys(COMPONENT_SHARED,regSettings,regSettingsUser,regPolicy,regPolicyUser); CSetting settings[]={ {L"Update",CSetting::TYPE_BOOL,0,0,1}, + {L"Nightly",CSetting::TYPE_BOOL,0,0,0}, {NULL} }; settings[0].LoadValue(regSettings,regSettingsUser,regPolicy,regPolicyUser); + settings[1].LoadValue(regSettings,regSettingsUser,regPolicy,regPolicyUser); if (!GetSettingBool(settings[0])) - return 0; + return 0; + nightly = GetSettingBool(settings[1]); } VersionCheckParams *params=new VersionCheckParams; @@ -515,6 +535,7 @@ DWORD CheckForNewVersion( HWND owner, TSettingsComponent component, TVersionChec params->component=component; params->callback=callback; params->progress=NULL; + params->nightly=nightly; g_bCheckingVersion=true; if (check==CHECK_AUTO_WAIT) @@ -810,6 +831,116 @@ VersionData::TLoadResult VersionData::Load() } } +VersionData::TLoadResult VersionData::LoadNightly() +{ + Clear(); + + auto buf = DownloadUrl(L"https://ci.appveyor.com/api/projects/passionate-coder/open-shell-menu/branch/master"); + if (buf.empty()) + return LOAD_ERROR; + + try + { + auto data = json::parse(buf.begin(), buf.end()); + auto build = data["build"]; + + // get version + auto version = build["version"].get(); + if (version.empty()) + return LOAD_BAD_FILE; + + { + int v1, v2, v3; + if (sscanf_s(version.c_str(), "%d.%d.%d", &v1, &v2, &v3) != 3) + return LOAD_BAD_FILE; + + newVersion = (v1 << 24) | (v2 << 16) | v3; + + if (newVersion <= GetVersionEx(g_Instance)) + return LOAD_OK; + } + + // artifact url + { + auto jobId = build["jobs"][0]["jobId"].get(); + if (jobId.empty()) + return LOAD_BAD_FILE; + + std::wstring jobUrl(L"https://ci.appveyor.com/api/buildjobs/"); + jobUrl += std::wstring(jobId.begin(), jobId.end()); + jobUrl += L"/artifacts"; + + buf = DownloadUrl(jobUrl.c_str()); + if (buf.empty()) + return LOAD_ERROR; + + auto artifacts = json::parse(buf.begin(), buf.end()); + + std::string fileName; + for (const auto& artifact : artifacts) + { + auto name = artifact["fileName"].get(); + if (name.find("OpenShellSetup") == 0) + { + fileName = name; + break; + } + } + + if (fileName.empty()) + return LOAD_BAD_FILE; + + auto artifactUrl(jobUrl); + artifactUrl += L'/'; + artifactUrl += std::wstring(fileName.begin(), fileName.end()); + + downloadUrl = artifactUrl.c_str(); + } + + // changelog + news.Append(CA2T(version.c_str())); + news.Append(L"\r\n\r\n"); + try + { + // use Github API to compare commit that actual version was built from (APPVEYOR_REPO_COMMIT) + // and commit that AppVeyor version was built from (commitId) + auto commitId = build["commitId"].get(); + + std::wstring compareUrl(L"https://api.github.com/repos/Open-Shell/Open-Shell-Menu/compare/"); + compareUrl += _T(APPVEYOR_REPO_COMMIT); + compareUrl += L"..."; + compareUrl += std::wstring(commitId.begin(), commitId.end()); + + buf = DownloadUrl(compareUrl.c_str()); + auto compare = json::parse(buf.begin(), buf.end()); + + // then use first lines (subjects) of commit messages as changelog + auto commits = compare["commits"]; + for (const auto& commit : commits) + { + auto message = commit["commit"]["message"].get(); + + auto pos = message.find('\n'); + if (pos != message.npos) + message.resize(pos); + + news.Append(L"- "); + news.Append(CA2T(message.c_str())); + news.Append(L"\r\n"); + } + } + catch (...) + { + } + } + catch (...) + { + return LOAD_BAD_FILE; + } + + return LOAD_OK; +} + VersionData::TLoadResult VersionData::Load( const wchar_t *fname, bool bLoadFlags ) { Clear(); diff --git a/Src/Lib/DownloadHelper.h b/Src/Lib/DownloadHelper.h index 5941f5c..32d536e 100644 --- a/Src/Lib/DownloadHelper.h +++ b/Src/Lib/DownloadHelper.h @@ -60,6 +60,7 @@ struct VersionData }; TLoadResult Load(); + TLoadResult LoadNightly(); TLoadResult Load( const wchar_t *fname, bool bLoadFlags ); private: void operator=( const VersionData& ); diff --git a/Src/Lib/Lib.vcxproj b/Src/Lib/Lib.vcxproj index 69d8f80..ac8a4d8 100644 --- a/Src/Lib/Lib.vcxproj +++ b/Src/Lib/Lib.vcxproj @@ -83,6 +83,11 @@ $(Configuration)64\ $(Configuration)64\ + + + APPVEYOR_REPO_COMMIT="$(APPVEYOR_REPO_COMMIT)";%(PreprocessorDefinitions) + + Disabled diff --git a/Src/Update/Update.cpp b/Src/Update/Update.cpp index 6f522b5..011d33e 100644 --- a/Src/Update/Update.cpp +++ b/Src/Update/Update.cpp @@ -60,11 +60,13 @@ static CSetting g_Settings[]={ {L"Update",CSetting::TYPE_GROUP}, {L"Language",CSetting::TYPE_STRING,0,0,L"",CSetting::FLAG_SHARED}, {L"Update",CSetting::TYPE_BOOL,0,0,1,CSetting::FLAG_SHARED}, + {L"Nightly",CSetting::TYPE_BOOL,0,0,0,CSetting::FLAG_SHARED}, {NULL} }; const int SETTING_UPDATE=2; +const int SETTING_NIGHTLY=3; /////////////////////////////////////////////////////////////////////////////// @@ -79,6 +81,7 @@ public: MESSAGE_HANDLER( WM_GETMINMAXINFO, OnGetMinMaxInfo ) MESSAGE_HANDLER( WM_CTLCOLORSTATIC, OnColorStatic ) COMMAND_HANDLER( IDC_CHECKAUTOCHECK, BN_CLICKED, OnCheckAuto ) + COMMAND_HANDLER( IDC_CHECKNIGHTLY, BN_CLICKED, OnCheckNightly ) COMMAND_HANDLER( IDC_BUTTONCHECKNOW, BN_CLICKED, OnCheckNow ) COMMAND_HANDLER( IDC_BUTTONDOWNLOAD, BN_CLICKED, OnDownload ) COMMAND_HANDLER( IDC_CHECKDONT, BN_CLICKED, OnDontRemind ) @@ -114,6 +117,7 @@ protected: LRESULT OnCancel( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ); LRESULT OnColorStatic( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ); LRESULT OnCheckAuto( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ); + LRESULT OnCheckNightly( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ); LRESULT OnCheckNow( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ); LRESULT OnDownload( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ); LRESULT OnDontRemind( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ); @@ -161,6 +165,13 @@ LRESULT CUpdateDlg::OnInitDialog( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& CheckDlgButton(IDC_CHECKAUTOCHECK,check?BST_CHECKED:BST_UNCHECKED); GetDlgItem(IDC_CHECKAUTOCHECK).EnableWindow(!(g_Settings[SETTING_UPDATE].flags&CSetting::FLAG_LOCKED_MASK)); GetDlgItem(IDC_BUTTONCHECKNOW).EnableWindow(!(g_Settings[SETTING_UPDATE].flags&CSetting::FLAG_LOCKED_MASK) || check); + + bool nightly = false; + if (g_Settings[SETTING_NIGHTLY].value.vt == VT_I4) + nightly = g_Settings[SETTING_NIGHTLY].value.intVal != 0; + CheckDlgButton(IDC_CHECKNIGHTLY, nightly ? BST_CHECKED : BST_UNCHECKED); + GetDlgItem(IDC_CHECKNIGHTLY).EnableWindow(!(g_Settings[SETTING_NIGHTLY].flags & CSetting::FLAG_LOCKED_MASK) && check); + UpdateUI(); return TRUE; @@ -210,6 +221,17 @@ LRESULT CUpdateDlg::OnCheckAuto( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bool check=IsDlgButtonChecked(IDC_CHECKAUTOCHECK)==BST_CHECKED; g_Settings[SETTING_UPDATE].value=CComVariant(check?1:0); g_Settings[SETTING_UPDATE].flags&=~CSetting::FLAG_DEFAULT; + GetDlgItem(IDC_CHECKNIGHTLY).EnableWindow(check); + UpdateUI(); + return 0; +} + +LRESULT CUpdateDlg::OnCheckNightly(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) +{ + CSettingsLockWrite lock; + bool check = IsDlgButtonChecked(IDC_CHECKNIGHTLY) == BST_CHECKED; + g_Settings[SETTING_NIGHTLY].value = CComVariant(check ? 1 : 0); + g_Settings[SETTING_NIGHTLY].flags &= ~CSetting::FLAG_DEFAULT; UpdateUI(); return 0; } diff --git a/Src/Update/Update.rc b/Src/Update/Update.rc index c92ed81..382ed11 100644 --- a/Src/Update/Update.rc +++ b/Src/Update/Update.rc @@ -123,22 +123,24 @@ END // Dialog // -IDD_UPDATE DIALOGEX 0, 0, 316, 183 +IDD_UPDATE DIALOGEX 0, 0, 316, 200 STYLE DS_SETFONT | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "Open-Shell Update" FONT 9, "Segoe UI", 400, 0, 0x0 BEGIN CONTROL "Automatically check for new versions",IDC_CHECKAUTOCHECK, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,129,10 - PUSHBUTTON "Check now",IDC_BUTTONCHECKNOW,7,19,50,14 - LTEXT "message",IDC_STATICLATEST,7,33,302,10,SS_CENTERIMAGE - EDITTEXT IDC_EDITTEXT,7,45,302,97,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT WS_VISIBLE | WS_VSCROLL - PUSHBUTTON "Download",IDC_BUTTONDOWNLOAD,7,146,50,14,NOT WS_VISIBLE + CONTROL "Check for nightly builds",IDC_CHECKNIGHTLY, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,19,151,10 + PUSHBUTTON "Check now",IDC_BUTTONCHECKNOW,7,34,50,14 + LTEXT "message",IDC_STATICLATEST,7,48,302,10,SS_CENTERIMAGE + EDITTEXT IDC_EDITTEXT,7,60,302,97,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT WS_VISIBLE | WS_VSCROLL + PUSHBUTTON "Download",IDC_BUTTONDOWNLOAD,7,161,50,14,NOT WS_VISIBLE CONTROL "Don't remind me again about this version",IDC_CHECKDONT, - "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,61,146,141,14 - CONTROL "Open-Shell-Menu",IDC_LINKWEB,"SysLink",WS_TABSTOP,7,166,66,10,WS_EX_TRANSPARENT - DEFPUSHBUTTON "OK",IDOK,202,162,50,14 - PUSHBUTTON "Cancel",IDCANCEL,259,162,50,14 + "Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,61,161,141,14 + CONTROL "Open-Shell-Menu",IDC_LINKWEB,"SysLink",WS_TABSTOP,7,181,66,10,WS_EX_TRANSPARENT + DEFPUSHBUTTON "OK",IDOK,202,177,50,14 + PUSHBUTTON "Cancel",IDCANCEL,259,177,50,14 END @@ -155,7 +157,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 309 TOPMARGIN, 7 - BOTTOMMARGIN, 174 + BOTTOMMARGIN, 191 END END #endif // APSTUDIO_INVOKED diff --git a/Src/Update/resource.h b/Src/Update/resource.h index 3cc9b96..f842f9c 100644 --- a/Src/Update/resource.h +++ b/Src/Update/resource.h @@ -9,6 +9,7 @@ #define IDC_CHECKDONT 1004 #define IDC_BUTTONCHECKNOW 1005 #define IDC_CHECKAUTOCHECK 1006 +#define IDC_CHECKNIGHTLY 1007 #define IDD_UPDATE 6001 #define IDS_UPDATED 6001 #define IDS_OUTOFDATE 6002 @@ -21,7 +22,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 227 +#define _APS_NEXT_RESOURCE_VALUE 228 #define _APS_NEXT_COMMAND_VALUE 32769 #define _APS_NEXT_CONTROL_VALUE 262 #define _APS_NEXT_SYMED_VALUE 106