From b7fb63ae405742caf4d863464177ca6bd0f374ce Mon Sep 17 00:00:00 2001
From: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
Date: Thu, 5 Sep 2024 15:53:40 -0400
Subject: [PATCH] versioning: use app/jamicore git SHAs as the version

Currently, the user-facing version number in the about dialog, is a
build-time timestamp that does help us isolate the project versions
accurately. Ultimately, we need to be able to reproduce issues
present in specific versions of Jami. This commit introduces a new
version number in the form: <client_sha>.<jamicore_sha>, allowing
us to reproduce builds more accurately.

Gitlab: #1820
Change-Id: Ie7e20b20da65284e33e745996c410f539b65080c
---
 CMakeLists.txt                                | 25 +++++++++++++-
 .../build/cmake/generate_version_info.cmake   | 34 +++++++++++++++++++
 src/app/mainview/components/AboutPopUp.qml    | 30 +++++++---------
 src/app/net/jami/Constants/JamiStrings.qml    |  4 ++-
 src/app/utilsadapter.cpp                      |  9 ++++-
 src/app/utilsadapter.h                        |  1 +
 src/version_info/version_info.cpp.in          |  4 +++
 src/version_info/version_info.h               |  6 ++++
 8 files changed, 92 insertions(+), 21 deletions(-)
 create mode 100644 extras/build/cmake/generate_version_info.cmake
 create mode 100644 src/version_info/version_info.cpp.in
 create mode 100644 src/version_info/version_info.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3fb265b36..5ed7eacb9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,7 +29,8 @@ else()
   project(jami)
 endif()
 
-include(${PROJECT_SOURCE_DIR}/extras/build/cmake/extra_tools.cmake)
+set(CMAKE_SCRIPTS_DIR ${PROJECT_SOURCE_DIR}/extras/build/cmake)
+include(${CMAKE_SCRIPTS_DIR}/extra_tools.cmake)
 
 option(WITH_DAEMON_SUBMODULE "Build with daemon submodule" ON)
 option(JAMICORE_AS_SUBDIR "Build Jami-core as a subdir dependency" OFF)
@@ -117,6 +118,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
 # src
 set(LIBCLIENT_SRC_DIR ${PROJECT_SOURCE_DIR}/src/libclient)
 set(APP_SRC_DIR ${PROJECT_SOURCE_DIR}/src/app)
+set(VERSION_INFO_DIR ${PROJECT_SOURCE_DIR}/src/version_info)
 # doc
 set(DOC_DIR ${PROJECT_SOURCE_DIR}/doc)
 # extras
@@ -210,6 +212,23 @@ include(FindPython3)
 find_package(Python3 3.6 REQUIRED COMPONENTS Interpreter)
 set(PYTHON_EXEC ${Python3_EXECUTABLE})
 
+# Versioning and build ID generation
+set(VERSION_FILE ${CMAKE_CURRENT_BINARY_DIR}/version_info.cpp)
+# Touch the file to make sure it exists at configure time as
+# we add it to the target_sources below.
+file(TOUCH ${VERSION_FILE})
+add_custom_target(
+  generate_version_info ALL
+  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+  COMMAND ${CMAKE_COMMAND}
+  -DAPP_SOURCE_DIR=${CMAKE_SOURCE_DIR}
+  -DAPP_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
+  -DCORE_SOURCE_DIR=${DAEMON_DIR}
+  -DCPP_INT_FILE=${VERSION_INFO_DIR}/version_info.cpp.in
+  -P ${CMAKE_SCRIPTS_DIR}/generate_version_info.cmake
+)
+list(APPEND CLIENT_INCLUDE_DIRS ${VERSION_INFO_DIR})
+
 # Resource auto-gen
 # QML and related code files
 # Check files in the app's src directory and force a reconfigure if it
@@ -247,6 +266,7 @@ set(QML_IMPORT_PATH ${QML_DIRS}
 add_definitions(-DQT_NO_KEYWORDS)
 
 set(COMMON_SOURCES
+  ${VERSION_FILE}
   ${APP_SRC_DIR}/bannedlistmodel.cpp
   ${APP_SRC_DIR}/accountlistmodel.cpp
   ${APP_SRC_DIR}/networkmanager.cpp
@@ -626,6 +646,9 @@ qt_add_executable(
   ${QML_RESOURCES_QML}
   ${SFPM_OBJECTS})
 
+# Make sure we can find the generated version file
+add_dependencies(${PROJECT_NAME} generate_version_info)
+
 foreach(MODULE ${QT_MODULES})
   list(APPEND QT_LIBS "Qt::${MODULE}")
 endforeach()
diff --git a/extras/build/cmake/generate_version_info.cmake b/extras/build/cmake/generate_version_info.cmake
new file mode 100644
index 000000000..ad7539623
--- /dev/null
+++ b/extras/build/cmake/generate_version_info.cmake
@@ -0,0 +1,34 @@
+find_package(Git QUIET REQUIRED)
+
+message(STATUS "Generating version information...")
+
+function(configure_version_string SOURCE_DIR VERSION_STRING_OUT)
+  # Get short git SHA
+  execute_process(
+    COMMAND "${GIT_EXECUTABLE}" rev-parse --short HEAD
+    WORKING_DIRECTORY "${SOURCE_DIR}"
+    OUTPUT_VARIABLE _GIT_SHA
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+  )
+
+  # Output the VERSION_STRING_OUT to the caller
+  set(${VERSION_STRING_OUT} "${_GIT_SHA}" PARENT_SCOPE)
+endfunction()
+
+# These need to be set to the parent scripts values for configure_file to work,
+# as it prepends CMAKE_CURRENT_SOURCE_DIR to the <input> and CMAKE_CURRENT_BINARY_DIR
+# to <output>.
+set(CMAKE_CURRENT_SOURCE_DIR ${APP_SOURCE_DIR})
+set(CMAKE_CURRENT_BINARY_DIR ${APP_BINARY_DIR})
+
+# Generate the version string for the application and core
+configure_version_string(${APP_SOURCE_DIR} APP_VERSION_STRING)
+configure_version_string(${CORE_SOURCE_DIR} CORE_VERSION_STRING)
+
+# Get output file names with the .in extension removed
+get_filename_component(VERSION_CPP_FILENAME ${CPP_INT_FILE} NAME_WE)
+set(VERSION_CPP_FILE "${CMAKE_CURRENT_BINARY_DIR}/${VERSION_CPP_FILENAME}.cpp")
+
+message(STATUS "infiles: ${CPP_INT_FILE}")
+message(STATUS "outfiles: ${VERSION_CPP_FILE}")
+configure_file(${CPP_INT_FILE} ${VERSION_CPP_FILE})
diff --git a/src/app/mainview/components/AboutPopUp.qml b/src/app/mainview/components/AboutPopUp.qml
index 0c645a0e4..ca80cdb8b 100644
--- a/src/app/mainview/components/AboutPopUp.qml
+++ b/src/app/mainview/components/AboutPopUp.qml
@@ -22,6 +22,7 @@ import Qt5Compat.GraphicalEffects
 import net.jami.Models 1.1
 import net.jami.Adapters 1.1
 import net.jami.Constants 1.1
+import net.jami.Helpers 1.1
 import "../../commoncomponents"
 
 BaseModalDialog {
@@ -63,23 +64,21 @@ BaseModalDialog {
                         source: JamiTheme.darkTheme ? JamiResources.logo_jami_standard_coul_white_svg : JamiResources.logo_jami_standard_coul_svg
                     }
 
-                    Rectangle {
-                        color: JamiTheme.backgroundRectangleColor
+                    Control {
                         Layout.fillHeight: true
                         Layout.fillWidth: true
-                        radius: 5
 
-                        ColumnLayout {
-                            id: sloganLayout
-
-                            anchors.verticalCenter: parent.verticalCenter
+                        background: Rectangle {
+                            color: JamiTheme.backgroundRectangleColor
+                            radius: 5
+                        }
 
+                        padding: 10
+                        contentItem: ColumnLayout {
+                            spacing: 4
                             TextEdit {
                                 id: jamiSlogansText
-
                                 Layout.alignment: Qt.AlignLeft
-                                Layout.margins: 10
-                                Layout.bottomMargin: 0
 
                                 wrapMode: Text.WordWrap
                                 font.pixelSize: JamiTheme.menuFontSize
@@ -100,23 +99,18 @@ BaseModalDialog {
                                 }
                             }
                             TextEdit {
-                                id: jamiVersionText
-
                                 Layout.alignment: Qt.AlignLeft
-                                Layout.margins: 10
-                                Layout.topMargin: 0
-                                Layout.maximumWidth: JamiTheme.preferredDialogWidth - 2*JamiTheme.preferredMarginSize
 
                                 font.pixelSize: JamiTheme.textFontSize
                                 padding: 0
-                                text: JamiStrings.version + ": " + UtilsAdapter.getVersionStr()
+                                readonly property bool isBeta: AppVersionManager.isCurrentVersionBeta()
+                                text: JamiStrings.buildID + ": " + UtilsAdapter.getBuildIDStr() + "\n" +
+                                      JamiStrings.version + ": " + (isBeta ? "(Beta) " : "") + UtilsAdapter.getVersionStr()
 
                                 selectByMouse: true
                                 readOnly: true
 
                                 color: JamiTheme.faddedFontColor
-                                horizontalAlignment: Text.AlignHCenter
-                                verticalAlignment: Text.AlignVCenter
                             }
                         }
                     }
diff --git a/src/app/net/jami/Constants/JamiStrings.qml b/src/app/net/jami/Constants/JamiStrings.qml
index 859c049b9..6d7c3e628 100644
--- a/src/app/net/jami/Constants/JamiStrings.qml
+++ b/src/app/net/jami/Constants/JamiStrings.qml
@@ -44,7 +44,9 @@ Item {
     property string reconnectTry: qsTr("Trying to reconnect to the Jami daemon (jamid)…")
 
     // AboutPopUp
-    property string version: qsTr("Version") + (AppVersionManager.isCurrentVersionBeta() ? " (Beta)" : "")
+    property string buildID: qsTr("Build ID")
+    property string version: qsTr("Version")
+
     property string declarationYear: "© 2015-2024"
     property string slogan: "Astarte"
     property string declaration: qsTr('Jami, a GNU package, is software for universal and distributed peer-to-peer communication that respects the freedom and privacy of its users. Visit <a href="https://jami.net" style="color: ' + JamiTheme.buttonTintedBlue + '">jami.net</a>' + ' to learn more.')
diff --git a/src/app/utilsadapter.cpp b/src/app/utilsadapter.cpp
index ec4ac13a7..e04e4088c 100644
--- a/src/app/utilsadapter.cpp
+++ b/src/app/utilsadapter.cpp
@@ -26,6 +26,7 @@
 #include "systemtray.h"
 #include "utils.h"
 #include "version.h"
+#include "version_info.h"
 #include "global.h"
 
 #include <api/datatransfermodel.h>
@@ -125,11 +126,17 @@ UtilsAdapter::getProjectCredits()
 }
 
 const QString
-UtilsAdapter::getVersionStr()
+UtilsAdapter::getBuildIDStr()
 {
     return QString(VERSION_STRING);
 }
 
+const QString
+UtilsAdapter::getVersionStr()
+{
+    return APP_VERSION_STRING + "." + CORE_VERSION_STRING;
+}
+
 void
 UtilsAdapter::setClipboardText(QString text)
 {
diff --git a/src/app/utilsadapter.h b/src/app/utilsadapter.h
index 8616d847f..db6b56d3a 100644
--- a/src/app/utilsadapter.h
+++ b/src/app/utilsadapter.h
@@ -96,6 +96,7 @@ public:
     Q_INVOKABLE void setToDefault(const Settings::Key key);
 
     Q_INVOKABLE const QString getProjectCredits();
+    Q_INVOKABLE const QString getBuildIDStr();
     Q_INVOKABLE const QString getVersionStr();
     Q_INVOKABLE void setClipboardText(QString text);
     Q_INVOKABLE const QString qStringFromFile(const QString& filename);
diff --git a/src/version_info/version_info.cpp.in b/src/version_info/version_info.cpp.in
new file mode 100644
index 000000000..3d452e270
--- /dev/null
+++ b/src/version_info/version_info.cpp.in
@@ -0,0 +1,4 @@
+#include "version_info.h"
+
+const QString APP_VERSION_STRING = "@APP_VERSION_STRING@";
+const QString CORE_VERSION_STRING = "@CORE_VERSION_STRING@";
\ No newline at end of file
diff --git a/src/version_info/version_info.h b/src/version_info/version_info.h
new file mode 100644
index 000000000..434eda5b9
--- /dev/null
+++ b/src/version_info/version_info.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <QString>
+
+extern const QString APP_VERSION_STRING;
+extern const QString CORE_VERSION_STRING;
-- 
GitLab