Una guía de CI/CD en GitLab para el (casi) principiante absoluto
O cómo obtener hermosas insignias para su proyecto en una noche de codificación fácil
Probablemente, todos los desarrolladores que tienen al menos un proyecto favorito en algún momento sienten curiosidad por las hermosas insignias con estados, cobertura de código, versiones de paquetes en nuget... Y esta necesidad me llevó a escribir este artículo. En preparación para escribirlo, obtuve esta belleza en uno de mis proyectos:
Este artículo lo guiará a través de la configuración básica de integración y entrega continuas para un proyecto de biblioteca de clases de .Net Core en GitLab, la publicación de documentación en GitLab Pages y el envío de paquetes creados a una fuente privada en Azure DevOps.
VS Code se utilizó como entorno de desarrollo con la extensión Flujo de trabajo de GitLab (para validar el archivo de configuración directamente desde el entorno de desarrollo).
Breve introducción
CD: ¿es cuando acaba de presionar y todo ya ha caído sobre el cliente?
¿Qué es CI / CD y por qué lo necesita? Puede buscarlo fácilmente en Google. Encuentre documentación completa sobre la configuración de canalizaciones en GitLab también fácil. Aquí describiré brevemente y, si es posible, sin fallas, el proceso del sistema a vista de pájaro:
el desarrollador envía una confirmación al repositorio, crea una solicitud de fusión a través del sitio, o de alguna otra manera, explícita o implícitamente inicia la canalización,
todas las tareas se seleccionan de la configuración, cuyas condiciones permiten que se inicien en el contexto dado,
Las tareas se organizan según sus etapas,
las etapas se ejecutan a su vez, es decir, paralelo a todas las tareas de esta etapa se completan,
si la etapa falla (es decir, al menos una de las tareas de la etapa falla), la canalización se detiene (casi siempre),
si todas las etapas se completan con éxito, la tubería se considera exitosa.
Por lo tanto, tenemos:
canalización: un conjunto de tareas organizadas en etapas en las que puede compilar, probar, empaquetar código, implementar una compilación terminada en un servicio en la nube, etc.
escenario (escenario) — unidad de organización de tubería, contiene 1+ tarea,
tarea (trabajo) es una unidad de trabajo en la canalización. Consiste en un script (obligatorio), condiciones de lanzamiento, configuraciones para publicar/almacenar en caché artefactos y mucho más.
En consecuencia, la tarea al configurar CI/CD se reduce a crear un conjunto de tareas que implementen todas las acciones necesarias para construir, probar y publicar código y artefactos.
Antes de empezar: ¿por qué?
¿Por qué Gitlab?
Porque cuando se hizo necesario crear repositorios privados para proyectos favoritos, se les pagaba en GitHub y yo era codicioso. Los repositorios se han vuelto gratuitos, pero hasta ahora esto no es motivo suficiente para cambiarme a GitHub.
¿Por qué no canalizaciones de Azure DevOps?
Porque allí la configuración es elemental: ni siquiera se requiere conocimiento de la línea de comando. Integración con proveedores de git externos: en un par de clics, importación de claves SSH para enviar confirmaciones al repositorio; también, la canalización se configura fácilmente incluso sin una plantilla.
Posición inicial: lo que tienes y lo que quieres
Tenemos:
repositorio en GitLab.
Queremos:
montaje automático y prueba para cada solicitud de fusión,
construyendo paquetes para cada solicitud de fusión y empujando al maestro, siempre que haya una cierta línea en el mensaje de confirmación,
enviar paquetes creados a una fuente privada en Azure DevOps,
montaje de documentación y publicación en GitLab Pages,
insignias! 11
Los requisitos descritos caen orgánicamente en el siguiente modelo de tubería:
Etapa 1 - Montaje
Recopilamos el código, publicamos los archivos de salida como artefactos
Etapa 2 - prueba
Obtenemos artefactos de la etapa de construcción, ejecutamos pruebas, recopilamos datos de cobertura de código
Etapa 3 - Enviar
Tarea 1: compilar el paquete nuget y enviarlo a Azure DevOps
Tarea 2: recopilamos el sitio de xmldoc en el código fuente y lo publicamos en GitLab Pages
Cuando haga clic en el botón Crear, se creará el proyecto y será redirigido a su página. En esta página, puede deshabilitar funciones innecesarias yendo a la configuración del proyecto (enlace inferior en la lista de la izquierda -> Descripción general -> Bloque de Azure DevOps Services)
Vaya a Atrifacts, haga clic en Crear feed
Ingrese el nombre de la fuente
Elige visibilidad
Desmarcar Incluir paquetes de fuentes públicas comunes, para que la fuente no se convierta en un clon nuget de volcado
Haga clic en Conectar a fuente, seleccione Visual Studio, copie Fuente desde el bloque Configuración de máquina
Vaya a la configuración de la cuenta, seleccione Token de acceso personal
Crear un nuevo token de acceso
Nombre - arbitrario
Organización - actual
Válido por un máximo de 1 año
Alcance: empaquetado/lectura y escritura
Copie el token creado - después de que se cierre la ventana modal, el valor no estará disponible
Vaya a la configuración del repositorio en GitLab, seleccione la configuración de CI / CD
Expanda el bloque Variables, agregue uno nuevo
Nombre: cualquiera sin espacios (estará disponible en el shell de comandos)
Valor - token de acceso del párrafo 9
Seleccionar variable Máscara
Esto completa la configuración previa.
Preparación del marco de configuración
Por defecto, la configuración de CI/CD en GitLab usa el archivo .gitlab-ci.yml desde la raíz del repositorio. Puede establecer una ruta arbitraria a este archivo en la configuración del repositorio, pero en este caso no es necesario.
Como puede ver en la extensión, el archivo contiene una configuración en el formato YAML. La documentación detalla qué claves pueden estar contenidas en el nivel superior de la configuración y en cada uno de los niveles anidados.
Primero, agreguemos un enlace a la imagen de la ventana acoplable en el archivo de configuración, en el que se realizarán las tareas. Para esto encontramos Página de imágenes de .Net Core en Docker Hub. En GitHub hay una guía detallada sobre qué imagen elegir para diferentes tareas. Una imagen con .Net Core 3.1 es adecuada para que la construyamos, así que no dude en agregar la primera línea a la configuración
image: mcr.microsoft.com/dotnet/core/sdk:3.1
Ahora, cuando se inicie la canalización desde el repositorio de 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 agregar escenario's. Por defecto, GitLab define 5 etapas:
.pre - realizado hasta todas las etapas,
.post - realizado después de todas las etapas,
build - primero después .pre escenario,
test - segunda fase,
deploy - la tercera etapa.
Sin embargo, nada le impide declararlos explícitamente. El orden en que se enumeran los pasos afecta el orden en que se realizan. Para completar, agreguemos a la configuración:
stages:
- build
- test
- deploy
Para la depuración, tiene sentido obtener información sobre el entorno en el que se ejecutan las tareas. Agreguemos un conjunto global de comandos que se ejecutarán antes de cada tarea con before_script:
before_script:
- $PSVersionTable.PSVersion
- dotnet --version
- nuget help | select-string Version
Queda por agregar al menos una tarea para que cuando se envíen los commits se inicie el pipeline. Por ahora, agreguemos una tarea vacía para demostrar:
dummy job:
script:
- echo ok
Comenzamos la validación, recibimos un mensaje de que todo está bien, nos comprometemos, presionamos, miramos los resultados en el sitio ... Y recibimos un error de secuencia de comandos: bash: .PSVersion: command not found. wtf?
Todo es lógico: de forma predeterminada, los corredores (responsables de ejecutar scripts de tareas y proporcionados por GitLab) usan bash para ejecutar comandos. Puede solucionar esto especificando explícitamente en la descripción de la tarea qué etiquetas debe tener el ejecutor de la canalización en ejecución:
dummy job on windows:
script:
- echo ok
tags:
- windows
¡Excelente! La canalización ahora se está ejecutando.
Un lector atento, después de haber repetido los pasos indicados, notará que la tarea se completó en la etapa test, aunque no especificamos el escenario. Como puedes adivinar test es el paso predeterminado.
Sigamos creando el esqueleto de configuración agregando todas las tareas descritas anteriormente:
build job:
script:
- echo "building..."
tags:
- windows
stage: build
test and cover job:
script:
- echo "running tests and coverage analysis..."
tags:
- windows
stage: test
pack and deploy job:
script:
- echo "packing and pushing to nuget..."
tags:
- windows
stage: deploy
pages:
script:
- echo "creating docs..."
tags:
- windows
stage: deploy
Obtuvimos una tubería no particularmente funcional, pero sin embargo correcta.
Configuración de disparadores
Debido al hecho de que no se especifican filtros desencadenantes para ninguna de las tareas, la canalización completamente ejecutarse cada vez que se envía una confirmación al repositorio. Dado que este no es el comportamiento deseado en general, configuraremos filtros de activación para tareas.
Los filtros se pueden configurar en dos formatos: solo/excepto и reglas. Brevemente, only/except permite configurar filtros por disparadores (merge_request, por ejemplo, establece la tarea que se ejecutará cada vez que se cree una solicitud de extracción y cada vez que se envíen confirmaciones a la rama que es el origen de la solicitud de fusión) y los nombres de las ramas (incluido el uso de expresiones regulares); rules le permite personalizar un conjunto de condiciones y, opcionalmente, cambiar la condición de ejecución de la tarea según el éxito de las tareas anteriores (when en GitLab CI/CD).
Recordemos un conjunto de requisitos: ensamblaje y prueba solo para solicitudes de combinación, empaquetado y envío a Azure DevOps, para solicitudes de combinación y envíos al maestro, generación de documentación, para envíos al maestro.
Primero, configuremos la tarea de compilación de código agregando una regla que se active solo en la solicitud de combinación:
build job:
# snip
only:
- merge_request
Ahora configuremos la tarea de empaquetado para que se active en la solicitud de fusión y agregue confirmaciones al maestro:
Bajo condiciones, puede usar variables enumeradas aquí; normas rules incompatible con las normas only/except.
Configuración del guardado de artefactos
durante una tarea build job tendremos artefactos de construcción que se pueden reutilizar en tareas posteriores. Para hacer esto, debe agregar las rutas a la configuración de la tarea, los archivos a lo largo de los cuales deberá guardar y reutilizar en las siguientes tareas, a la clave artifacts:
Las rutas admiten comodines, lo que definitivamente las hace más fáciles de configurar.
Si una tarea crea artefactos, cada tarea subsiguiente podrá acceder a ellos; se ubicarán a lo largo de las mismas rutas en relación con la raíz del repositorio que se recopilaron de la tarea original. Los artefactos también están disponibles para descargar en el sitio.
Ahora que tenemos un marco de configuración listo (y probado), podemos proceder a escribir scripts para tareas.
Escribimos guiones
Tal vez, alguna vez, en una galaxia muy, muy lejana, crear proyectos (incluidos los de .net) desde la línea de comandos era una molestia. Ahora puede compilar, probar y publicar el proyecto en 3 equipos:
dotnet build
dotnet test
dotnet pack
Naturalmente, hay algunos matices por los que complicaremos un poco los comandos.
Queremos una compilación de lanzamiento, no una compilación de depuración, por lo que agregamos a cada comando -c Release
Al realizar pruebas, queremos recopilar datos de cobertura de código, por lo que debemos incluir un analizador de cobertura en las bibliotecas de prueba:
Agregar el paquete a todas las bibliotecas de prueba coverlet.msbuild: dotnet add package coverlet.msbuild de la carpeta del proyecto
Agregar al comando de ejecución de prueba /p:CollectCoverage=true
Agregue una clave a la configuración de la tarea de prueba para obtener resultados de cobertura (consulte a continuación)
Al empaquetar el código en paquetes nuget, configure el directorio de salida para los paquetes: -o .
Recopilación de datos de cobertura de código
Después de ejecutar las pruebas, Coverlet imprime estadísticas de ejecución en la consola:
GitLab le permite especificar una expresión regular para obtener estadísticas, que luego se pueden obtener en forma de insignia. La expresión regular se especifica en la configuración de la tarea con la clave coverage; la expresión debe contener un grupo de captura, cuyo valor se pasará a la insignia:
test and cover job:
# snip
coverage: /|s*Totals*|s*(d+[,.]d+%)/
Aquí obtenemos estadísticas de una línea con cobertura total de línea.
Publicar paquetes y documentación.
Ambas acciones están programadas para la última etapa de la tubería: ya que el montaje y las pruebas han pasado, podemos compartir nuestros desarrollos con el mundo.
Primero, considere publicar en la fuente del paquete:
Si el proyecto no tiene un archivo de configuración nuget (nuget.config), crea uno nuevo: dotnet new nugetconfig
Para qué: es posible que la imagen no tenga acceso de escritura a las configuraciones globales (usuario y máquina). Para no detectar errores, simplemente creamos una nueva configuración local y trabajamos con ella.
Agreguemos una nueva fuente de paquete a la configuración local: nuget sources add -name <name> -source <url> -username <organization> -password <gitlab variable> -configfile nuget.config -StorePasswordInClearText
name - nombre de fuente local, no crítico
url - URL de la fuente de la etapa "Preparación de cuentas", página 6
organization - nombre de la organización en Azure DevOps
gitlab variable - el nombre de la variable con el token de acceso agregado a GitLab ("Preparación de cuentas", p. 11). Naturalmente, en el formato $variableName
En caso de errores, puede ser útil agregar -verbosity detailed
Envío del paquete a la fuente: nuget push -source <name> -skipduplicate -apikey <key> *.nupkg
Enviamos todos los paquetes desde el directorio actual, por lo que *.nupkg.
name - del paso anterior.
key - cualquier línea. En Azure DevOps, en la ventana Conectar a fuente, el ejemplo siempre es la línea az.
-skipduplicate - al intentar enviar un paquete ya existente sin esta clave, la fuente devolverá un error 409 Conflict; con la tecla se saltará el envío.
Ahora configuremos la creación de documentación:
Primero, en el repositorio, en la rama maestra, inicializamos el proyecto docfx. Para hacer esto, ejecute el comando desde la raíz docfx init y establecer de forma interactiva los parámetros clave para la documentación de construcción. Descripción detallada de la configuración mínima del proyecto aquí.
Al configurar, es importante especificar el directorio de salida ..public - GitLab por defecto toma el contenido de la carpeta pública en la raíz del repositorio como fuente para Pages. Porque el proyecto se ubicará en una carpeta anidada en el repositorio: agregue una salida al nivel superior en la ruta.
Empujemos los cambios a GitLab.
Agregar una tarea a la configuración de canalización pages (palabra reservada para tareas de publicación de sitios en GitLab Pages):
script:
nuget install docfx.console -version 2.51.0 - instalar docfx; la versión se especifica para garantizar que las rutas de instalación del paquete sean correctas.
Anteriormente, al configurar un proyecto, especificaba el código fuente de la documentación como un archivo de solución. La principal desventaja es que también se crea documentación para proyectos de prueba. En caso de que esto no sea necesario, puede establecer este valor en el nodo metadata.src:
metadata.src.src: "../" - subimos un nivel en relación con la ubicación docfx.json, porque en los patrones, la búsqueda en el árbol de directorios no funciona.
metadata.src.files: ["**/*.csproj"] - un patrón global, recopilamos todos los proyectos C # de todos los directorios.
metadata.src.exclude: ["*.tests*/**"] - patrón global, excluye todo de las carpetas con .tests en el nombre
Subtotal
Una configuración tan simple se puede crear en solo media hora y un par de tazas de café, lo que le permitirá verificar que el código está construido y las pruebas pasan, construir un nuevo paquete, actualizar la documentación y complacer a la vista con hermosas insignias en el LÉAME del proyecto con cada solicitud de fusión y envío al maestro.
.gitlab-ci.yml final
image: 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
Hablando de insignias
¡Por ellos, después de todo, todo comenzó!
Las insignias con estados de canalización y cobertura de código están disponibles en GitLab en la configuración de CI/CD en el bloque de canalizaciones Gtntral:
Creé una insignia con un enlace a la documentación en la plataforma escudos.io - Todo es bastante sencillo allí, puede crear su propia insignia y recibirla mediante una solicitud.
![Пример с Shields.io](https://img.shields.io/badge/custom-badge-blue)
Azure DevOps Artifacts también le permite crear insignias para paquetes con la versión más reciente. Para hacer esto, en la fuente en el sitio de Azure DevOps, debe hacer clic en Crear insignia para el paquete seleccionado y copiar el marcado de descuento:
Agregando belleza
Resaltar fragmentos de configuración comunes
Mientras escribía la configuración y buscaba en la documentación, encontré una característica interesante de YAML: la reutilización de fragmentos.
Como puede ver en la configuración de tareas, todas requieren la etiqueta windows en el corredor, y se activan cuando se envía una solicitud de fusión al maestro/creada (excepto la documentación). Agreguemos esto al fragmento que reutilizaremos:
Y ahora podemos insertar el fragmento declarado anteriormente en la descripción de la tarea:
build job:
<<: *common_tags
<<: *common_only
Los nombres de los fragmentos deben comenzar con un punto, para que no se interpreten como una tarea.
Versiones de paquetes
Al crear un paquete, el compilador verifica los interruptores de la línea de comando y, en su ausencia, los archivos del proyecto; cuando encuentra un nodo Version, toma su valor como la versión del paquete que se está construyendo. Resulta que para crear un paquete con una nueva versión, debe actualizarlo en el archivo del proyecto o pasarlo como un argumento de línea de comando.
Agreguemos una lista de deseos más: deje que los dos números menores en la versión sean el año y la fecha de compilación del paquete, y agregue versiones preliminares. Por supuesto, puede agregar estos datos al archivo del proyecto y verificar antes de cada envío, pero también puede hacerlo en la tubería, recopilando la versión del paquete del contexto y pasándola a través del argumento de la línea de comando.
Acordemos que si el mensaje de confirmación contiene una línea como release (v./ver./version) <version number> (rev./revision <revision>)?, luego tomaremos la versión del paquete de esta línea, la complementaremos con la fecha actual y la pasaremos como argumento al comando dotnet pack. En ausencia de una línea, simplemente no recogeremos el paquete.
El siguiente script resuelve este problema:
# регулярное выражение для поиска строки с версией
$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
Adición de un script a una tarea pack and deploy job y observar el ensamblaje de paquetes estrictamente en presencia de una cadena determinada en el mensaje de confirmación.
En total
Después de pasar aproximadamente media hora o una hora escribiendo la configuración, depurando el powershell local y, posiblemente, un par de lanzamientos fallidos, obtuvimos una configuración simple para automatizar tareas de rutina.
Por supuesto, GitLab CI/CD es mucho más extenso y multifacético de lo que parece después de leer esta guía. esta completamente equivocado. allí incluso Auto DevOps espermitiendo
detectar, construir, probar, implementar y monitorear automáticamente sus aplicaciones
Ahora los planes son configurar una canalización para implementar aplicaciones en Azure, usando Pulumi y determinando automáticamente el entorno de destino, que se tratará en el siguiente artículo.