Guía de CI/CD en GitLab para (casi) principiantes absolutos.

O cómo conseguir bonitos badges para tu proyecto en una tarde de codificación sin estrés

Seguramente, a cada desarrollador que tiene al menos un proyecto personal en algún momento le pica la curiosidad sobre bonitos badges con estados, cobertura de código, versiones de paquetes en nuget... Y esa curiosidad me llevó a escribir este artículo. En el proceso de preparación para su redacción, conseguí esta belleza en uno de mis proyectos:

Guía de CI/CD en GitLab para (casi) principiantes absolutos.

En este artículo se revisará la configuración básica de integración y entrega continua para un proyecto de biblioteca de clases en .Net Core en GitLab, incluyendo la publicación de documentación en GitLab Pages y el envío de paquetes construidos a un feed privado en Azure DevOps.

Como entorno de desarrollo se utilizó VS Code con la extensión GitLab Workflow (para validar el archivo de configuración directamente desde el entorno de desarrollo).

Introducción breve

¿CD es cuando acabas de hacer push y al cliente ya se le ha caído todo?

Qué es CI/CD y para qué sirve se puede encontrar fácilmente en Google. También es fácil encontrar documentación completa sobre cómo configurar pipelines en GitLab sin mucho problema. Aquí describiré brevemente y, en la medida de lo posible, sin errores el proceso de funcionamiento del sistema desde una perspectiva general:

  • el desarrollador envía un commit al repositorio, crea un merge request a través del sitio web, o de alguna otra manera, explícita o implícitamente, inicia el pipeline,
  • de la configuración se seleccionan todas las tareas cuyas condiciones permiten ejecutarlas en este contexto,
  • las tareas se organizan según sus etapas,
  • las etapas se ejecutan una tras otra, es decir, en paralelo se ejecutan todas las tareas de esa etapa,
  • si una etapa falla (es decir, si al menos una de las tareas de la etapa falla) — el pipeline se detiene (casi siempre),
  • si todas las etapas se completan exitosamente, el pipeline se considera que ha pasado correctamente.

Así, tenemos:

  • un pipeline es un conjunto de tareas, organizadas en etapas, donde se puede compilar, probar, empaquetar el código, desplegar la construcción lista en un servicio en la nube, etc.
  • etapa (stage) — unidad de organización del pipeline, contiene 1+ tarea,
  • tarea (job) — unidad de trabajo en el pipeline. Consiste en un script (obligatorio), condiciones de ejecución, configuraciones de publicación/caché de artefactos y mucho más.

Por lo tanto, la tarea al configurar CI/CD se reduce a crear un conjunto de tareas que implementen todas las acciones necesarias para compilar, probar y publicar el código y los artefactos.

Antes de empezar: ¿por qué?

  • ¿Por qué GitLab?

Porque cuando surgió la necesidad de crear repositorios privados para proyectos personales, en GitHub eran de pago, y yo soy tacaño. Los repositorios se volvieron gratuitos, pero hasta ahora eso no es suficiente motivo para mudarme a GitHub.

  • ¿Por qué no Azure DevOps Pipelines?

Porque ahí la configuración es elemental: ni siquiera se requieren conocimientos de línea de comandos. La integración con proveedores externos de git se hace en un par de clics, importar claves SSH para enviar commits al repositorio también es así, el pipeline se configura fácilmente incluso sin plantilla.

Posición inicial: qué hay y qué se desea

Tenemos:

  • un repositorio en GitLab.

Queremos:

  • compilación automática y pruebas para cada merge request,
  • compilación de paquetes para cada merge request y push a master, siempre que haya una línea específica en el mensaje de commit,
  • envío de los paquetes compilados a un feed privado en Azure DevOps,
  • compilación de la documentación y publicación en GitLab Pages,
  • ¡badges!11

Los requisitos descritos se alinean perfectamente con el siguiente modelo de pipeline:

  • Etapa 1 — Compilación
    • Compilamos el código, los archivos de salida se publican como artefactos
  • Etapa 2 — Pruebas
    • Obtenemos los artefactos de la etapa de compilación, ejecutamos las pruebas y recolectamos los datos de cobertura del código
  • Etapa 3 — Envío
    • Tarea 1 — compilamos el paquete nuget y lo enviamos a Azure DevOps
    • Tarea 2 — compilamos el sitio desde xmldoc en el código fuente y lo publicamos en GitLab Pages

¡Empecemos!

Compilamos la configuración

Preparamos las cuentas

  1. Creamos una cuenta en Microsoft Azure

  2. Vamos a Azure DevOps

  3. Creamos un nuevo proyecto

    1. Nombre — cualquier cosa
    2. Visibilidad — cualquier
      Guía de CI/CD en GitLab para (casi) principiantes absolutos.

  4. Al hacer clic en el botón Crear, se creará el proyecto y se redirigirá a su página. En esta página se pueden deshabilitar las funcionalidades innecesarias yendo a la configuración del proyecto (enlace inferior en la lista de la izquierda -> Overview -> bloque Azure DevOps Services)
    Guía de CI/CD en GitLab para (casi) principiantes absolutos.

  5. Vamos a Atrifacts, hacemos clic en Crear feed

    1. Ingresamos el nombre de la fuente
    2. Seleccionamos la visibilidad
    3. Desmarcamos la casilla Incluir paquetes de fuentes públicas comunes, para que la fuente no se convierta en un basurero clon de nuget
      Guía de CI/CD en GitLab para (casi) principiantes absolutos.

  6. Hacemos clic en Conectar al feed, seleccionamos Visual Studio, del bloque Configuración de máquina copiamos la Fuente
    Guía de CI/CD en GitLab para (casi) principiantes absolutos.

  7. Vamos a la configuración de la cuenta, seleccionamos Token de acceso personal
    Guía de CI/CD en GitLab para (casi) principiantes absolutos.

  8. Creamos un nuevo token de acceso

    1. Nombre — arbitrario
    2. Organización — actual
    3. La duración es de un máximo de 1 año
    4. Ámbito (scope) — Packaging/Read & Write
      Guía de CI/CD en GitLab para (casi) principiantes absolutos.

  9. Copiamos el token creado — después de cerrar la ventana modal, el valor no estará disponible

  10. Accedemos a la configuración del repositorio en GitLab, seleccionamos la configuración de CI/CD
    Guía de CI/CD en GitLab para (casi) principiantes absolutos.

  11. Desplegamos el bloque Variables, añadimos uno nuevo

    1. Nombre — cualquier nombre sin espacios (disponible en la línea de comandos)
    2. Valor — el token de acceso del punto 9
    3. Seleccionamos Mask variable
      Guía de CI/CD en GitLab para (casi) principiantes absolutos.

Con esto, la configuración preliminar está completa.

Preparamos el esqueleto de configuración

De forma predeterminada, para configurar CI/CD en GitLab se utiliza un archivo .gitlab-ci.yml en la raíz del repositorio. Se puede establecer una ruta arbitraria a este archivo en la configuración del repositorio, pero en este caso no es necesario.

Como se puede ver por la extensión, el archivo contiene configuración en formato YAML. La documentación describe detalladamente qué claves pueden contenerse en el nivel superior de configuración y en cada uno de los niveles anidados.

Primero añadiremos al archivo de configuración un enlace a la imagen de docker, en la que se ejecutarán las tareas. Para esto, encontramos la página de imágenes .Net Core en Docker Hub. Hay GitHub hay una guía detallada sobre qué imagen elegir para diferentes tareas. Para nuestra compilación, elegiremos la imagen con .Net Core 3.1, así que simplemente añadimos la siguiente línea en la configuración

image: mcr.microsoft.com/dotnet/core/sdk:3.1

A partir de ahora, al ejecutar el pipeline almacenado en imágenes de Microsoft, se descargará la imagen especificada, en la que se ejecutarán todas las tareas de la configuración.

El siguiente paso es añadir stageetapas. Por defecto, GitLab define 5 etapas:

  • .pre — se ejecuta antes de todas las etapas,
  • .post — se ejecuta después de todas las etapas,
  • build — la primera después de .pre la etapa,
  • test — segunda etapa,
  • deploy — tercera etapa.

No hay nada que impida declararlas explícitamente, sin embargo. El orden en el que se indican las etapas influye en el orden en que se ejecutan. Para ser completos, añadamos a la configuración:

stages:
  - build
  - test
  - deploy

Para depurar, tiene sentido obtener información sobre el entorno en el que se ejecutan las tareas. Añadamos un conjunto global de comandos que se ejecutará antes de cada tarea, usando before_script:

before_script:
  - $PSVersionTable.PSVersion
  - dotnet --version
  - nuget help | select-string Version

Solo queda añadir al menos una tarea para que al enviar commits se inicie el pipeline. Por ahora, añadiremos una tarea vacía para demostración:

dummy job:
  script:
    - echo ok

Iniciamos la validación, recibimos un mensaje de que todo está bien, hacemos commit, pusheamos, miramos los resultados en el sitio... Y obtenemos un error de script — bash: .PSVersion: comando no encontrado. ¿Qué demonios?

Todo tiene sentido: por defecto, los runners (responsables de ejecutar los scripts de tareas y proporcionados por GitLab) utilizan bash para ejecutar comandos. Podemos solucionar esto, indicando explícitamente en la descripción de la tarea, qué etiquetas deben tener el runner del pipeline ejecutor:

tarea dummy en windows:
  script:
    - echo ok
  tags:
    - windows

¡Excelente! Ahora el pipeline se ejecuta.

El lector atento, al repetir los pasos indicados, notará que la tarea se ejecutó en la etapa test, aunque no especificamos una etapa. Como se puede deducir, test es la etapa por defecto.

Continuemos creando la estructura de configuración, añadiendo todas las tareas descritas anteriormente:

tarea de build:
  script:
    - echo "building..."
  tags:
    - windows
  stage: build

tarea de test y cobertura:
  script:
    - echo "running tests and coverage analysis..."
  tags:
    - windows
  stage: test

tarea de empaquetado y despliegue:
  script:
    - echo "packing and pushing to nuget..."
  tags:
    - windows
  stage: deploy

páginas:
  script:
    - echo "creating docs..."
  tags:
    - windows
  stage: deploy

Obtenemos un pipeline que, aunque no es especialmente funcional, es correcto.

Configuración de triggers

Dado que no se han especificado filtros de activación para ninguna de las tareas, el pipeline se completamente ejecutará con cada envío de commits al repositorio. Dado que este no es el comportamiento deseado en general, configuraremos filtros de activación para las tareas.

Los filtros se pueden configurar en dos formatos: only/except y rules. En resumen, only/except permite configurar filtros por triggers (merge_request, por ejemplo, configura la tarea para ejecutarse en cada creación de un merge request y en cada envío de commits a la rama que es origen del merge request) y nombres de ramas (incluyendo el uso de expresiones regulares); rules permite establecer un conjunto de condiciones y, opcionalmente, cambiar la condición de ejecución de la tarea según el éxito de tareas anteriores (when en GitLab CI/CD).

Recordemos el conjunto de requisitos: compilación y pruebas solo para merge request, empaquetado y envío a Azure DevOps — para merge requests y pushes a master, generación de documentación — para pushes a master.

Para comenzar, configuraremos una tarea de compilación de código, añadiendo una regla de activación solo para merge request:

tarea de build:
  # snip
  only:
    - merge_request

Ahora configuraremos la tarea de empaquetado para activarse en merge request y en la adición de commits a master:

paquetar y desplegar trabajo:
  # snip
  solo:
    - merge_request
    - master

Como se puede ver, es bastante simple y directo.

También se puede configurar la tarea para que se active solo si se crea un merge request con una rama objetivo o de origen específica:

  reglas:
    - si: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"

En las condiciones se pueden utilizar las variables enumeradas aquí; las reglas rules no son compatibles con las reglas only/except.

Configuración de la conservación de artefactos

Durante la ejecución de la tarea trabajo de construcción se crearán artefactos de construcción que se pueden reutilizar en tareas posteriores. Para ello, se deben añadir en la configuración de la tarea las rutas y archivos que se deben conservar y reutilizar en las siguientes tareas, en la clave artefactos:

trabajo de construcción:
  # snip
  artefactos:
    rutas:
      - path/to/build/artifacts
      - another/path
      - MyCoolLib.*/bin/Release/*

Las rutas admiten comodines, lo que sin duda facilita su designación.

Si la tarea crea artefactos, entonces cada tarea posterior podrá acceder a ellos; estarán ubicados en las mismas rutas relativas a la raíz del repositorio, desde donde se recopilaron de la tarea de origen. Además, los artefactos están disponibles para descargar en el sitio.

Ahora que tenemos el esqueleto de la configuración listo (y verificado), podemos pasar a escribir los scripts para las tareas.

Escribimos scripts

Quizás, hace mucho tiempo, en una galáxia muy, muy lejana, compilar proyectos (incluidos los de .net) desde la línea de comandos era un dolor. Sin embargo, ahora se puede compilar, probar y publicar un proyecto en 3 comandos:

dotnet build
dotnet test
dotnet pack

Por supuesto, hay algunos detalles que complican un poco los comandos.

  1. Queremos una compilación de lanzamiento, no de depuración, por lo que añadimos -c Release
  2. Al probar, queremos recopilar datos sobre la cobertura del código, por lo que será necesario habilitar el analizador de cobertura en las bibliotecas de prueba:
    1. Se debe agregar el paquete coverlet.msbuild: dotnet add package coverlet.msbuild desde la carpeta del proyecto
    2. En el comando de ejecución de pruebas añadiremos /p:CollectCoverage=true
    3. En la configuración de la tarea de pruebas agregaremos una clave para obtener los resultados de cobertura (ver más abajo)
  3. Al empaquetar el código en paquetes nuget, estableceremos el directorio de salida para los paquetes: -o .

Recopilando datos de cobertura del código

Coverlet muestra en la consola las estadísticas de ejecución después de ejecutar las pruebas:

Calculando el resultado de la cobertura...
  Generando el informe 'C:Usersxxxsourcereposmy-projectmyProject.testscoverage.json'

+-------------+--------+--------+--------+
| Módulo      | Línea  | Rama   | Método |
+-------------+--------+--------+--------+
| proyecto 1  | 83,24% | 66,66% | 92,1%  |
+-------------+--------+--------+--------+
| proyecto 2  | 87,5%  | 50%    | 100%   |
+-------------+--------+--------+--------+
| proyecto 3  | 100%   | 83,33% | 100%   |
+-------------+--------+--------+--------+

+---------+--------+--------+--------+
|         | Línea  | Rama   | Método |
+---------+--------+--------+--------+
| Total   | 84,27% | 65,76% | 92,94% |
+---------+--------+--------+--------+
| Promedio| 90,24% | 66,66% | 97,36% |
+---------+--------+--------+--------+

GitLab permite especificar una expresión regular para obtener estadísticas, que luego se pueden obtener en forma de insignia. La expresión regular se indica en la configuración de la tarea con la clave coverage; en la expresión debe haber un grupo de captura, cuyo valor será transmitido a la insignia:

trabajo de pruebas y cobertura:
  # snip
  coverage: \/|s*Totales*s*(d+[,.]d+%)\/

Aquí obtenemos estadísticas de la línea total de cobertura.

Publicamos paquetes y documentación

Ambas acciones están programadas para la última etapa del pipeline; dado que la construcción y las pruebas se han completado, podemos compartir nuestro trabajo con el mundo.

Primero, veamos la publicación en la fuente de paquetes:

  1. Si en el proyecto no hay un archivo de configuración de nuget (nuget.config), crearemos uno nuevo: dotnet new nugetconfig

    ¿Por qué? en la imagen puede estar prohibido el acceso de escritura a las configuraciones globales (de usuario y de máquina). Para evitar errores, simplemente crearemos una nueva configuración local y trabajaremos con ella.

  2. Agreguemos una nueva fuente de paquetes a la configuración local: nuget sources add -name <name> -source <url> -username <organization> -password <gitlab variable> -configfile nuget.config -StorePasswordInClearText
    1. name — nombre local de la fuente, no es crítico
    2. url — URL de la fuente de la etapa "Preparando cuentas", p. 6
    3. organization — nombre de la organización en Azure DevOps
    4. gitlab variable — nombre de la variable con el token de acceso, añadida en GitLab ("Preparando cuentas", p. 11). Por supuesto, en el formato $variableName
    5. -StorePasswordInClearText — hack para evitar el error de acceso denegado (no fui el primero en tropezar con esto)
    6. En caso de errores, puede ser útil añadir -verbosity detailed
  3. Enviamos el paquete a la fuente: nuget push -source <name> -skipduplicate -apikey <key> *.nupkg
    1. Enviamos todos los paquetes del directorio actual, por lo que *.nupkg.
    2. name — de la etapa anterior.
    3. key — cualquier cadena. En Azure DevOps, en la ventana Conectar a feed siempre se menciona como ejemplo la cadena az.
    4. -skipduplicate — al intentar enviar un paquete ya existente sin esta clave, el origen devolverá un error 409 Conflicto; con la clave, el envío será omitido.

Ahora configuraremos la creación de la documentación:

  1. Para comenzar, en el repositorio, en la rama master, inicializamos el proyecto docfx. Para ello desde la raíz debemos ejecutar el comando docfx init y en modo interactivo estableceremos los parámetros clave para la construcción de la documentación. Una descripción detallada de la configuración mínima del proyecto aquí.
    1. Al configurar, es importante especificar el directorio de salida ..public — GitLab por defecto toma el contenido de la carpeta public en la raíz del repositorio como fuente para Pages. Dado que el proyecto se ubicará en una carpeta anidada dentro del repositorio, añadimos a la ruta la salida un nivel hacia arriba.
  2. Enviar cambios a GitLab.
  3. En la configuración del pipeline añadimos la tarea pages (palabra reservada para las tareas de publicación de sitios en GitLab Pages):
    1. Script:
      1. nuget install docfx.console -version 2.51.0 — instalará docfx; la versión se indica para garantizar la corrección de las rutas de instalación del paquete.
      2. .docfx.console.2.51.0toolsdocfx.exe .docfx_projectdocfx.json — recopilamos la documentación
    2. Nodo artifacts:

pages:
  # snip
  artifacts:
    paths:
      - public

Una digresión lírica sobre docfx

Anteriormente, al configurar el proyecto, indicaba la fuente de código para la documentación como archivo de solución. La principal desventaja es que la documentación se crea también para los proyectos de prueba. En caso de que esto no sea necesario, se puede establecer tal valor en el nodo metadata.src:

{
  "metadata": [
    {
      "src": [
        {
          "src": "..\/",
          "files": [
            "**/*.csproj"
          ],
          "exclude":[
            "*.tests*\/**"
          ]
        }
      ],
      \/\/ --- snip ---
    },
    \/\/ --- snip ---
  ],
  \/\/ --- snip ---
}

  1. metadata.src.src: "..\/" — subimos un nivel en relación a la ubicación docfx.json, dado que en los patrones no funciona la búsqueda hacia arriba en el árbol de directorios.
  2. metadata.src.files: ["**/*.csproj"] — patrón global, recopilamos todos los proyectos C# de todos los directorios.
  3. metadata.src.exclude: ["*.tests*\/**"] — patrón global, excluimos todo de las carpetas con .tests en el nombre

Resultado intermedio

Esta configuración simple se puede elaborar en literalmente media hora y un par de tazas de café, que permitirá comprobar en cada solicitud de fusión y envío a master que el código se compila y las pruebas pasan, recopilar un nuevo paquete, actualizar la documentación y alegrar la vista con bonitos distintivos en el README del proyecto.

El archivo final .gitlab-ci.yml

imagen: mcr.microsoft.com/dotnet/core/sdk:3.1

before_script:
  - $PSVersionTable.PSVersion
  - dotnet --version
  - nuget help | select-string Version

stages:
  - build
  - test
  - deploy

build job:
  stage: build
  script:
    - dotnet build -c Release
  tags:
    - windows
  only:
    - merge_requests
    - master
  artifacts:
    paths:
      - your/path/to/binaries

test and cover job:
  stage: test
  tags:
    - windows
  script:
    - dotnet test -c Release /p:CollectCoverage=true
  coverage: /|s*Totals*|s*(d+[,.]d+%) /
  only:
    - merge_requests
    - master

pack and deploy job:
  stage: deploy
  tags:
    - windows
  script:
    - dotnet pack -c Release -o .
    - dotnet new nugetconfig
    - nuget sources add -name feedName -source https://pkgs.dev.azure.com/your-organization/_packaging/your-feed/nuget/v3/index.json -username your-organization -password $nugetFeedToken -configfile nuget.config -StorePasswordInClearText
    - nuget push -source feedName -skipduplicate -apikey az *.nupkg
  only:
    - master

pages:
  tags:
    - windows
  stage: deploy
  script:
    - nuget install docfx.console -version 2.51.0
    - $env:path = "$env:path;$($(get-location).Path)"
    - .docfx.console.2.51.0toolsdocfx.exe .docfxdocfx.json
  artifacts:
    paths:
      - public
  only:
    - master

Por cierto, sobre las insignias

¡Todo esto fue por ellas!

Las insignias con los estados del pipeline y la cobertura del código están disponibles en GitLab en la configuración de CI/CD en el bloque de Gtntral pipelines:

Guía de CI/CD en GitLab para (casi) principiantes absolutos.

La insignia con enlace a la documentación la creé en la plataforma Shields.io — aquí todo es bastante sencillo, puedes crear tu propia insignia y obtenerla a través de una solicitud.

![Ejemplo de Shields.io](https://img.shields.io/badge/custom-badge-blue)

Guía de CI/CD en GitLab para (casi) principiantes absolutos.

Azure DevOps Artifacts también permite crear insignias para paquetes indicando la versión actual. Para esto, en la fuente del sitio de Azure DevOps, debes hacer clic en Crear insignia en el paquete seleccionado y copiar el markdown:

Guía de CI/CD en GitLab para (casi) principiantes absolutos.

Guía de CI/CD en GitLab para (casi) principiantes absolutos.

Añadiendo un toque estético

Destacamos los fragmentos comunes de la configuración

Mientras escribía la configuración y buscaba en la documentación, me encontré con una interesante característica de YAML: la reutilización de fragmentos.

Como se puede ver en la configuración de las tareas, todas requieren la presencia de la etiqueta windows en el runner, y se activan al enviar a master/creando una solicitud de fusión (excepto la documentación). Agreguemos esto al fragmento que reutilizaremos:

.common_tags: &common_tags
  tags:
    - windows
.common_only: &common_only
  only:
    - merge_requests
    - master

Y ahora en la descripción de la tarea podemos insertar el fragmento previamente declarado:

build job:
  <<: *common_tags
  <<: *common_only

Los nombres de los fragmentos deben comenzar con un punto para no ser interpretados como tareas.

Versionado de paquetes

Al crear un paquete, el compilador verifica las claves de la línea de comandos y, si no están presentes, los archivos de proyecto; al encontrar el nodo Version, toma su valor como la versión del paquete que se está construyendo. Por lo tanto, para construir un paquete con una nueva versión, es necesario actualizarla en el archivo del proyecto o pasarla como argumento en la línea de comandos.

Agreguemos otro deseo: que los dos últimos números en la versión sean el año y la fecha de la construcción del paquete, y añadamos versiones previas. Se pueden añadir estos datos al archivo del proyecto y verificarlos antes de cada envío, claro, pero también se puede hacer en la canalización, construyendo la versión del paquete a partir del contexto y pasándola como argumento de la línea de comandos.

Convenimos que si en el mensaje de confirmación hay una línea del tipo release (v.\/ver.\/version) <número de versión> (rev.\/revisión <revisión>)?, tomaremos la versión del paquete de esta línea, le añadiremos la fecha actual y la pasaremos como argumento al comando dotnet pack. Si no hay línea, simplemente no construiremos el paquete.

Esta tarea la resuelve el siguiente script:

# регулярное выражение для поиска строки с версией
$rx = "releases+(v.?|ver.?|version)s*(?<maj>d+)(?<min>.d+)?(?<rel>.d+)?s*((rev.?|revision)?s+(?<rev>[a-zA-Z0-9-_]+))?"
# ищем строку в сообщении коммита, передаваемом в одной из предопределяемых GitLab'ом переменных
$found = $env:CI_COMMIT_MESSAGE -match $rx
# совпадений нет - выходим
if (!$found) { Write-Output "no release info found, aborting"; exit }
# извлекаем мажорную и минорную версии
$maj = $matches['maj']
$min = $matches['min']
# если строка содержит номер релиза - используем его, иначе - текущий год
if ($matches.ContainsKey('rel')) { $rel = $matches['rel'] } else { $rel = ".$(get-date -format "yyyy")" }
# в качестве номера сборки - текущие месяц и день
$bld = $(get-date -format "MMdd")
# если есть данные по пререлизной версии - включаем их в версию
if ($matches.ContainsKey('rev')) { $rev = "-$($matches['rev'])" } else { $rev = '' }
# собираем единую строку версии
$version = "$maj$min$rel.$bld$rev"
# собираем пакеты
dotnet pack -c Release -o . /p:Version=$version

Agregamos el script a la tarea pack and deploy job y observamos la construcción de paquetes estrictamente en presencia de la línea especificada en el mensaje de confirmación.

Total

Después de gastar aproximadamente media hora a una hora en la redacción de la configuración, depuración en powershell local y, quizás, un par de intentos fallidos, conseguimos una configuración sencilla para automatizar tareas rutinarias.

Por supuesto, GitLab CI/CD es mucho más amplio y complejo de lo que puede parecer después de leer esta guía - no es así en absoluto. Allí incluso hay Auto DevOps, que permite

detecta automáticamente, construye, prueba, despliega y monitorea tus aplicaciones

Ahora, en los planes está configurar la canalización para desplegar aplicaciones en Azure, utilizando Pulumi y la detección automática del entorno objetivo, lo que se cubrirá en el próximo artículo.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster