Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

Una de las funciones más necesarias que no está disponible en la versión gratuita de GitLab es la posibilidad de votar en contra de la anulación del repositorio controlando el Merge request (MR) mediante una revisión de código obligatoria.

Vamos a implementar una funcionalidad mínima: prohibiremos el Merge hasta que varios desarrolladores den un 'me gusta' al MR.

¿Por qué es esto importante?

Nuestra organización puede permitirse comprar una licencia de GitLab. Sin embargo, como el desarrollo se lleva a cabo en un entorno cerrado sin acceso a Internet y hay una estricta planificación presupuestaria, la compra de licencias autogestionadas con la funcionalidad requerida puede tardar meses, y ya necesitamos comenzar a trabajar.

Por lo tanto, se hace necesario:

  • o prohibir completamente el Merge en ramas protegidas para algunos desarrolladores, pero entonces los desarrolladores que tienen derecho a hacer Merge obtienen conflictos al fusionar MR ajenos como un extra;
  • o permitir fusiones descontroladas con tu rama principal sin revisión de código, incluso si es un Junior que se unió ayer.

Lo primero que hice fue buscar en Google, pensando que seguro alguien ya había hecho algo similar (sin necesidad de modificar el código), pero resultó que no había una implementación de este tipo en la versión comunitaria.

Esquema general de trabajo

Como ejemplo, configuraremos las aprobaciones de Merge request en un repositorio de prueba myapp:

  1. Crearemos un token para acceder a la API de GitLab (a través de él obtendremos información sobre la cantidad de votos 'a favor' y 'en contra')
  2. Agregaremos el token a las variables de GitLab
  3. Prohibiremos el Merge en caso de errores en el pipeline (si no hay suficientes votos 'a favor')
  4. Configuraremos la verificación de votos como parte del pipeline CI/CD
  5. Prohibiremos hacer commits en ramas protegidas, todos los cambios se realizarán únicamente a través de MR
  6. Comprobaremos qué hemos logrado finalmente

1. Creamos un token para acceder a la API

Accedemos a Configuración de usuario → Tokens de acceso y anotamos el token:

Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

Cuenta para obtener el token
El acceso a la API permite hacer prácticamente todo con tus repositorios, por lo que recomiendo crear una cuenta de GitLab separada, darle los permisos mínimos en tus repositorios (por ejemplo, Reporter) y obtener un token para esta cuenta.

2. Agregamos el token a las variables de GitLab

Por ejemplo, en el paso anterior obtuvimos el token QmN2Y0NOUFlfeXhvd21ZS01aQzgK

Accedemos a Configuración → CI/CD → Variables → Agregar variable → GITLAB_TOKEN_FOR_CI

Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

En resumen, obtendremos:

Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

Esto se puede hacer tanto en un repositorio como en un grupo de repositorios.

3. Prohibimos Merge si no se obtienen aprobaciones de los colegas después de realizar la revisión de código.

En nuestro caso, la prohibición de Merge será que el pipeline de compilación devuelva un error si no hay suficientes votos.

Vamos a Configuración → General → Solicitudes de fusión → Revisiones de fusión y habilitamos la opción Las líneas de compilación deben ejecutarse correctamente.

Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

4. Configuramos el pipeline

Si aún no has creado un pipeline CI/CD para tu aplicación
Creamos un archivo en la raíz del repositorio .gitlab-ci.yml con el contenido más simple:

stages:
  - build
  - test

variables:
  NEED_VOTES: 1

include:
  - remote: "https://gitlab.com/gitlab-ce-mr-approvals/ci/-/raw/master/check-approve.gitlab-ci.yml"

run-myapp:
  stage: build
  script: echo "Hello world"

Un repositorio separado para la configuración CI/CD
Te recomendaría crear un repositorio separado en el que debes crear el archivo myapp.gitlab-ci.yml para configurar el pipeline. Así podrás controlar mejor el acceso de los participantes que pueden modificar el pipeline de compilación y obtener un token de acceso.

Ubicación del nuevo archivo del pipeline deberá especificarse ingresando al repositorio myapp — Configuración — CI/CD — Líneas de compilación — Ruta de configuración CI personalizada — especificar el nuevo archivo, por ejemplo myapp.gitlab-ci.yml@gitlab-ce-mr-approvals/Ci

Consejo: utiliza un linter para realizar cambios en los archivos de GitLab CI
Incluso si trabajas solo, será útil pasar por MR, corriendo todos tus cambios en los archivos del pipeline a través de un linter. Si cometes un error de sintaxis en el archivo YAML, no romperás el pipeline en funcionamiento, sino que simplemente bloquearás el Merge.

Ejemplo de contenedores con linters que puedes integrar en tu pipeline:

hub.docker.com/r/gableroux/gitlab-ci-lint
hub.docker.com/r/sebiwi/gitlab-ci-validate

Y un ejemplo de etapa de validación:

stages:
  - lint

lint:
  stage: lint
  image: sebiwi/gitlab-ci-validate:1.3.0
  variables:
    GITLAB_HOST: https://gitlab.com
  script:
    - CI_FILES=(./*.yml)
    - for f in "${CI_FILES[@]}"; do
        gitlab-ci-validate $f;
      done;

Solo queda agregar algunos parámetros a tu pipeline para que todo funcione:

etapas:
- prueba

variables:
NEED_VOTES: 1

include:
- remote: "https://gitlab.com/gitlab-ce-mr-approvals/ci/-/raw/master/check-approve.gitlab-ci.yml"

La variable NEED_VOTES define cuántos "me gusta" debe tener el MR para que el Merge esté disponible. Un valor de uno significa que tú mismo puedes aprobar tu MR, "dándole un me gusta".

include conecta la etapa de test, que verifica el número de "me gusta".

Pipeline más simple usando myapp.gitlab-ci.yml
etapas:
- construir
- prueba

variables:
NEED_VOTES: 0

include:
- remote: "https://gitlab.com/gitlab-ce-mr-approvals/ci/-/raw/master/check-approve.gitlab-ci.yml"

run-myapp:
etapa: construcción
imagen: openjdk
script:
- echo CI_MERGE_REQUEST_TARGET_BRANCH_NAME $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
- java HelloWorld.java

Contenido check-approve.gitlab-ci.yml
ci-mr:
etapa: prueba
script:
- echo ${CI_API_V4_URL}
- echo "CI_PROJECT_ID ${CI_PROJECT_ID}"
- echo "CI_COMMIT_SHA ${CI_COMMIT_SHA}"
- "export MR_ID=$(curl --silent --request GET --header "PRIVATE-TOKEN: $GITLAB_TOKEN_FOR_CI" ${CI_API_V4_URL}\/projects\/${CI_PROJECT_ID}\/merge_requests | jq ".[] | if .sha == \"${CI_COMMIT_SHA}\" then .id else {} end" | grep --invert-match {})"
- "export MR_TITLE=$(curl --silent --request GET --header "PRIVATE-TOKEN: $GITLAB_TOKEN_FOR_CI" ${CI_API_V4_URL}\/projects\/${CI_PROJECT_ID}\/merge_requests | jq ".[] | if .sha == \"${CI_COMMIT_SHA}\" then .title else {} end" | grep --invert-match {})"
- "export MR_WIP=$(curl --silent --request GET --header "PRIVATE-TOKEN: $GITLAB_TOKEN_FOR_CI" ${CI_API_V4_URL}\/projects\/${CI_PROJECT_ID}\/merge_requests | jq ".[] | if .sha == \"${CI_COMMIT_SHA}\" then .work_in_progress else {} end" | grep --invert-match {})"
- "export MR_UPVOTES=$(curl --silent --request GET --header "PRIVATE-TOKEN: $GITLAB_TOKEN_FOR_CI" ${CI_API_V4_URL}\/projects\/${CI_PROJECT_ID}\/merge_requests | jq ".[] | if .sha == \"${CI_COMMIT_SHA}\" then .upvotes else {} end" | grep --invert-match {})"
- "export MR_DOWNVOTES=$(curl --silent --request GET --header "PRIVATE-TOKEN: $GITLAB_TOKEN_FOR_CI" ${CI_API_V4_URL}\/projects\/${CI_PROJECT_ID}\/merge_requests | jq ".[] | if .sha == \"${CI_COMMIT_SHA}\" then .downvotes else {} end" | grep --invert-match {})"
- MR_VOTES=$(expr ${MR_UPVOTES} - ${MR_DOWNVOTES})
- NEED_VOTES_REAL=${NEED_VOTES:-1}
- echo "MR_ID ${MR_ID} MR_TITLE ${MR_TITLE} MR_WIP ${MR_WIP} MR_UPVOTES ${MR_UPVOTES} MR_DOWNVOTES ${MR_DOWNVOTES}"
- echo "MR_VOTES ${MR_VOTES} Voto positivo = 1, voto negativo = -1, MR OK si los votos >=${NEED_VOTES_REAL}"
- if [ "${MR_VOTES}" -ge "$(expr ${NEED_VOTES_REAL})" ];
then
echo "MR OK";
else
echo "MR ERROR Se necesitan más votos";
exit 1;
fi
imagen: laptevss/gitlab-api-util
reglas:
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ \/^release\/.*$\/'

Más detalles sobre lo que sucede durante la revisión:

  • se ha establecido una restricción de que la revisión ocurrirá solo al crear MR en ramas master o release/*
  • usando la API de GitLab, obtenemos el número de 'me gusta' y 'no me gusta'
  • calculamos la diferencia entre las respuestas positivas y negativas
  • si la diferencia es menor que el valor que hemos establecido en NEED_VOTES, bloqueamos la posibilidad de realizar la fusión

5. Prohibimos los commits en ramas protegidas

Definimos las ramas para las que debemos realizar code review e indicamos que se puede trabajar con ellas solo a través de MR.

Para ello, vamos a Configuración → Repositorio → Ramas protegidas:

Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

6. Revisamos

Establezcamos NEED_VOTES: 0

Creamos MR y marcamos 'no me gusta'.

Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

En los registros de construcción:

Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

Ahora marcamos 'me gusta' y ejecutamos la comprobación nuevamente:

Revisión de código en GitLab CE: si no hay aprobación de Merge request, pero realmente lo deseas

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