Files
mabibli/MaBibli.Shared/Dtos/VersionApplication.cs
T
mathieuandClaude Opus 5 498579f27b Dis à l'application quelle version elle est, et sous quelle licence
Page « À propos » (/a-propos) : manuel annoncé à venir, contact, site de
l'auteur, version publiée avec sa date de build, et licence AGPL v3 avec le
lien vers le dépôt — l'AGPL attend que les utilisateurs d'un service en réseau
puissent en obtenir la source, ce lien n'est donc pas un ornement.

L'application ne connaissait pas sa version : elle vivait dans le manifeste du
paquet et dans les tags git, jamais dans le binaire. `build/publier-release.sh`
pose désormais `-p:Version` et `-p:MaBibliDateBuild` ; l'API rend les deux par
`GET /api/version`.

⚠️ L'horodatage est le TÉMOIN de l'injection, et rien ne le calcule côté
MSBuild. Sans lui, la version lue serait le « 1.0.0 » que le SDK pose par
défaut : il se lirait comme une vraie version alors qu'il ne désigne rien, et
c'est exactement la valeur qu'on ira chercher pour diagnostiquer un appareil au
cache dépareillé. Mieux vaut ne rien annoncer — un binaire compilé à la main se
déclare « version de développement », ce qui est vrai.

Autres décisions :

- l'entrée du menu est DÉTACHÉE des six destinations, par un filet au-dessus
  sur téléphone et à gauche en rangée sur PC : « À propos » est une annexe, pas
  une septième destination. Ses règles vivent en feuille GLOBALE, comme tout le
  menu — une règle scopée n'atteint pas ce que rend un NavLink ;
- le « mailto: » reste un lien même hors-ligne : il ne charge aucune page et
  passe la main au client de messagerie, qui sait mettre un message en attente.
  Le site et le dépôt, eux, basculent en boutons désactivés portant leur motif,
  comme les liens d'export des envies ;
- la version est un sixième instantané hors-ligne : on la lit justement quand
  quelque chose ne va pas, et un appareil qu'on soupçonne est souvent celui qui
  n'a plus de réseau. L'écran dit alors que c'est la dernière version vue du
  serveur.

Le point de rupture de 40 rem reste identique dans les deux feuilles, et
/a-propos est inscrite dans la table de remontée des routes.

Vérifié en exécution, l'API lancée : sans injection
`{"numero":null,"publiee":false}`, avec `-p:Version=0.4.1
-p:MaBibliDateBuild=…` `{"numero":"0.4.1","publiee":true}`. Le rendu des écrans
n'a PAS été vérifié en navigateur. 618 tests au vert (605 avant).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:50:34 +02:00

63 lines
2.9 KiB
C#

namespace MaBibli.Shared.Dtos;
/// <summary>
/// Version publiée de l'application, et date à laquelle son binaire a été produit.
/// </summary>
/// <remarks>
/// <para>
/// ⚠️ <b>Mieux vaut ne rien afficher qu'un numéro faux.</b> Sans injection par la chaîne de
/// publication, le SDK .NET pose <c>1.0.0</c> dans tout assembly : ce numéro se lirait comme une
/// vraie version alors qu'il ne désigne rien. Or c'est précisément la valeur qu'on ira lire pour
/// diagnostiquer un appareil dont le cache est dépareillé — s'y tromper coûterait le diagnostic.
/// </para>
/// <para>
/// D'où le <b>témoin</b> retenu : l'horodatage de build. Il n'existe que si
/// <c>build/publier-release.sh</c> (dépôt <c>mabibli_ynh</c>) l'a posé, et le script pose les deux
/// valeurs ensemble. Sans lui, la version lue est celle du SDK et n'est pas rendue : l'application
/// se déclare « version de développement », ce qui est vrai.
/// </para>
/// </remarks>
public record VersionApplication
{
/// <summary>Numéro publié (« 0.4.0 »), ou <c>null</c> pour un binaire non publié.</summary>
public string? Numero { get; init; }
/// <summary>Instant de production du binaire, en UTC. <c>null</c> hors publication.</summary>
public DateTimeOffset? DateBuild { get; init; }
/// <summary>Vrai quand le binaire vient bien de la chaîne de publication.</summary>
public bool Publiee => Numero is { Length: > 0 } && DateBuild is not null;
/// <summary>Un binaire compilé à la main : il n'a pas de version à annoncer.</summary>
public static readonly VersionApplication Developpement = new();
/// <summary>
/// Interprète les deux valeurs lues dans l'assembly. Fonction <b>pure</b>, pour être
/// éprouvable sans fabriquer d'assembly.
/// </summary>
/// <param name="versionInformative">
/// <c>AssemblyInformationalVersion</c>. ⚠️ Le SDK y ajoute parfois <c>+empreinte</c> (source
/// link, <c>SourceRevisionId</c>) : la partie utile est ce qui précède le <c>+</c>.
/// </param>
/// <param name="horodatage">Métadonnée <c>MaBibliDateBuild</c>, au format ISO 8601 en UTC.</param>
public static VersionApplication Depuis(string? versionInformative, string? horodatage)
{
// Le témoin d'abord : sans horodatage, rien n'a été injecté, et la version lue est celle
// que le SDK invente. On ne la rend donc pas, même si elle est présente.
if (!DateTimeOffset.TryParse(
horodatage,
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.AdjustToUniversal | System.Globalization.DateTimeStyles.AssumeUniversal,
out var date))
{
return Developpement;
}
var numero = versionInformative?.Split('+')[0].Trim();
return numero is { Length: > 0 }
? new VersionApplication { Numero = numero, DateBuild = date }
: Developpement;
}
}