Versionado Semántico (o SemVer por sus siglas en inglés) es un esquema de versionado de software que estipula números de versión de tres partes en la forma <mayor>.<menor>.<corrección>
, como 1.0.2
, con un sufijo opcional de versiones preliminares en la forma -<prerelease>
, como en 1.0.2-beta
.
SemVer es quizás el esquema de versionado más ampliamente utilizado hoy en día. Por ejemplo, tanto Nuget como npm recomiendan y lo admiten, y VS Code lo utiliza también.
En la mayoría de los repos de GitHub que utilizan la función de Lanzamientos de GitHub para publicar versiones, verías un número de versión SemVer en el distintivo de la versión más reciente en la página de inicio, como se puede ver en la captura de pantalla a continuación:
Cuello necesito establecer un número de versión SemVer al construir proyectos de API de ASP.NET Core, y luego leer o informar esto en tiempo de ejecución.
Por ejemplo, si creo una API mínima con su versión establecida en 1.0.2-beta
, esta sería reportada por un endpoint /version
expuesto por la API, como se muestra en la captura de pantalla a continuación desde Hoppscotch (esta es una herramienta similar a Postman con la conveniencia de que se ejecuta en el navegador):
Verificar que la versión reportada desde los servicios desplegados, como aplicaciones web y APIs, es correcta, es una parte crucial de mi pipeline de CI/CD y es uno de los tests de humo que utilizo para determinar si un despliegue ha sido exitoso.
Una ligera complicación al establecer un número de versión SemVer en ensamblados .NET es que .NET originalmente utilizaba números de versión de cuatro partes como 1.0.3.212
y los ensamblados aún tienen estos (ensamblado es el término de .NET para unidades de código compiladas a bytecode de .NET, los más típicos de estos siendo dll’s y exe’s).
La otra es que .NET no tiene uno, sino muchos, ligeramente diferentes, números de versión que están presentes en el mismo ensamblado.
En este artículo, te mostraré cómo evitar estas peculiaridades y aplicar un número de versión SemVer en un ensamblado .NET durante la compilación. Es decir, en un compilado .exe o .dll, y cómo leerlo en tiempo de ejecución.
Tabla de Contenidos
Estructura de un Número de Versión SemVer
Considera un número de versión SemVer como 1.0.2
o 1.0.2-beta
. Tiene la forma <mayor>
.<menor>
.< parche>
–<prerelease>
Esto es lo que significan los varios componentes:
El componente <major>
del número de versión se incrementaría solo si la nueva versión rompería una versión existente (más reciente).
En caso de una aplicación de interfaz de usuario, los clientes pueden entenderse como clientes humanos. Así que si la nueva versión rompería los activos existentes de los usuarios, como definiciones de flujo de trabajo, esto requeriría incrementar el número de versión mayor. En este evento, si la versión anterior era 1.0.2
, la nueva versión debería ser 2.0.0
(todos los componentes inferiores del número de versión se reinician).
En caso de una biblioteca, como un paquete de biblioteca en Nuget o NPM, los clientes serían otros códigos. Así que si la nueva versión rompería el código del cliente existente, es decir, no sería compatible con versiones anteriores de sí misma, entonces nuevamente se incrementaría el componente <major>
.
<minor>
se incrementa si se han añadido nuevas características pero la nueva versión sigue siendo compatible con versiones anteriores. Así que de 1.0.2
se pasaría a 1.1.0
.
<patch>
se incrementa cuando se necesita hacer una nueva versión aunque no haya cambios que rompan la compatibilidad y no se haya añadido nueva funcionalidad. Esto podría ocurrir, por ejemplo, si había que lanzar una corrección de errores.
-<prerelease>
es un sufijo opcional. Se adjunta típicamente a un número de versión de tres partes cuando el software necesita estar disponible durante fases de prueba previas al lanzamiento, como alfa y beta. Por ejemplo, antes de lanzar generalmente la versión 1.0.2
de su software, puede hacerla disponible para sus probadores beta como 1.0.2-beta
.
El componente <prerelease>
puede ser prácticamente cualquier cadena de su elección y el único requisito es que sea un identificador alfanumérico como beta
o 12
o alpha2
(sin caracteres otros que números o letras del alfabeto) o varios identificadores alfanuméricos separados por un punto (.
), por ejemplo, development.version
.
Los muchos números de versión de un ensamblado .NET
Como explica Andrew Lock en su artículo sobre versionado de .NET, un ensamblado .NET no tiene uno sino varios números de versión diferentes:
-
AssemblyVersion: Este es un número de versión de cuatro partes, por ejemplo,
1.0.2.0
. Se utiliza por el tiempo de ejecución al cargar ensamblados enlazados. -
FileVersion: Este es el número de versión informado para un archivo .dll en el Explorador de Archivos de Windows cuando hace clic derecho en el ensamblado y selecciona Propiedades.
-
VersiónInformativa: Otra versión más, y al igual que FileVersion, se puede ver en el diálogo de Propiedades si haces clic derecho sobre el ensamblaje en Windows y seleccionas Propiedades. Esta puede contener cadenas de texto y no solo números enteros y puntos, como AssemblyVersion y FileVersion están restringidos.
-
VersiónPaquete: Si el proyecto es un paquete Nuget, esta sería la versión del paquete del que forma parte el ensamblaje.
Todas estas versiones se emiten en el ensamblaje durante la compilación como metadatos. Puedes verlas si inspeccionas el ensamblaje con JetBrains dotPeek (gratis) o Red gate Reflector (no gratis) o herramientas similares.
FileVersion y VersiónInformativa también se pueden ver en la pestaña Detalles del diálogo de Propiedades que aparece cuando haces clic derecho en el archivo del ensamblaje en el Explorador de Archivos de Windows y seleccionas Propiedades:
En la captura de pantalla de arriba, “Versión del producto” es la etiqueta para InformationalVersion mientras que “Versión del archivo” es la etiqueta para FileVersion.
De los cuatro tipos de números de versión descritos anteriormente, solo los primeros tres se aplican a cualquier ensamblaje (es decir, independientemente de si el ensamblaje es parte de un paquete NuGet).
De esos tres, AssemblyVersion siempre agrega un 0
en el cuarto lugar si intenta establecer una versión SemVer que solo tiene tres números (más un sufijo prerelease opcional). Por ejemplo, si intenta establecer una versión SemVer de 1.0.2-beta
durante la compilación y luego leer el valor AssemblyVersion en tiempo de ejecución en el ensamblaje, sería 1.0.2.0
.
FileVersion hace lo mismo, como se muestra en la captura de pantalla de arriba.
InformationalVersion es el único número de versión que se establecería exactamente a la versión del servidor que establece durante la compilación, como muestra la captura de pantalla de arriba.
Por lo tanto, InformationalVersion es la versión que se debe leer en tiempo de ejecución para recuperar la versión SemVer del ensamblaje.
Cómo Establecer un Número de Versión SemVer
Hay dos cosas que debe hacer para establecer un número de versión SemVer en un ensamblaje durante la compilación.
Primero, en un elemento <PropertyGroup>
en el archivo csproj
del proyecto, agregue el elemento <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
:
<PropertyGroup>
...
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup>
Como se describe en este problema, esto asegura que InformationalVersion se configure exactamente con el número de versión SemVer que especificamos y no se adjunte un +<código de hash>
al final.
Segundo, pásale el número de versión como valor de la propiedad Version
en el comando dotnet build
, por ejemplo:
dotnet build --configuration Release -p Version=1.0.2-beta
Esto establecería InformationalVersion en el ensamblado compilado (.exe o .dll) como 1.0.2-beta
.
INCIDENTALMENTE, también establecería AssemblyVersion y FileVersion (se agregaría un 0
al final de 1.0.2
), pero no estamos interesados en esos.
Nota que en lugar de pasar el argumento Version
en la línea de comandos, puedes establecer la propiedad de MS Build <Version>1.0.2-beta</Version>
en un elemento <PropertyGroup>
en el archivo csproj. Sin embargo, pasar un valor del parámetro Version
al dotnet build
es más sencillo porque el archivo csproj no necesita ser modificado cada vez que se incrementa el número de versión. Esto es útil en pipelines de CD. Además, por defecto, los archivos csproj no tienen ninguna propiedad relacionada con la versionado.
Cómo Leer la Versión SemVer de un Ensamblado en Tiempo de Ejecución
El código que lee InfromationalVersion en tiempo de ejecución es el siguiente:
string? version = Assembly.GetEntryAssembly()?.
GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.
InformationalVersion;
En mis APIs mínimas, para agregar un endpoint /version
como se muestra en la sección de Introducción anterior, coloco el fragmento anterior en Program.cs
, luego agrego el siguiente fragmento inmediatamente después. Nota que todo debería aparecer antes de que se llame a builder.Build()
:
//este objeto de un tipo anónimo será
//serializado como JSON en el cuerpo de la respuesta
//cuando sea devuelto por un manejador
var objVersion = new { Version = version ?? "" };
//OTRO CÓDIGO
//var app = builder.Build()
Después de que se llama a builder.Build()
, creo el manejador para el endpoint /version
:
app.MapGet("/version", () => objVersion);
Ahora, cuando ejecuto el proyecto de la API y llamo al endpoint /version
, obtengo el número de versión de vuelta en un objeto JSON en el cuerpo de la respuesta HTTP:
{
"version": "1.0.2-beta"
}
Esto es lo que mostraba la captura de pantalla de Hoppscotch en la Introducción.
Conclusión
Este artículo le mostró cómo establecer un número de versión SemVer en sus ensamblados, bibliotecas o aplicaciones de .NET.
También le mostró cómo leer el número de versión en tiempo de ejecución.
Source:
https://www.freecodecamp.org/news/set-semantic-versioning-for-net/