From efcebfd82cc80164b6e931bcee1d2e4aff4631b2 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Thu, 28 May 2026 15:22:35 +0800 Subject: [PATCH] [feat] add explicit hotfix patch version support --- .github/workflows/build.yml | 144 +++++++-- .github/workflows/update-version-json.yml | 27 +- src/version_checker/version_checker.cpp | 337 +++++++++++++++++----- src/version_checker/version_checker.h | 8 +- xmake/targets.lua | 8 + 5 files changed, 427 insertions(+), 97 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aa78a4d..f4862f2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,11 @@ on: tags: - "*" workflow_dispatch: + inputs: + patch: + description: "Hotfix patch number, for example 1 or 2. Use 0 for a normal build." + required: false + default: "0" permissions: contents: write @@ -38,20 +43,47 @@ jobs: steps: - name: Extract version number run: | - VERSION="${GITHUB_REF##*/}" - VERSION_NUM="${VERSION#v}" - echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV + VERSION_REF="${GITHUB_REF##*/}" + VERSION_BASE="${VERSION_REF#v}" + PATCH_NUMBER="${{ github.event.inputs.patch }}" + BUILD_DATE_OVERRIDE="" + + if [[ ! "${PATCH_NUMBER}" =~ ^[0-9]+$ ]]; then + PATCH_NUMBER=0 + fi + + if [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + BUILD_DATE_OVERRIDE="${BASH_REMATCH[3]}" + PATCH_NUMBER="${BASH_REMATCH[4]}" + elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + BUILD_DATE_OVERRIDE="${BASH_REMATCH[3]}" + elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$ && "${PATCH_NUMBER}" == "0" ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + PATCH_NUMBER="${BASH_REMATCH[3]}" + fi + + echo "VERSION_BASE=${VERSION_BASE}" >> $GITHUB_ENV + echo "PATCH_NUMBER=${PATCH_NUMBER}" >> $GITHUB_ENV + echo "BUILD_DATE_OVERRIDE=${BUILD_DATE_OVERRIDE}" >> $GITHUB_ENV - name: Set legal Debian version shell: bash run: | - SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7) - BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) + BUILD_DATE="${BUILD_DATE_OVERRIDE}" + if [[ -z "${BUILD_DATE}" ]]; then + BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) + fi - if [[ ! "${VERSION_NUM}" =~ ^[0-9] ]]; then - LEGAL_VERSION="v0.0.0-${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}" + if [[ ! "${VERSION_BASE}" =~ ^[0-9] ]]; then + VERSION_BASE="0.0.0-${VERSION_BASE}" + fi + + if [[ "${PATCH_NUMBER}" != "0" ]]; then + LEGAL_VERSION="v${VERSION_BASE}-${BUILD_DATE}-${PATCH_NUMBER}" else - LEGAL_VERSION="v${VERSION_NUM}-${BUILD_DATE}-${SHORT_SHA}" + LEGAL_VERSION="v${VERSION_BASE}-${BUILD_DATE}" fi echo "LEGAL_VERSION=${LEGAL_VERSION}" >> $GITHUB_ENV @@ -103,13 +135,37 @@ jobs: - name: Extract version number id: version run: | - VERSION="${GITHUB_REF##*/}" - SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7) + VERSION_REF="${GITHUB_REF##*/}" + VERSION_BASE="${VERSION_REF#v}" BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) - VERSION_NUM="v${VERSION#v}-${BUILD_DATE}-${SHORT_SHA}" + PATCH_NUMBER="${{ github.event.inputs.patch }}" + + if [[ ! "${PATCH_NUMBER}" =~ ^[0-9]+$ ]]; then + PATCH_NUMBER=0 + fi + + if [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + BUILD_DATE="${BASH_REMATCH[3]}" + PATCH_NUMBER="${BASH_REMATCH[4]}" + elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + BUILD_DATE="${BASH_REMATCH[3]}" + elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$ && "${PATCH_NUMBER}" == "0" ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + PATCH_NUMBER="${BASH_REMATCH[3]}" + fi + + if [[ "${PATCH_NUMBER}" != "0" ]]; then + VERSION_NUM="v${VERSION_BASE}-${BUILD_DATE}-${PATCH_NUMBER}" + else + VERSION_NUM="v${VERSION_BASE}-${BUILD_DATE}" + fi + echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_ENV echo "VERSION_NUM=${VERSION_NUM}" echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV + echo "PATCH_NUMBER=${PATCH_NUMBER}" >> $GITHUB_ENV - name: Cache xmake dependencies uses: actions/cache@v5 @@ -163,10 +219,34 @@ jobs: $version = $ref -replace '^refs/(tags|heads)/', '' $version = $version -replace '^v', '' $version = $version -replace '/', '-' - $SHORT_SHA = $env:GITHUB_SHA.Substring(0,7) $BUILD_DATE = ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId((Get-Date), "China Standard Time")).ToString("yyyyMMdd") - echo "VERSION_NUM=v$version-$BUILD_DATE-$SHORT_SHA" >> $env:GITHUB_ENV + $PATCH_NUMBER = "${{ github.event.inputs.patch }}" + + if ($PATCH_NUMBER -notmatch '^[0-9]+$') { + $PATCH_NUMBER = "0" + } + + if ($version -match '^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$') { + $version = $Matches[1] + $BUILD_DATE = $Matches[3] + $PATCH_NUMBER = $Matches[4] + } elseif ($version -match '^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$') { + $version = $Matches[1] + $BUILD_DATE = $Matches[3] + } elseif ($version -match '^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$' -and $PATCH_NUMBER -eq "0") { + $version = $Matches[1] + $PATCH_NUMBER = $Matches[3] + } + + if ($PATCH_NUMBER -ne "0") { + $VERSION_NUM = "v$version-$BUILD_DATE-$PATCH_NUMBER" + } else { + $VERSION_NUM = "v$version-$BUILD_DATE" + } + + echo "VERSION_NUM=$VERSION_NUM" >> $env:GITHUB_ENV echo "BUILD_DATE=$BUILD_DATE" >> $env:GITHUB_ENV + echo "PATCH_NUMBER=$PATCH_NUMBER" >> $env:GITHUB_ENV - name: Cache xmake dependencies uses: actions/cache@v5 @@ -311,18 +391,41 @@ jobs: - name: Extract version number id: version run: | - VERSION="${GITHUB_REF##*/}" - SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7) + VERSION_REF="${GITHUB_REF##*/}" + VERSION_BASE="${VERSION_REF#v}" BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) - BUILD_DATE_ISO=$(TZ=Asia/Shanghai date +%Y-%m-%d) - VERSION_NUM="${VERSION#v}-${BUILD_DATE}-${SHORT_SHA}" + PATCH_NUMBER="${{ github.event.inputs.patch }}" + + if [[ ! "${PATCH_NUMBER}" =~ ^[0-9]+$ ]]; then + PATCH_NUMBER=0 + fi + + if [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + BUILD_DATE="${BASH_REMATCH[3]}" + PATCH_NUMBER="${BASH_REMATCH[4]}" + elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + BUILD_DATE="${BASH_REMATCH[3]}" + elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$ && "${PATCH_NUMBER}" == "0" ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + PATCH_NUMBER="${BASH_REMATCH[3]}" + fi + + BUILD_DATE_ISO="${BUILD_DATE:0:4}-${BUILD_DATE:4:2}-${BUILD_DATE:6:2}" + if [[ "${PATCH_NUMBER}" != "0" ]]; then + VERSION_NUM="${VERSION_BASE}-${BUILD_DATE}-${PATCH_NUMBER}" + else + VERSION_NUM="${VERSION_BASE}-${BUILD_DATE}" + fi + VERSION_WITH_V="v${VERSION_NUM}" - VERSION_ONLY="${VERSION#v}" echo "VERSION_NUM=${VERSION_NUM}" >> $GITHUB_OUTPUT echo "VERSION_WITH_V=${VERSION_WITH_V}" >> $GITHUB_OUTPUT - echo "VERSION_ONLY=${VERSION_ONLY}" >> $GITHUB_OUTPUT + echo "VERSION_BASE=${VERSION_BASE}" >> $GITHUB_OUTPUT echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_OUTPUT echo "BUILD_DATE_ISO=${BUILD_DATE_ISO}" >> $GITHUB_OUTPUT + echo "PATCH_NUMBER=${PATCH_NUMBER}" >> $GITHUB_OUTPUT - name: Rename artifacts run: | @@ -380,8 +483,9 @@ jobs: run: | cat > version.json << EOF { - "version": "${{ steps.version.outputs.VERSION_ONLY }}", + "version": "${{ steps.version.outputs.VERSION_NUM }}", "releaseDate": "${{ steps.version.outputs.BUILD_DATE_ISO }}", + "patch": ${{ steps.version.outputs.PATCH_NUMBER }}, "releaseName": "", "releaseNotes": "", "tagName": "${{ steps.version.outputs.VERSION_WITH_V }}", diff --git a/.github/workflows/update-version-json.yml b/.github/workflows/update-version-json.yml index ae3db0f..b951d86 100644 --- a/.github/workflows/update-version-json.yml +++ b/.github/workflows/update-version-json.yml @@ -22,19 +22,31 @@ jobs: id: version run: | TAG_NAME="${{ github.event.release.tag_name }}" - VERSION_ONLY="${TAG_NAME#v}" - echo "TAG_NAME=${TAG_NAME}" >> $GITHUB_OUTPUT - echo "VERSION_ONLY=${VERSION_ONLY}" >> $GITHUB_OUTPUT + TAG_VERSION="${TAG_NAME#v}" + VERSION_FULL="${TAG_VERSION}" + VERSION_BASE="${TAG_VERSION}" + PATCH_NUMBER=0 - # Extract date from tag if available (format: v1.2.3-20251113-abc) - if [[ "${TAG_NAME}" =~ -([0-9]{8})- ]]; then - DATE_STR="${BASH_REMATCH[1]}" + # Extract date and patch from tags such as v1.2.3-20251113-1. + if [[ "${TAG_VERSION}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + DATE_STR="${BASH_REMATCH[3]}" + PATCH_NUMBER="${BASH_REMATCH[4]}" + BUILD_DATE_ISO="${DATE_STR:0:4}-${DATE_STR:4:2}-${DATE_STR:6:2}" + elif [[ "${TAG_VERSION}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + DATE_STR="${BASH_REMATCH[3]}" BUILD_DATE_ISO="${DATE_STR:0:4}-${DATE_STR:4:2}-${DATE_STR:6:2}" else # Use release published date BUILD_DATE_ISO=$(echo "${{ github.event.release.published_at }}" | cut -d'T' -f1) fi + + echo "TAG_NAME=${TAG_NAME}" >> $GITHUB_OUTPUT + echo "VERSION_FULL=${VERSION_FULL}" >> $GITHUB_OUTPUT + echo "VERSION_BASE=${VERSION_BASE}" >> $GITHUB_OUTPUT echo "BUILD_DATE_ISO=${BUILD_DATE_ISO}" >> $GITHUB_OUTPUT + echo "PATCH_NUMBER=${PATCH_NUMBER}" >> $GITHUB_OUTPUT - name: Install jq run: sudo apt-get update && sudo apt-get install -y jq @@ -122,8 +134,9 @@ jobs: # Generate version.json using cat and heredoc cat > version.json << EOF { - "version": "${{ steps.version.outputs.VERSION_ONLY }}", + "version": "${{ steps.version.outputs.VERSION_FULL }}", "releaseDate": "${{ steps.version.outputs.BUILD_DATE_ISO }}", + "patch": ${{ steps.version.outputs.PATCH_NUMBER }}, "releaseName": ${{ steps.release_info.outputs.RELEASE_NAME }}, "releaseNotes": ${{ steps.release_info.outputs.RELEASE_BODY }}, "tagName": "${{ steps.version.outputs.TAG_NAME }}", diff --git a/src/version_checker/version_checker.cpp b/src/version_checker/version_checker.cpp index a27b3cc..5cf3b62 100644 --- a/src/version_checker/version_checker.cpp +++ b/src/version_checker/version_checker.cpp @@ -8,7 +8,10 @@ #include +#include +#include #include +#include #include #include #include @@ -16,12 +19,219 @@ namespace crossdesk { static std::string latest_release_date_ = ""; +static bool latest_patch_available_ = false; +static int latest_patch_ = 0; + +std::vector SplitVersion(const std::string& ver); + +namespace { + +constexpr size_t kMaxInlinePatchDigits = 4; + +struct ParsedVersion { + std::vector numbers; + std::string date; + bool has_patch = false; + int patch = 0; +}; + +bool IsDigit(char c) { + return std::isdigit(static_cast(c)) != 0; +} + +bool IsAlphaNumeric(char c) { + return std::isalnum(static_cast(c)) != 0; +} + +bool IsAllDigits(const std::string& value) { + if (value.empty()) { + return false; + } + + for (char c : value) { + if (!IsDigit(c)) { + return false; + } + } + return true; +} + +bool TryParseNonNegativeInt(const std::string& value, int* result) { + if (!IsAllDigits(value)) { + return false; + } + + try { + const long long parsed = std::stoll(value); + if (parsed > std::numeric_limits::max()) { + return false; + } + *result = static_cast(parsed); + return true; + } catch (...) { + return false; + } +} + +bool TryParseInlinePatch(const std::string& value, int* result) { + if (value.size() > kMaxInlinePatchDigits) { + return false; + } + + return TryParseNonNegativeInt(value, result); +} + +size_t FindNumericStart(const std::string& version) { + size_t start = 0; + while (start < version.size() && !IsDigit(version[start])) { + start++; + } + return start; +} + +size_t FindNumericEnd(const std::string& version, size_t start) { + size_t end = start; + while (end < version.size() && + (IsDigit(version[end]) || version[end] == '.')) { + end++; + } + return end; +} + +bool HasDigitBoundary(const std::string& value, size_t pos, size_t len) { + const bool before_ok = pos == 0 || !IsDigit(value[pos - 1]); + const size_t end = pos + len; + const bool after_ok = end >= value.size() || !IsDigit(value[end]); + return before_ok && after_ok; +} + +bool IsCompactDateAt(const std::string& value, size_t pos) { + if (pos + 8 > value.size() || !HasDigitBoundary(value, pos, 8)) { + return false; + } + + for (size_t i = 0; i < 8; ++i) { + if (!IsDigit(value[pos + i])) { + return false; + } + } + return true; +} + +std::string CompactDateToIso(const std::string& compact_date) { + return compact_date.substr(0, 4) + "-" + compact_date.substr(4, 2) + "-" + + compact_date.substr(6, 2); +} + +bool ExtractDateFromText(const std::string& value, + std::string* date, + size_t* date_end) { + for (size_t i = 0; i < value.size(); ++i) { + if (IsCompactDateAt(value, i)) { + *date = CompactDateToIso(value.substr(i, 8)); + *date_end = i + 8; + return true; + } + } + + return false; +} + +bool ExtractPatchAfterDate(const std::string& value, + size_t start, + int* patch) { + size_t pos = start; + while (pos < value.size() && !IsAlphaNumeric(value[pos])) { + pos++; + } + + const size_t token_start = pos; + while (pos < value.size() && IsAlphaNumeric(value[pos])) { + pos++; + } + + if (token_start == pos) { + return false; + } + + return TryParseInlinePatch(value.substr(token_start, pos - token_start), + patch); +} + +ParsedVersion ParseVersion(const std::string& version) { + const size_t numeric_start = FindNumericStart(version); + const size_t numeric_end = FindNumericEnd(version, numeric_start); + + ParsedVersion parsed; + parsed.numbers = SplitVersion(version.substr(numeric_start, + numeric_end - numeric_start)); + + const std::string suffix = version.substr(numeric_end); + size_t date_end = 0; + if (ExtractDateFromText(suffix, &parsed.date, &date_end)) { + int patch = 0; + if (ExtractPatchAfterDate(suffix, date_end, &patch)) { + parsed.has_patch = true; + parsed.patch = patch; + } + } + + return parsed; +} + +int CompareNumericVersion(const std::vector& current, + const std::vector& latest) { + std::vector current_parts = current; + std::vector latest_parts = latest; + const size_t len = std::max(current_parts.size(), latest_parts.size()); + current_parts.resize(len, 0); + latest_parts.resize(len, 0); + + for (size_t i = 0; i < len; ++i) { + if (latest_parts[i] > current_parts[i]) { + return 1; + } + if (latest_parts[i] < current_parts[i]) { + return -1; + } + } + + return 0; +} + +void ResetLatestMetadata() { + latest_release_date_ = ""; + latest_patch_available_ = false; + latest_patch_ = 0; +} + +bool ReadPatchField(const nlohmann::json& json, int* patch) { + if (!json.contains("patch")) { + return false; + } + + const auto& patch_value = json["patch"]; + if (patch_value.is_number_integer()) { + const long long parsed = patch_value.get(); + if (parsed < 0 || parsed > std::numeric_limits::max()) { + return false; + } + *patch = static_cast(parsed); + return true; + } + + if (patch_value.is_string()) { + return TryParseNonNegativeInt(patch_value.get(), patch); + } + + return false; +} + +} // namespace std::string ExtractNumericPart(const std::string& ver) { - size_t start = 0; - while (start < ver.size() && !std::isdigit(ver[start])) start++; - size_t end = start; - while (end < ver.size() && (std::isdigit(ver[end]) || ver[end] == '.')) end++; + const size_t start = FindNumericStart(ver); + const size_t end = FindNumericEnd(ver, start); return ver.substr(start, end - start); } @@ -42,25 +252,13 @@ std::vector SplitVersion(const std::string& ver) { // extract date from version string (format: v1.2.3-20251113-abc // or 1.2.3-20251113-abc) std::string ExtractDateFromVersion(const std::string& version) { - size_t dash1 = version.find('-'); - if (dash1 != std::string::npos) { - size_t dash2 = version.find('-', dash1 + 1); - if (dash2 != std::string::npos) { - std::string date_part = version.substr(dash1 + 1, dash2 - dash1 - 1); - - bool is_date = true; - for (char c : date_part) { - if (!std::isdigit(c)) { - is_date = false; - break; - } - } - if (is_date) { - // convert YYYYMMDD to YYYY-MM-DD - return date_part.substr(0, 4) + "-" + date_part.substr(4, 2) + "-" + - date_part.substr(6, 2); - } - } + const size_t numeric_start = FindNumericStart(version); + const size_t numeric_end = FindNumericEnd(version, numeric_start); + const std::string suffix = version.substr(numeric_end); + std::string date; + size_t date_end = 0; + if (ExtractDateFromText(suffix, &date, &date_end)) { + return date; } return ""; } @@ -73,55 +271,54 @@ bool IsNewerDate(const std::string& date1, const std::string& date2) { } bool IsNewerVersion(const std::string& current, const std::string& latest) { - auto v1 = SplitVersion(ExtractNumericPart(current)); - auto v2 = SplitVersion(ExtractNumericPart(latest)); - - size_t len = std::max(v1.size(), v2.size()); - v1.resize(len, 0); - v2.resize(len, 0); - - for (size_t i = 0; i < len; ++i) { - if (v2[i] > v1[i]) return true; - if (v2[i] < v1[i]) return false; - } - - // if versions are equal, compare by release date - if (!latest_release_date_.empty()) { - // try to extract date from current version string - std::string current_date = ExtractDateFromVersion(current); - if (!current_date.empty()) { - return IsNewerDate(current_date, latest_release_date_); - } else { - return true; - } - } - - return false; + return IsNewerVersionWithMetadata( + current, latest, latest_release_date_, + latest_patch_available_ ? latest_patch_ : -1); } -bool IsNewerVersionWithDate(const std::string& current_version, - const std::string& current_date, - const std::string& latest_version, - const std::string& latest_date) { - // compare versions - auto v1 = SplitVersion(ExtractNumericPart(current_version)); - auto v2 = SplitVersion(ExtractNumericPart(latest_version)); +bool IsNewerVersionWithMetadata(const std::string& current, + const std::string& latest, + const std::string& latest_date, + int latest_patch) { + const ParsedVersion current_version = ParseVersion(current); + const ParsedVersion latest_version = ParseVersion(latest); - size_t len = std::max(v1.size(), v2.size()); - v1.resize(len, 0); - v2.resize(len, 0); - - for (size_t i = 0; i < len; ++i) { - if (v2[i] > v1[i]) return true; - if (v2[i] < v1[i]) return false; + const int numeric_compare = + CompareNumericVersion(current_version.numbers, latest_version.numbers); + if (numeric_compare > 0) { + return true; + } + if (numeric_compare < 0) { + return false; } - // if versions are equal, compare by release date - if (!current_date.empty() && !latest_date.empty()) { - return IsNewerDate(current_date, latest_date); + const std::string resolved_latest_date = + !latest_date.empty() ? latest_date : latest_version.date; + if (!resolved_latest_date.empty() && !current_version.date.empty()) { + if (resolved_latest_date > current_version.date) { + return true; + } + if (resolved_latest_date < current_version.date) { + return false; + } + } else if (!resolved_latest_date.empty() && current_version.date.empty()) { + return true; + } else if (resolved_latest_date.empty() && !current_version.date.empty()) { + return false; + } + + const bool metadata_has_patch = latest_patch >= 0; + const bool latest_has_patch = metadata_has_patch || latest_version.has_patch; + if (latest_has_patch || current_version.has_patch) { + const int resolved_latest_patch = + metadata_has_patch ? latest_patch + : (latest_version.has_patch ? latest_version.patch + : 0); + const int resolved_current_patch = + current_version.has_patch ? current_version.patch : 0; + return resolved_latest_patch > resolved_current_patch; } - // if dates are not available, versions are equal return false; } @@ -140,19 +337,21 @@ nlohmann::json CheckUpdate() { } else { latest_release_date_ = ""; } + latest_patch_ = 0; + latest_patch_available_ = ReadPatchField(j, &latest_patch_); return j; } catch (std::exception&) { - latest_release_date_ = ""; + ResetLatestMetadata(); return nlohmann::json{}; } } else { - latest_release_date_ = ""; + ResetLatestMetadata(); return nlohmann::json{}; } } else { - latest_release_date_ = ""; + ResetLatestMetadata(); return nlohmann::json{}; } } -} // namespace crossdesk \ No newline at end of file +} // namespace crossdesk diff --git a/src/version_checker/version_checker.h b/src/version_checker/version_checker.h index 75e09c1..d6382ed 100644 --- a/src/version_checker/version_checker.h +++ b/src/version_checker/version_checker.h @@ -16,6 +16,12 @@ nlohmann::json CheckUpdate(); bool IsNewerVersion(const std::string& current, const std::string& latest); +// Pass latest_patch < 0 when patch metadata is unavailable. +bool IsNewerVersionWithMetadata(const std::string& current, + const std::string& latest, + const std::string& latest_date, + int latest_patch); + } // namespace crossdesk -#endif \ No newline at end of file +#endif diff --git a/xmake/targets.lua b/xmake/targets.lua index 443c79a..371e0b4 100644 --- a/xmake/targets.lua +++ b/xmake/targets.lua @@ -65,6 +65,14 @@ function setup_targets() set_default(false) add_files("tests/display_popup_hover_state_test.cpp") + target("version_checker_test") + set_kind("binary") + set_default(false) + add_packages("cpp-httplib", "nlohmann_json") + add_includedirs("src/version_checker") + add_files("tests/version_checker_test.cpp", + "src/version_checker/version_checker.cpp") + target("screen_capturer") set_kind("object") add_deps("rd_log", "common")