diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index b89fe75..4daa925 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -6,7 +6,7 @@ name: release # ручной запуск -> только сборки, файлы остаются артефактами прогона on: push: - branches: [master, rc] + #branches: [master, rc] tags: ["v*"] workflow_dispatch: @@ -103,6 +103,8 @@ jobs: runs-on: linux env: VERSION: ${{ needs.create-release.outputs.version }} + # Только для релизов/пре-релизов; при ручном запуске канал определит CMake по ветке + BUILD_VERSION: ${{ needs.create-release.outputs.tag != '' && needs.create-release.outputs.version || '' }} RELEASE_ID: ${{ needs.create-release.outputs.release_id }} outputs: sums: ${{ steps.pack.outputs.sums }} @@ -114,7 +116,7 @@ jobs: export PATH="$QT_TOOLS/CMake/bin:$QT_TOOLS/Ninja:$PATH" rm -rf build AppDir dist cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_PREFIX_PATH="$QT_ROOT" \ + -DCMAKE_PREFIX_PATH="$QT_ROOT" -DLAUNCHER_BUILD_VERSION="$BUILD_VERSION" \ -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib cmake --build build -j 8 cmake --install build --prefix "$PWD/AppDir/usr" @@ -157,6 +159,8 @@ jobs: runs-on: windows env: VERSION: ${{ needs.create-release.outputs.version }} + # Только для релизов/пре-релизов; при ручном запуске канал определит CMake по ветке + BUILD_VERSION: ${{ needs.create-release.outputs.tag != '' && needs.create-release.outputs.version || '' }} RELEASE_ID: ${{ needs.create-release.outputs.release_id }} outputs: sums: ${{ steps.build.outputs.sums }} @@ -177,7 +181,7 @@ jobs: Enter-VsDevShell -VsInstallPath $vs -SkipAutomaticLocation -DevCmdArguments "-arch=x64 -host_arch=x64" $env:PATH = "$env:QT_TOOLS\CMake_64\bin;$env:QT_TOOLS\Ninja;$env:PATH" - cmake -S . -B $build -G Ninja -DCMAKE_BUILD_TYPE=Release "-DCMAKE_PREFIX_PATH=$env:QT_ROOT" + cmake -S . -B $build -G Ninja -DCMAKE_BUILD_TYPE=Release "-DCMAKE_PREFIX_PATH=$env:QT_ROOT" "-DLAUNCHER_BUILD_VERSION=$env:BUILD_VERSION" if ($LASTEXITCODE) { throw "cmake configure" } cmake --build $build if ($LASTEXITCODE) { throw "cmake build" } @@ -228,6 +232,8 @@ jobs: runs-on: macos env: VERSION: ${{ needs.create-release.outputs.version }} + # Только для релизов/пре-релизов; при ручном запуске канал определит CMake по ветке + BUILD_VERSION: ${{ needs.create-release.outputs.tag != '' && needs.create-release.outputs.version || '' }} RELEASE_ID: ${{ needs.create-release.outputs.release_id }} outputs: sums: ${{ steps.pack.outputs.sums }} @@ -239,7 +245,7 @@ jobs: export PATH="$QT_TOOLS/CMake/CMake.app/Contents/bin:$QT_TOOLS/Ninja:$PATH" rm -rf build stage dist dmgroot cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_PREFIX_PATH="$QT_ROOT" \ + -DCMAKE_PREFIX_PATH="$QT_ROOT" -DLAUNCHER_BUILD_VERSION="$BUILD_VERSION" \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" cmake --build build cmake --install build --prefix "$PWD/stage" diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bffe76..b801b39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,4 +9,13 @@ - Система сезонных сборок: полная автоматизация в один клик - Авторизация через ely.by и\или лицензию, с возможностью работы скинов ### Исправлено -- UI фиксы \ No newline at end of file +- UI фиксы + +## [1.0.1-rc1] — 2026-10-23 +### Добавлено +- Отображене текущей версии сборки лаунчера на главном окне +### Исправлено +- Исправлен баг когда невозможно выйти из модального окна сезонных сборок и\или отменить текущую загрузку +- Исправлен баг отображения статуса активной сборки на зелёном фоне +- Исправлен баг, что во время игры можно было потенциально сменить сборку +- Исправлен баг первого запуска после установки лаунчера, если у пользователя была не пустая .minecraft \ No newline at end of file diff --git a/Main.qml b/Main.qml index 8464393..9b0ce5e 100644 --- a/Main.qml +++ b/Main.qml @@ -101,6 +101,29 @@ Window { text: Loc.t.app.label.version.arg(Qt.application.version) font.pixelSize: 12 } + + // Канал сборки из суффикса версии (cmake/LauncherVersion.cmake): у релиза бейджа нет + Rectangle { + id: channelBadge + readonly property var channel: /-(dev|rc)/.exec(Qt.application.version) + visible: channel !== null + anchors.left: launcherVersion.right + anchors.leftMargin: 6 + anchors.verticalCenter: launcherVersion.verticalCenter + width: channelLabel.implicitWidth + 8 + height: channelLabel.implicitHeight + 2 + radius: 3 + color: channel && channel[1] === "rc" ? "#c08a1e" : "#b03a3a" + + Text { + id: channelLabel + anchors.centerIn: parent + text: channelBadge.channel ? channelBadge.channel[1].toUpperCase() : "" + color: "#ffffff" + font.pixelSize: 10 + font.bold: true + } + } } // ── Status toast ─────────────────────────────────────────────────────── diff --git a/cmake/LauncherVersion.cmake b/cmake/LauncherVersion.cmake new file mode 100644 index 0000000..cd6434b --- /dev/null +++ b/cmake/LauncherVersion.cmake @@ -0,0 +1,101 @@ +# Полная версия лаунчера и канал сборки (dev / rc / release). +# +# В репозитории хранится только число X.Y.Z из project(), одинаковое во всех +# ветках, — поэтому merge dev → rc → master не трогает версию. Канал +# вычисляется при сборке (по убыванию приоритета): +# 1. LAUNCHER_BUILD_VERSION — полная версия от CI: 1.0.1, 1.0.1-rc2; +# 2. тег на HEAD: vX.Y.Z — релиз, vX.Y.Z-rcN — rc; +# 3. ветка (из переменных CI или git): master — релиз, rc/beta — rc; +# 4. всё остальное, в т.ч. сборка без git, — dev. +# +# Работает в двух режимах: +# include() при configure — проверяет входные данные и пишет заголовок; +# cmake -P на каждой сборке — чтобы после git checkout без reconfigure +# канал и хэш коммита не устаревали. +# Входные переменные: LAUNCHER_VERSION_NUMERIC, LAUNCHER_SOURCE_DIR, +# LAUNCHER_VERSION_HEADER, LAUNCHER_BUILD_VERSION (необязательна). +# Результат: LAUNCHER_VERSION, LAUNCHER_CHANNEL и файл LAUNCHER_VERSION_HEADER. + +set(_numeric "${LAUNCHER_VERSION_NUMERIC}") + +find_package(Git QUIET) +function(_launcher_git out) + set(${out} "" PARENT_SCOPE) + if(NOT GIT_EXECUTABLE) + return() + endif() + execute_process(COMMAND "${GIT_EXECUTABLE}" ${ARGN} + WORKING_DIRECTORY "${LAUNCHER_SOURCE_DIR}" + RESULT_VARIABLE _rc OUTPUT_VARIABLE _out ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(_rc EQUAL 0) + set(${out} "${_out}" PARENT_SCOPE) + endif() +endfunction() + +set(_full "") +if(LAUNCHER_BUILD_VERSION) + set(_full "${LAUNCHER_BUILD_VERSION}") + string(REGEX REPLACE "^v" "" _full "${_full}") + string(REGEX REPLACE "-.*$" "" _base "${_full}") + if(NOT _base STREQUAL _numeric) + message(FATAL_ERROR "LAUNCHER_BUILD_VERSION=${LAUNCHER_BUILD_VERSION} не совпадает " + "с project(BiLauncher VERSION ${_numeric})") + endif() +else() + # Тег vX.Y.Z или vX.Y.Z-rcN ровно на HEAD, относящийся к текущей версии + _launcher_git(_tags tag --points-at HEAD) + string(REPLACE "\n" ";" _tags "${_tags}") + string(REPLACE "." "\\." _numeric_re "${_numeric}") + foreach(_tag IN LISTS _tags) + if(_tag MATCHES "^v(${_numeric_re}(-rc[0-9]+)?)$") + # Релизный тег важнее rc-тега на том же коммите + if(NOT _full OR NOT CMAKE_MATCH_2) + set(_full "${CMAKE_MATCH_1}") + endif() + endif() + endforeach() +endif() + +if(_full) + if(_full MATCHES "-rc[0-9]*$") + set(LAUNCHER_CHANNEL "rc") + elseif(_full STREQUAL _numeric) + set(LAUNCHER_CHANNEL "release") + else() + set(LAUNCHER_CHANNEL "dev") + endif() + set(LAUNCHER_VERSION "${_full}") +else() + # В CI на detached HEAD имени ветки в git нет — берём его из окружения + set(_branch "$ENV{GITHUB_HEAD_REF}") + if(NOT _branch AND "$ENV{GITHUB_REF_TYPE}" STREQUAL "branch") + set(_branch "$ENV{GITHUB_REF_NAME}") + endif() + if(NOT _branch) + _launcher_git(_branch rev-parse --abbrev-ref HEAD) + endif() + + if(_branch STREQUAL "master") + set(LAUNCHER_CHANNEL "release") + set(LAUNCHER_VERSION "${_numeric}") + elseif(_branch STREQUAL "rc" OR _branch STREQUAL "beta") + set(LAUNCHER_CHANNEL "rc") + set(LAUNCHER_VERSION "${_numeric}-rc") + else() + set(LAUNCHER_CHANNEL "dev") + set(LAUNCHER_VERSION "${_numeric}-dev") + _launcher_git(_hash rev-parse --short=7 HEAD) + if(_hash) + # Неотслеживаемые файлы (каталоги сборки и т.п.) не считаем + execute_process(COMMAND "${GIT_EXECUTABLE}" diff --quiet HEAD -- + WORKING_DIRECTORY "${LAUNCHER_SOURCE_DIR}" RESULT_VARIABLE _dirty ERROR_QUIET) + if(NOT _dirty EQUAL 0) + set(_hash "${_hash}.dirty") + endif() + string(APPEND LAUNCHER_VERSION "+${_hash}") + endif() + endif() +endif() + +configure_file("${CMAKE_CURRENT_LIST_DIR}/launcherversion.h.in" "${LAUNCHER_VERSION_HEADER}" @ONLY) diff --git a/cmake/launcherversion.h.in b/cmake/launcherversion.h.in new file mode 100644 index 0000000..bc49e92 --- /dev/null +++ b/cmake/launcherversion.h.in @@ -0,0 +1,7 @@ +#pragma once + +// Сгенерировано cmake/LauncherVersion.cmake — не редактировать. + +#define LAUNCHER_VERSION "@LAUNCHER_VERSION@" // 1.0.1, 1.0.1-rc2, 1.0.1-dev+a1b2c3d +#define LAUNCHER_VERSION_NUMERIC "@LAUNCHER_VERSION_NUMERIC@" +#define LAUNCHER_CHANNEL "@LAUNCHER_CHANNEL@" // dev, rc, release diff --git a/doc/build-and-release.md b/doc/build-and-release.md index f820730..8c1a490 100644 --- a/doc/build-and-release.md +++ b/doc/build-and-release.md @@ -40,15 +40,41 @@ cmake --build build Из Qt Creator проект открывается как обычный CMake-проект. -Версия приложения задаётся в начале `CMakeLists.txt` двумя значениями: +### Версия и канал сборки -- `project(BiLauncher VERSION x.y.z)` — только числа. Отсюда `PROJECT_VERSION` идёт в ресурс версии - `.exe`, в `Info.plist` и в установщик: все три принимают только числовую версию. -- `LAUNCHER_VERSION_SUFFIX` — метка пре-релиза, например `-dev` или `-rc1`. Для релиза пустая. +В репозитории хранится только число: `project(BiLauncher VERSION x.y.z)` в `CMakeLists.txt`. Оно +одинаковое во всех ветках, поэтому merge `dev` → `rc` → `master` версию не перетирает и не даёт +конфликтов. `PROJECT_VERSION` идёт в ресурс версии `.exe`, в `Info.plist` и в установщик: все три +принимают только числовую версию. -Полная строка `LAUNCHER_VERSION` = `PROJECT_VERSION` + суффикс (`1.0.1-dev`) уходит дефайном в код: -в `QCoreApplication::applicationVersion`, в аргументы запуска игры и в подпись «Версия: …» в левом -верхнем углу окна. Почему так, см. [ADR-0002](adr/0002-launcher-version-display.md). +Канал (dev / rc / релиз) в коде не записан — его на каждой сборке вычисляет +`cmake/LauncherVersion.cmake` и пишет в сгенерированный `/generated/launcherversion.h` +(`LAUNCHER_VERSION`, `LAUNCHER_CHANNEL`). Порядок определения: + +1. `-DLAUNCHER_BUILD_VERSION=1.0.1-rc2` — полная версия от CI; база обязана совпадать с + `project()`, иначе configure падает; +2. тег на HEAD: `vx.y.z` — релиз, `vx.y.z-rcN` — rc; +3. ветка (из `GITHUB_HEAD_REF`/`GITHUB_REF_NAME` или git): `master` — релиз, `rc` и `beta` — rc; +4. любая другая ветка или сборка без git — dev. + +| Сборка | `LAUNCHER_VERSION` | В окне | +|---------------------------|----------------------|-----------------| +| релиз (master, `vx.y.z`) | `1.0.1` | `Версия: 1.0.1` | +| rc из CI (`vx.y.z-rcN`) | `1.0.1-rc2` | + бейдж RC | +| rc локально | `1.0.1-rc` | + бейдж RC | +| dev | `1.0.1-dev+a1b2c3d` | + бейдж DEV | + +У dev-сборки после `+` — короткий хэш коммита, `.dirty` — есть незакоммиченные правки в +отслеживаемых файлах. Генерация идёт на каждой сборке, так что после `git checkout` другой +ветки reconfigure не нужен. + +Полная строка уходит в `QCoreApplication::applicationVersion`, в аргументы запуска игры и в +подпись «Версия: …» в левом верхнем углу окна; бейдж канала QML берёт из её суффикса. Почему так, +см. [ADR-0002](adr/0002-launcher-version-display.md). + +**Когда поднимать версию.** Номер поднимается в `dev` в начале цикла (сразу после релиза) и +дальше приходит в `rc` и `master` через merge. Хотфикс прямо в `rc` или `master` — правка и bump +patch там же, затем обратный merge `master` → `rc` → `dev`. ## Что собирается diff --git a/gamelauncher.h b/gamelauncher.h index 167f30f..acb42ba 100644 --- a/gamelauncher.h +++ b/gamelauncher.h @@ -1,5 +1,6 @@ #pragma once +#include "launcherversion.h" #include "minecraftversion.h" #include diff --git a/main.cpp b/main.cpp index 917b139..83ab7dd 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include "launcherpaths.h" +#include "launcherversion.h" #include "localization.h" #include diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 31ceed5..d72e26e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -58,10 +58,8 @@ qt_add_qml_module(bilauncher_testlib RESOURCES ${_module_res} NO_LINT ) -target_include_directories(bilauncher_testlib PUBLIC "${PROJECT_SOURCE_DIR}") -target_compile_definitions(bilauncher_testlib PUBLIC - LAUNCHER_VERSION="${LAUNCHER_VERSION}" -) +target_include_directories(bilauncher_testlib PUBLIC "${PROJECT_SOURCE_DIR}" "${LAUNCHER_GENERATED_DIR}") +add_dependencies(bilauncher_testlib launcher_version) target_link_libraries(bilauncher_testlib PUBLIC Qt6::Quick Qt6::QuickControls2 Qt6::Qml Qt6::Svg Qt6::Core Qt6::CorePrivate Qt6::Gui Qt6::Network )