15.2 Generando Artifacts en Azure Pipelines

Para explicar la flexibilida de .net y Azure Pipelines voy a publicar 2 artifacts con el mismo código, uno para instalarlo en un AppService en Linux con Azure con una base de datos MySQL y el otro para publicar en un hosting windows por FTP.

Una parte interesante de Azure pipelines es que te permite crear variables de tipo Secret, las cuales estan ocultas y son independientes del código. Vamos a agregar una variable para guardar la cadena de conexión donde vamos a tener nuestra base de datos de MySQL puedes tener la bd de MySQL en un hosting, un servidor o en Azure. Esta cadena de conexión se va a reemplazar en el momento en que generamos el Pipeline para poder generar el script que actualizará la base de datos.

Modificando el Pipeline para generar el Artifact para publicar a Azure.

Agregando variables

  1. Entramos a Azure Devops en la opción Pipelines. Selecciona el último Pipeline y da clic en Edit.

2. Damos clic en el botón Variables para agregar una variable de tipo secret que tendra nuestra cadena de conexión, al tenerla en variable la cadena de conexión no esta disponible en el código fuente.

3. Da clic en el botón + para agregar una nueva variable. En la imagen ya tengo agregada la imagen, como es de tipo secreta solo se ve el nombre pero no el valor.

Como ejemplo voy a agregar otra variable para la base de datos de SQL de Azure. Con el nombre AzureSQLServer y en value agrego la cadena de conexión de Azure

Server=tcp:armhe.database.windows.net,1433;
Initial Catalog=CaducaRest;Persist Security Info=False;
User ID=AdminCaduca;Password=tuPassword;
MultipleActiveResultSets=False;Encrypt=True;
TrustServerCertificate=False;Connection Timeout=30;

Puedes ver que se agrega la variable. Da clic en Save

4. En la sección de variables puedes agregar las variables que deseas cambiar de tu archivo appsettings.json

appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "server=localhost;port=3306;database=caduca;user=AdminCaduca;Password=StKRV6MR6A;sslMode=none",
"SQLServerConnection": "Server=localhost;Database=caduca;User Id=AdminCaduca;Password=StKRV6MR6A;",
"AzureSQLConnection": "Server=tcp:armhe.database.windows.net,1433;Initial Catalog=CaducaRest;Persist Security Info=False;User ID=AdminCaduca;Password=StKRV6MR6A;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
},
"Tokens": {
"Key": "4eQXP7GTCTRwC6x6",
"Issuer": "http://localhost:5000",
"Audience": "http://localhost:5000"
},
"Logging": {
"LogLevel": {
"Default": "Debug"
}
},
"AllowedHosts": "*"
}

Por ejemplo para cambiar la cadena de Conexión DefaultConnection debemos agregar una variable con el nombre ConnectionStrings.DefaultConnection, para cada nivel del json se sustituye con un punto (.). Para utilizar las variables que definimos agregamos la variable con $ y entre paréntesis. Ejemplo $(Mysql).

variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
system.debug: false
ConnectionStrings.DefaultConnection: $(Mysql)

Para practicar el remplazo de variables puedes agregar variables para cambiar el key, Issuer, Audience, el nivel de log, con variables secretas o directamente en el código yaml. Como el código yaml se incluye en el repositorio de Github, es mejor utilizar variables para los valores que no deseas que esten disponibles para cualquier programador en el código fuente.

El archivo yaml queda de la siguiente manera:

azure-pipelines.yml
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
system.debug: false
ConnectionStrings.DefaultConnection: $(Mysql)
Tokens.Key: $(key)
Tokens.Issuer: $(issuer)
Tokens.Audience: $(issuer)
Logging.LogLevel.Default: "Error"

12.1.2 Transformando el appsettings con las variables

Agrega un task llamado File Transform para remplazar los valores de archivos json o xml por las variables definidas en nuestro pipeline.

Agrega lo siguiente:

  • Package or folder: Agrega la dirección de la carpeta donde esta el archivo que deseas cambiar. En este caso es: $(build.SourcesDirectory)/CaducaRest/

  • XML Transformation rules: Aqui se agregan las reglas para transformar algún webconfig.xml en este caso no voy a realizar ningíun cambio al webcofig o algún otro xml por lo que voy a borrar el contenido por default.

  • Json target files: Selecciona los archivos json que deseas cambiar. En esta caso solo deseo cambiar el archivo appsettings, por lo tanto teclea el valor **/appsettings.json

  • Xml target files: Aqui se seleccionan los archivos xml que deseas cambiar, en este caso no necesito cambiar nada.

Puedes ver la documentación oficial de este task aquí

Da clic en el botón Add y borra la propiedad del xml. Queda de la siguiente manera. Siempre agrego la propiedad displayName para tener un nombre mas descriptivo en este caso es: Transformar appsettings

displayName: "Transformar appsettings"
inputs:
folderPath: '$(build.SourcesDirectory)/CaducaRest/'
jsonTargetFiles: '**/appsettings.json'

12.1.3 Instalado Entity Framework

Para poder generar el script de MySQL para las migraciones necesitamos instalar la herramienta para ef, la podemos instalar con el siguiente comando:

dotnet tool install --global dotnet-ef

Para esto agrega una tarea Command line y en el área de Script agrega el comando

El archivo yaml queda de la siguiente manera:

displayName: "Instalar .NET Global"
inputs:
script: 'dotnet tool install --global dotnet-ef'

12.1.4 Generando el script para actualizar la base de datos

Después de generar el build del proyecto de Caduca Rest podemos agregar el siguiente comando para generar un script para actualizar la base de datos, con los siguientes parámetos:

  • -p: Indicas la ruta y el nombre del archivo de tu proyecto, el cual termina en .csproj, en este caso es $(build.SourcesDirectory)\CaducaRest\CaducaRest.csproj

  • -i: Genera un script que se puede utilizar en cualquier base de datos, se tenga un cambio o no en el commit.

  • -o: Indicas la ruta y nombre del archivo .sql con los cambios para la base de datos. En mi caso lo quiero en la ruta Scripts con el nombre update_to_latest.sql. $(Build.ArtifactStagingDirectory)\Scripts\update_to_latest.sql

El comando a ejecutar queda de la siguiente manera:

dotnet ef migrations script
-p $(build.SourcesDirectory)\CaducaRest\CaducaRest.csproj
-i -o $(Build.ArtifactStagingDirectory)
\Scripts\update_to_latest.sql

Para ejecutar este comando agrega el siguiente yaml

- script: dotnet ef migrations script -p $(build.SourcesDirectory)\CaducaRest\CaducaRest.csproj -i -o $(Build.ArtifactStagingDirectory)\Scripts\update_to_latest.sql
displayName: 'Generar Script'

Puedes ver la referencia completa del comando aquí

12.1.5 Generando un Artifact para Linux

Agrega una tarea de .Net Core para generar los archivos necesarios para publicar los archivos necesarios para linux.

  • Command: Seleciona publish para genear los archivos necesarios.

  • Arguments: Agrega los siguientes argumentos:

    • -r: Selecciona el runtime, en este caso para linux es linux-x64

    • -configuration: Selecciona la configuración que esta al inicio del pipeline $(BuildConfiguration)

    • --output: Selecciona la ruta donde se van . a publicar los archivos, en este caso es $(Build.ArtifactStagingDirectory)/linux

    • --framework: Selecciona el framework deseado en este caso deseo que sea .net core 3.1 netcoreapp3.1

La tarea queda de la siguiente manera:

displayName: "Publish Linux"
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-r linux-x64 --configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory) --framework netcoreapp3.1'
zipAfterPublish: true

Puedes ver la documentación oficial del comando publish aquí

12.1.6 Generando un Artifact para Windows

Para windows no es necesario agregar la opción runtime, y al nombre le voy a agregar que sea en una carpeta llamada win para diferenciarla del de linux.

--output $(Build.ArtifactStagingDirectory)/win
displayName: "Publish Win"
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)/win --framework netcoreapp3.1'
zipAfterPublish: true

12.1.7 Publicando los Artifacts

Por último después de los scripts de pruebas, agrega una tarea Publish build artifacts para generar artifacts para windows, linux y el script de sql.

Configura la tarea de la siguiente manera:

  • Path to publish: selecciona la carpeta que deseas publicar, en el caso de linux será $(Build.ArtifactStagingDirectory)/linux

  • Artifact name: Teclea el nombre el artifacto en este caso sera restLinux

  • Artifact publish Location: Selecciona si deseas publicarlo en el pipeline o en una carpeta compartida.

La tarea queda de la siguiente manera:

displayName: "Upload Artifacts linux"
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)/linux'
artifactName: 'restLinux'

Puedes agregar las demás tareas para publicar el artifact de windows y el script de sql

Puedes ver la documentación oficial de la tarea de publish aquí

El archivo queda de la sigueinte manera:

# ASP.NET Core (.NET Framework)
# Build and test ASP.NET Core projects targeting the full .NET Framework.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
trigger:
- master
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
system.debug: false
ConnectionStrings.DefaultConnection: $(Mysql)
Tokens.Key: $(key)
Tokens.Issuer: $(issuer)
Tokens.Audience: $(issuer)
Logging.LogLevel.Default: "Error"
steps:
displayName: "Instalar .NET Core 3.1.x"
inputs:
version: '3.1.103'
packageType: sdk
displayName: "Transformar appsettings"
inputs:
folderPath: '$(build.SourcesDirectory)/CaducaRest/'
jsonTargetFiles: '**/appsettings.json'
displayName: "Instalar .NET Global"
inputs:
script: 'dotnet tool install --global dotnet-ef'
- script: dotnet build --configuration $(buildConfiguration)
displayName: 'Build Project $(buildConfiguration) '
- script: dotnet ef migrations script -p $(build.SourcesDirectory)\CaducaRest\CaducaRest.csproj -i -o $(Build.ArtifactStagingDirectory)\Scripts\update_to_latest.sql
displayName: 'Generar Script'
displayName: Testing Unit Test
inputs:
command: 'test'
projects: '**/xUnit.CaducaRest/*.csproj'
arguments: '--configuration $(buildConfiguration) --collect "Code coverage"'
testRunTitle: 'Unit Test'
displayName: Testing Integration Test
inputs:
command: 'test'
projects: '**/CaducaRest.IntegrationTest/*.csproj'
arguments: '--configuration $(buildConfiguration) --collect "Code coverage"'
testRunTitle: 'Integration Test'
displayName: Copy postman scripts
inputs:
SourceFolder: 'postman'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/postman'
displayName: "Publish Linux"
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-r linux-x64 --configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)/linux --framework netcoreapp3.1'
zipAfterPublish: true
displayName: "Publish Win"
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)/win --framework netcoreapp3.1'
zipAfterPublish: true
displayName: "Upload Artifacts linux"
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)/linux'
artifactName: 'restLinux'
displayName: "Upload Artifacts win"
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)/win'
artifactName: 'restWin'
displayName: "Upload Artifacts MySQL"
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)/Scripts'
artifactName: 'Scripts'

Listo, si todo fue correcto cada vez que le des commit a tu código se crearán los artifacts. Solo hay un error en el reemplazo de variables que después voy a revisar porque marca error.

En el ejemplo tengo 5 artifacts porque tengo ya configurado los artifacts para postman y selenium que explicaré mas adelante.

En el botón View 3 changes se tiene la lista de cambios, ya sea en el commit o la lista de tareas cambiadas en Azure Devops

Un ejemplo de la lista de cambios es el siguiente

Si das clic en el link de los artifacts puedes ver la lista de artifacts y los puedes descargar.