Mise en place du versionnement horodaté (même mécanisme que shipheart_qt) : génération de _version.h à chaque build, archivage automatique d'une copie horodatée de l'exécutable dans versions/, affichage de la version au démarrage

This commit is contained in:
2026-09-24 08:52:06 +02:00
parent 88591790d0
commit b74f14452e
15 changed files with 98 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
260924_084534
+27
View File
@@ -7,8 +7,35 @@ set(CMAKE_AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS Core HttpServer Sql) find_package(Qt6 REQUIRED COMPONENTS Core HttpServer Sql)
# ---------------------------------------------------------------------------
# Version dynamique, injectée dans _version.h à CHAQUE build - même mécanisme
# que shipheart_qt (cf synthèse, format yymmdd_HHMMSS, ex: 260924_080910) :
# une cible personnalisée sans suivi de fichier est toujours considérée
# périmée par Ninja/Make et se ré-exécute avant chaque build, contrairement à
# string(TIMESTAMP ...) seul qui ne s'évalue qu'à la configuration du projet.
# ---------------------------------------------------------------------------
add_custom_target(generate_version ALL
COMMAND ${CMAKE_COMMAND}
-DOUTPUT_FILE=${CMAKE_CURRENT_SOURCE_DIR}/_version.h
-P ${CMAKE_CURRENT_SOURCE_DIR}/generate_version.cmake
COMMENT "Régénération de _version.h (horodatage)"
BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/_version.h
)
add_executable(shipheart_api add_executable(shipheart_api
main.cpp main.cpp
# Header listé pour visibilité dans l'IDE (non obligatoire pour la compilation) :
_version.h
)
add_dependencies(shipheart_api generate_version)
add_custom_command(TARGET shipheart_api POST_BUILD
COMMAND ${CMAKE_COMMAND}
-DEXE_PATH=$<TARGET_FILE:shipheart_api>
-DSRC_DIR=${CMAKE_CURRENT_SOURCE_DIR}
-P ${CMAKE_CURRENT_SOURCE_DIR}/archive_version.cmake
COMMENT "Archivage d'une copie horodatée de l'exécutable"
) )
target_link_libraries(shipheart_api PRIVATE target_link_libraries(shipheart_api PRIVATE
Binary file not shown.
+6
View File
@@ -0,0 +1,6 @@
#ifndef _VERSION_H
#define _VERSION_H
#define VERSION "260924_084534"
#endif // _VERSION_H
+8
View File
@@ -0,0 +1,8 @@
file(READ "${SRC_DIR}/.current_version" APP_VERSION)
string(STRIP "${APP_VERSION}" APP_VERSION)
file(MAKE_DIRECTORY "${SRC_DIR}/versions")
file(COPY "${EXE_PATH}" DESTINATION "${SRC_DIR}/versions")
file(RENAME "${SRC_DIR}/versions/shipheart_api" "${SRC_DIR}/versions/shipheart_api_${APP_VERSION}")
message(STATUS "Exécutable archivé : versions/shipheart_api_${APP_VERSION}")
Binary file not shown.
+22
View File
@@ -0,0 +1,22 @@
# Exécuté à CHAQUE build (pas seulement à chaque reconfiguration CMake) via
# la cible "generate_version" dans CMakeLists.txt, qui n'a aucun suivi de
# fichier et est donc toujours considérée périmée par Ninja/Make.
#
# Même mécanisme que shipheart_qt (cf synthèse) : deux exécutables différents
# (même par une modification infime du code) obtiennent systématiquement un
# horodatage différent, garantissant l'unicité recherchée.
string(TIMESTAMP APP_VERSION "%y%m%d_%H%M%S")
file(WRITE "${OUTPUT_FILE}"
"#ifndef _VERSION_H
#define _VERSION_H
#define VERSION \"${APP_VERSION}\"
#endif // _VERSION_H
")
file(WRITE "${CMAKE_CURRENT_LIST_DIR}/.current_version" "${APP_VERSION}")
message(STATUS "Version ShipHeart API régénérée : ${APP_VERSION}")
Binary file not shown.
+3
View File
@@ -15,6 +15,8 @@
#include <QPair> #include <QPair>
#include <QDebug> #include <QDebug>
#include "_version.h"
// ============================================================================ // ============================================================================
// shipheart_api - Phase 4 : première tranche verticale, domaine "circuit" // shipheart_api - Phase 4 : première tranche verticale, domaine "circuit"
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -193,6 +195,7 @@ static void assurer_connexion_bdd()
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
qInfo() << "shipheart_api version" << VERSION;
// --- Connexion MariaDB locale --- // --- Connexion MariaDB locale ---
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
Binary file not shown.
+31
View File
@@ -164,6 +164,32 @@ static QHttpServerResponse withCors(QHttpServerResponse &&resp)
return std::move(resp); return std::move(resp);
} }
// ----------------------------------------------------------------------------
// Vérifie que la connexion MariaDB (ouverte une seule fois au démarrage) est
// toujours active, et la rétablit sinon. isOpen() peut rester vrai même après
// une coupure réseau ou un dépassement de wait_timeout côté serveur - un test
// léger (SELECT 1) confirme que la connexion répond réellement. Sans ce
// contrôle, l'API reste "vivante" indéfiniment mais chaque requête SQL échoue
// silencieusement après une longue période d'inactivité (constaté après
// plusieurs semaines d'exécution continue, cf synthèse).
// À appeler en tout premier dans chaque route qui touche la base.
// ----------------------------------------------------------------------------
static void assurer_connexion_bdd()
{
QSqlDatabase db = QSqlDatabase::database();
if (db.isOpen()) {
QSqlQuery test(db);
if (test.exec("SELECT 1")) return;
}
qWarning() << "Connexion MariaDB perdue, tentative de reconnexion...";
db.close();
if (db.open()) {
qInfo() << "Reconnexion MariaDB réussie.";
} else {
qCritical() << "Reconnexion MariaDB échouée :" << db.lastError().text();
}
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
@@ -196,6 +222,7 @@ int main(int argc, char *argv[])
// requêtes onglets de mise_a_jour_onglets_circuits()). // requêtes onglets de mise_a_jour_onglets_circuits()).
// ------------------------------------------------------------------ // ------------------------------------------------------------------
server.route("/elements/familles", [](const QHttpServerRequest &request) { server.route("/elements/familles", [](const QHttpServerRequest &request) {
assurer_connexion_bdd();
QUrlQuery query(request.query()); QUrlQuery query(request.query());
const QString type = query.queryItemValue("type"); const QString type = query.queryItemValue("type");
@@ -352,6 +379,7 @@ int main(int argc, char *argv[])
// différents, mais un seul, appelé deux fois). // différents, mais un seul, appelé deux fois).
// ------------------------------------------------------------------ // ------------------------------------------------------------------
server.route("/elements", [](const QHttpServerRequest &request) { server.route("/elements", [](const QHttpServerRequest &request) {
assurer_connexion_bdd();
QUrlQuery query(request.query()); QUrlQuery query(request.query());
const QString type = query.queryItemValue("type"); const QString type = query.queryItemValue("type");
const QString familleStr = query.queryItemValue("famille"); const QString familleStr = query.queryItemValue("famille");
@@ -610,6 +638,7 @@ int main(int argc, char *argv[])
// ------------------------------------------------------------------ // ------------------------------------------------------------------
server.route("/elements/circuit/<arg>", QHttpServerRequest::Method::Patch, server.route("/elements/circuit/<arg>", QHttpServerRequest::Method::Patch,
[](int idCircuit, const QHttpServerRequest &request) { [](int idCircuit, const QHttpServerRequest &request) {
assurer_connexion_bdd();
const QJsonDocument body = QJsonDocument::fromJson(request.body()); const QJsonDocument body = QJsonDocument::fromJson(request.body());
if (!body.isObject()) { if (!body.isObject()) {
QJsonObject err; QJsonObject err;
@@ -686,6 +715,7 @@ int main(int argc, char *argv[])
// Circuits/batteries (sélection de variable côté client) à venir. // Circuits/batteries (sélection de variable côté client) à venir.
// ------------------------------------------------------------------ // ------------------------------------------------------------------
server.route("/mesures", [](const QHttpServerRequest &request) { server.route("/mesures", [](const QHttpServerRequest &request) {
assurer_connexion_bdd();
QUrlQuery query(request.query()); QUrlQuery query(request.query());
const QString type = query.queryItemValue("type"); const QString type = query.queryItemValue("type");
const QString idStr = query.queryItemValue("id"); const QString idStr = query.queryItemValue("id");
@@ -1037,6 +1067,7 @@ int main(int argc, char *argv[])
// 90% du temps de traitement natif pour un gain marginal). // 90% du temps de traitement natif pour un gain marginal).
// ------------------------------------------------------------------ // ------------------------------------------------------------------
server.route("/energie", [](const QHttpServerRequest &request) { server.route("/energie", [](const QHttpServerRequest &request) {
assurer_connexion_bdd();
QUrlQuery query(request.query()); QUrlQuery query(request.query());
const QString type = query.queryItemValue("type"); const QString type = query.queryItemValue("type");
const QString idStr = query.queryItemValue("id"); const QString idStr = query.queryItemValue("id");
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.