Go tools & GitLab: cómo hacer una integración continua como un jefe

Foto de Todd Quackenbush en Unsplash

En Pantomath, utilizamos GitLab para todo nuestro trabajo de desarrollo. El propósito de este documento no es presentar GitLab y todas sus características, sino presentar cómo utilizamos estas herramientas para facilitar nuestras vidas.

Entonces, ¿qué es todo esto? Para automatizar todo lo relacionado con su proyecto de desarrollo y permitirle centrarse en su código. Cubriremos la pelusa, las pruebas unitarias, la carrera de datos, el desinfectante de memoria, la cobertura del código y la compilación.

Todo el código que se muestra en esta publicación está disponible en https://gitlab.com/pantomath-io/demo-tools. Así que siéntase libre de obtener el repositorio y use las etiquetas para navegar en él. El repositorio debe colocarse en la carpeta src de su $ GOPATH:

$ go get -v -d gitlab.com/pantomath-io/demo-tools
$ cd $ GOPATH / src / gitlab.com / pantomath-io / demo-tools

Ir herramientas

Afortunadamente, Go viene con muchas herramientas útiles para construir, probar y verificar su código. De hecho, todo está ahí. Solo agregaremos herramientas adicionales para unirlas. Pero antes de ir allí, debemos tomarlos uno por uno y ver qué hacen.

Lista de paquetes

Su proyecto go es una colección de paquetes, como se describe en el documento oficial. La mayoría de las siguientes herramientas se alimentarán con estos paquetes y, por lo tanto, el primer comando que necesitamos es una forma de enumerar los paquetes. Con suerte, ve cubriendo nuestras espaldas con el subcomando de la lista (lee el excelente manual y esta excelente publicación de Dave Cheney):

$ ir a la lista. / ...

Tenga en cuenta que queremos evitar aplicar nuestras herramientas en recursos externos y restringirlo a nuestro código. Entonces necesitamos deshacernos de los directorios de proveedores:

$ ir a la lista. / ... | grep -v / vendedor /

Hilas

Esta es la primera herramienta que usamos en el código: el linter. Su función es asegurarse de que el código respeta el estilo del código. Esto puede sonar como una herramienta opcional, o al menos una "agradable", pero realmente ayuda a mantener un estilo consistente sobre su proyecto.

Este linter no es parte de go per se, por lo que debe tomarlo e instalarlo a mano (consulte el documento oficial).

El uso es bastante simple: solo lo ejecuta en los paquetes de su código (también puede señalar los archivos .go):

$ golint -set_exit_status $ (ir a la lista. / ... | grep -v / vendor /)

Tenga en cuenta la opción -set_exit_status. De manera predeterminada, golint solo imprime los problemas de estilo y los devuelve (con un código de retorno 0), por lo que el CI nunca considera que algo salió mal. Si especifica -set_exit_status, el código de retorno de golint será diferente de 0 si se encuentra algún problema de estilo.

Prueba de unidad

Estas son las pruebas más comunes que puede ejecutar en su código. Para cada archivo .go, necesitamos tener un archivo _test.go asociado que contenga las pruebas unitarias. Puede ejecutar las pruebas para todos los paquetes con el siguiente comando:

$ go test -short $ (go list. / ... | grep -v / vendor /)

Carrera de datos

Esto suele ser un tema difícil de cubrir, pero la herramienta go lo tiene por defecto (pero solo está disponible en linux / amd64, freebsd / amd64, darwin / amd64 y windows / amd64). Para obtener más información sobre la carrera de datos, consulte este artículo. Mientras tanto, aquí está cómo ejecutarlo:

$ go test -race -short $ (go list. / ... | grep -v / vendor /)

Desinfectante de memoria

Clang tiene un buen detector para lecturas no inicializadas llamado MemorySanitizer. La herramienta go test es lo suficientemente amable como para interactuar con este módulo de Clang (tan pronto como esté en el host linux / amd64 y use una versión reciente de Clang / LLVM (> = 3.8.0). Este comando es cómo ejecutarlo:

$ go test -msan -short $ (go list. / ... | grep -v / vendor /)

Cobertura de código

También es necesario evaluar el estado de su código y ver qué parte del código se encuentra bajo pruebas unitarias y qué parte no. Rob Pike escribió una publicación completa sobre ese mismo tema.

Para calcular la relación de cobertura del código, necesitamos ejecutar el siguiente script:

$ PKG_LIST = $ (ir a la lista. / ... | grep -v / vendor /)
$ por paquete en $ {PKG_LIST}; hacer
    vaya a prueba -covermode = count -coverprofile "cover / $ {package ## * /}. cov" "$ package";
hecho
$ tail -q -n +2 cover / *. cov >> cover / coverage.cov
$ go tool cover -func = cover / coverage.cov

Si queremos obtener el informe de cobertura en formato HTML, debemos agregar el siguiente comando:

$ go tool cover -html = cover / coverage.cov -o coverage.html

Construir

Por último, pero no menos importante, una vez que el código se haya probado completamente, es posible que queramos compilarlo para que podamos crear un binario que funcione.

$ go build -i -v gitlab.com/pantomath-io/demo-tools

Makefile

etiqueta git: init-makefile

Foto de Matt Artz en Unsplash

Ahora tenemos todas las herramientas que podemos usar en el contexto de la Integración continua, podemos envolverlas todas en un Makefile y tener una forma consistente de llamarlas.

El propósito de este documento no es presentar make, pero puede consultar la documentación oficial para obtener más información al respecto.

PROJECT_NAME: = "demo-tools"
PKG: = "gitlab.com/pantomath-io/$(PROJECT_NAME)"
PKG_LIST: = $ (shell go list $ {PKG} / ... | grep -v / vendor /)
GO_FILES: = $ (shell find. -Name '* .go' | grep -v / vendor / | grep -v _test.go)
.PHONY: todas las dep depuran la cobertura de prueba limpia coverhtml pelusa
todos: construir
pelusa: ## Lint los archivos
 @golint -set_exit_status $ {PKG_LIST}
prueba: ## Ejecutar pruebas unitarias
 @go test -short $ {PKG_LIST}
carrera: dep ## Ejecutar detector de carrera de datos
 @go test -race -short $ {PKG_LIST}
msan: dep ## Ejecuta el desinfectante de memoria
 @go test -msan -short $ {PKG_LIST}
cobertura: ## Generar informe de cobertura de código global
 ./tools/coverage.sh;
coverhtml: ## Generar informe de cobertura de código global en HTML
 ./tools/coverage.sh html;
dep: ## Obtiene las dependencias
 @go get -v -d. / ...
build: dep ## Construye el archivo binario
 @go build -i -v $ (PKG)
limpio: ## Eliminar compilación anterior
 @rm -f $ (PROYECTO_NOMBRE)
ayuda: ## Mostrar esta pantalla de ayuda
 @grep -h -E '^ [a-zA-Z _-] +:. *? ##. * $$' $ (MAKEFILE_LIST) | awk 'BEGIN {FS = ":. *? ##"}; {printf "\ 033 [36m% -30s \ 033 [0m% s \ n", $$ 1, $$ 2} '

¿Qué tenemos ahora? Un objetivo para cualquier herramienta presentada anteriormente y 3 objetivos más para:

  • instalación de dependencias (dep);
  • mantenimiento del proyecto (limpio);
  • ayuda agradable y brillante (ayuda).

Tenga en cuenta que también tuvimos que crear un script para el trabajo de cobertura del código. Esto se debe a que implementar bucles sobre archivos en un Makefile es una molestia. Entonces, el trabajo se realiza en un script bash, y el Makefile solo activa este script.

Puede probar el Makefile con los siguientes comandos:

$ hacer ayuda
$ hacer pelusa
$ hacer cobertura

Integración continua

etiqueta git: init-ci

Foto de Max Panamá en Unsplash

Ahora que las herramientas están en su lugar, y podemos ejecutar varias pruebas en nuestro código, nos gustaría automatizarlas en su repositorio. Afortunadamente, GitLab ofrece canalizaciones de CI solo por esto. Y la configuración para esto es bastante sencilla: todo lo que crea es un archivo .gitlab-ci.yml en la raíz del repositorio.

La documentación completa en este archivo Yaml presenta todas las opciones, pero puede comenzar con este .gitlab-ci.yml:

imagen: golang: 1.9
cache:
  caminos:
    - / apt-cache
    - /go/src/github.com
    - /go/src/golang.org
    - /go/src/google.golang.org
    - /go/src/gopkg.in
etapas:
  - prueba
  - construir
before_script:
  - mkdir -p /go/src/gitlab.com/pantomath-io / go / src / _ / builds
  - cp -r $ CI_PROJECT_DIR /go/src/gitlab.com/pantomath-io/pantomath
  - ln -s /go/src/gitlab.com/pantomath-io / go / src / _ / builds / pantomath-io
  - hacer dep
unit_tests:
  etapa: prueba
  guión:
    - hacer prueba
race_detector:
  etapa: prueba
  guión:
    - hacer carrera
Memory_sanitizer:
  etapa: prueba
  guión:
    - hacer msan
cobertura de código:
  etapa: prueba
  guión:
    - hacer cobertura
code_coverage_report:
  etapa: prueba
  guión:
    - hacer coverhtml
  solamente:
  - maestro
lint_code:
  etapa: prueba
  guión:
    - hacer pelusa
construir:
  etapa: construir
  guión:
    - hacer

Si desglosa el archivo, aquí hay algunas explicaciones sobre su contenido:

  • Lo primero es elegir qué imagen de Docker se usará para ejecutar el CI. Dirígete a Docker Hub para elegir la imagen correcta para tu proyecto.
  • Luego, especifica algunas carpetas de esta imagen para almacenar en caché. El objetivo aquí es evitar descargar el mismo contenido varias veces. Una vez que se completa un trabajo, las rutas enumeradas se archivarán y el próximo trabajo utilizará el mismo archivo.
  • Usted define las diferentes etapas que agruparán sus trabajos. En nuestro caso, tenemos dos etapas (para ser procesadas en ese orden): prueba y compilación. Podríamos tener otras etapas, como la implementación.
  • La sección before_script define los comandos que se ejecutarán en el contenedor Docker justo antes de que el trabajo se haya realizado. En nuestro contexto, los comandos simplemente copian o vinculan el repositorio desplegado en $ GOPATH e instalan dependencias.
  • Luego vienen los trabajos reales, utilizando los objetivos Makefile. Tenga en cuenta el caso especial de code_coverage_report donde la ejecución está restringida a la rama maestra (por ejemplo, no queremos actualizar el informe de cobertura de código de las ramas de características).

A medida que confirmamos / enviamos el archivo .gitlab-ci.yml en el repositorio, el CI se activa automáticamente. Y la tubería falla. ¿Cómo?

El trabajo lint_code falla porque no puede encontrar el binario golint:

$ hacer pelusa
make: golint: Comando no encontrado
Makefile: 11: la receta para el objetivo 'pelusa' falló
make: *** [lint] Error 127

Por lo tanto, actualice su Makefile para instalar golint como parte del objetivo de dep.

El trabajo memory_sanitizer falla porque gcc se queja:

$ make msan
# runtime / cgo
gcc: error: argumento no reconocido para -fsanitize = opción: 'memoria'
Makefile: 20: la receta para el objetivo 'msan' falló
make: *** [msan] Error 2

Pero recuerde que necesitamos usar Clang / LLVM> = 3.8.0 para disfrutar de la opción -msan en el comando go test.

Tenemos 2 opciones aquí:

  • o configuramos Clang en el trabajo (usando before_script);
  • o usamos una imagen Docker con Clang instalado por defecto.

La primera opción es buena, pero eso implica hacer esta configuración para cada trabajo. Esto va a ser tan largo que deberíamos hacerlo de una vez por todas. Por lo tanto, preferimos la segunda opción, que es una buena forma de jugar con GitLab Registry.

etiqueta git: use-own-docker

Necesitamos crear un Dockerfile para el contenedor (como de costumbre: lea la documentación oficial para obtener más opciones al respecto):

# Imagen base: https://hub.docker.com/_/golang/
DE golang: 1.9
MANTENEDOR Julien Andrieux 
# Instalar golint
ENV GOPATH / go
ENV PATH $ {GOPATH} / bin: $ PATH
EJECUTAR vaya a obtener -u github.com/golang/lint/golint
# Agregar clave apt para el repositorio LLVM
EJECUTAR wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
# Añadir repositorio LLVM apt
EJECUTAR echo "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-5.0 main" | tee -a /etc/apt/sources.list
# Instalar clang desde el repositorio LLVM
EJECUTAR apt-get update && apt-get install -y --no-install-recomendamos \
    clang-5.0 \
    && apt-get clean \
    && rm -rf / var / lib / apt / lists / * / tmp / * / var / tmp / *
# Establecer Clang como CC predeterminado
ENV set_clang /etc/profile.d/set-clang-cc.sh
EJECUTAR echo "export CC = clang-5.0" | tee -a $ {set_clang} && chmod a + x $ {set_clang}

El contenedor creado a partir de este Dockerfile se basará en la imagen golang: 1.9 (la referenciada en el archivo .gitlab-ci.yml).

Mientras lo hacemos, instalamos golint en el contenedor, por lo que lo tenemos disponible. Luego seguimos la forma oficial de instalar Clang 5.0 desde el repositorio LLVM.

Ahora que tenemos el Dockerfile en su lugar, necesitamos construir la imagen del contenedor y ponerla a disposición para GitLab:

$ docker login register.gitlab.com
$ docker build -t Registry.gitlab.com/pantomath-io/demo-tools.
$ docker push register.gitlab.com/pantomath-io/demo-tools

El primer comando te conecta al Registro de GitLab. Luego construye la imagen del contenedor descrita en el Dockerfile. Y finalmente, lo empujas al Registro de GitLab.

Eche un vistazo al Registro de su repositorio, verá su imagen, lista para ser utilizada. Y para que el CI use su imagen, solo necesita actualizar el archivo .gitlab-ci.yml:

imagen: golang: 1.9

se convierte

image: Registry.gitlab.com/pantomath-io/demo-tools:latest

Un último detalle: debe indicarle al CI que use el compilador adecuado (es decir, la variable de entorno CC), por lo que agregamos la inicialización de la variable en el archivo .gitlab-ci.yml:

exportar CC = clang-5.0

Una vez que se realiza la modificación, la próxima confirmación activará la canalización, que ahora funciona:

https://gitlab.com/pantomath-io/demo-tools/pipelines/13497136

Insignias

etiqueta git: insignias init

Foto de Jakob Owens en Unsplash

Ahora que las herramientas están en su lugar, cada commit lanzará un conjunto de pruebas, y probablemente desee mostrarlo, y eso es legítimo :) La mejor manera de hacerlo es usar insignias, y el mejor lugar para ello es el archivo README.

Edítelo y agregue las 4 siguientes insignias:

  • Estado de compilación: el estado de la última tubería en la rama maestra:
[! [Estado de compilación] (https://gitlab.com/pantomath-io/demo-tools/badges/master/build.svg)] (https://gitlab.com/pantomath-io/demo-tools/commits /dominar)
  • Informe de cobertura: el porcentaje de código cubierto por las pruebas
[! [Informe de cobertura] (https://gitlab.com/pantomath-io/demo-tools/badges/master/coverage.svg)] (https://gitlab.com/pantomath-io/demo-tools/commits /dominar)
  • Ir Boleta de calificaciones:
[! [Go Report Card] (https://goreportcard.com/badge/gitlab.com/pantomath-io/demo-tools)] (https://goreportcard.com/report/gitlab.com/pantomath-io/ herramientas de demostración)
  • Licencia:
[! [Licencia MIT] (https://img.shields.io/badge/License-MIT-brightgreen.svg)] (https://img.shields.io/badge/License-MIT-brightgreen.svg)

El informe de cobertura necesita una configuración especial. Debe decirle a GitLab cómo obtener esa información, teniendo en cuenta que hay un trabajo en el CI que lo muestra cuando se ejecuta.
Hay una configuración para proporcionar a GitLab una expresión regular, utilizada en la salida de cualquier trabajo. Si la expresión regular coincide, GitLab considera que la coincidencia es el resultado de la cobertura del código.

Diríjase a Configuración> CI / CD en su repositorio, desplácese hacia abajo hasta la configuración de Análisis de cobertura de prueba en la sección Configuración de canalizaciones generales y use la siguiente expresión regular:

total: \ s + \ (sentencias \) \ s + (\ d +. \ d + \%)

¡Ya está todo listo! Dirígete a la descripción general de tu repositorio y mira tu README:

Conclusión

¿Que sigue? Probablemente más pruebas en tu CI. También puede mirar el CD (Implementación continua) para automatizar la implementación de sus compilaciones. La documentación se puede hacer con GoDoc. Tenga en cuenta que genera un informe de cobertura con code_coverage_report, pero no lo use en el CI. Puede hacer que el trabajo copie el archivo HTML en un servidor web, usando scp (consulte esta documentación sobre cómo usar las claves SSH).

Muchas gracias a Charles Francoise, quien co-escribió este artículo y https://gitlab.com/pantomath-io/demo-tools.

Actualmente estamos trabajando en Pantomath. Pantomath es una solución moderna de monitoreo de código abierto, creada para el rendimiento, que cierra las brechas en todos los niveles de su empresa. El bienestar de su infraestructura es asunto de todos. Mantente al día con el proyecto