Vamos a desarrollar una solución para una canalización de CI/CD con GitHub Actions.
Para ello vamos a abordar distintos aspectos como pueden ser:
Pasar varios code linters en busca de errores de sintaxis
Generar un diagrama del contendido del repositorio
Autoincrementar la versión del package
Automatizar la creación de la release en GitHub
Automatizar la actualización del changelog conforme a los cambios en la release
Eliminar las últimas 10 release y tags
Generar reporte de PageSpeed Insights y adjuntar en el pipeline
Generar la imagen Docker y subirla al Registry de GitHub
Buscar información sensible “contraseñas o token” en los commit
Automatizar la creación de la pull request conforme a una plantilla
Crear distintos disparadores en el pipeline según el contenido del commit
Ahora que tenemos los puntos definidos de lo que queremos realizar en nuestra automatización, vamos a comenzar.
GitHub - danieljesussp/danieljsaldana-terminal
Para empezar, vamos a crear nuestro fichero con todos los jobs con los que vamos a trabajar.
📋 En relación con los tutoriales anteriores, en este introduciremos los eventos que disparan nuestro worflow, condicionales y la necesidad de que se complete un job anterior.
Tenemos dos disparados distintos. Por un lado, tenemos el disparador task completed e issue resolved. Cada uno de ellos, nos permitirá controlar que jobs se van a ejecutar según el contenido commit.
group : ci-tests-${{ github.ref }}-1
if : github.event_name == 'pull_request' && github.event.action == 'synchronize' || contains(github.event.head_commit.message, 'task completed') || contains(github.event.head_commit.message, 'issue resolved')
uses : ./.github/workflows/gitguardian.yml
GITGUARDIAN_API_KEY : ${{ secrets.GITGUARDIAN_API_KEY }}
if : github.event_name == 'pull_request' && github.event.action == 'synchronize' || contains(github.event.head_commit.message, 'task completed') || contains(github.event.head_commit.message, 'issue resolved')
uses : ./.github/workflows/super-linter.yml
name : Create pull request
if : contains(github.event.head_commit.message, 'issue resolved')
needs : [ super_linter , gitguardian ]
uses : ./.github/workflows/create-pull-request.yml
if : github.event.pull_request.merged == true
uses : ./.github/workflows/repo_visualizer.yml
if : github.event.pull_request.merged == true
uses : ./.github/workflows/release.yml
name : Delete older releases
if : github.event.pull_request.merged == true
uses : ./.github/workflows/delete-tag-and-release.yml
name : Lighthouse check action
if : github.event.pull_request.merged == true
uses : ./.github/workflows/lighthouse.yml
build_and_push_to_registry :
name : Build and push Docker image to GitHub Packages
if : github.event.pull_request.merged == true
uses : ./.github/workflows/build.yml
if : github.event.pull_request.merged == true
needs : [ build_and_push_to_registry ]
uses : ./.github/workflows/trivy-image.yml
En este job vamos a revisar que nuestros commit no contengan ningún token o contraseña.
💡 Aquí podríamos agregar otros jobs, como por ejemplo test funcionales, pero esto lo dejaremos para más adelante.
- name : '☁️ checkout repository'
uses : actions/checkout@v3
fetch-depth : 0 # fetch all history so multiple commits can be scanned
uses : GitGuardian/gg-shield-action@master
GITHUB_PUSH_BEFORE_SHA : ${{ github.event.before }}
GITHUB_PUSH_BASE_SHA : ${{ github.event.base }}
GITHUB_PULL_BASE_SHA : ${{ github.event.pull_request.base.sha }}
GITHUB_DEFAULT_BRANCH : ${{ github.event.repository.default_branch }}
GITGUARDIAN_API_KEY : ${{ secrets.GITGUARDIAN_API_KEY }}
Esta es una solución de las muchas que podemos encontrar, ya que verifica que nuestros ficheros no tengan errores de sintaxis. Esto puede ser interesante si tenemos manifiestos Kubernetes o los propios pipeline.
- name : '☁️ checkout repository'
uses : actions/checkout@v3
uses : github/super-linter@v4
DEFAULT_WORKSPACE : .github
VALIDATE_ALL_CODEBASE : false
DEFAULT_BRANCH : production
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
- name : Archive super-linter artifacts
uses : actions/upload-artifact@v2
Ahora vamos a automatizar la apertura de la pull request.
name : Create pull request
- name : '☁️ checkout repository'
uses : actions/checkout@v3
- name : Version Increment
echo "**********************"
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
npm version minor -m "v%s"
version=$(node -p "require('./package.json').version")
echo "::set-output name=version::${version}"
echo "**********************"
- name : Create Pull Request
uses : peter-evans/create-pull-request@v4
commit-message : Create pull request
committer : ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
author : ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
branch : ${{ github.ref }}
title : 'v${{ steps.version.outputs.version }} release'
<!-- Incluya un resumen del cambio y qué problema se solucionó. -->
<!-- Incluya también la motivación y el contexto pertinentes. -->
<!-- Enumere las dependencias necesarias para este cambio. -->
## De qué se trata este PR
- Ingrese una breve descripción para este PR
### Ejecuciones de prueba
<!-- Elimine las opciones que no sean relevantes. -->
- [ ] 📚 Actualización de documentación
- [ ] 🐞 Corrección de errores (cambio continuo que soluciona un problema)
- [ ] 🔬 Nueva característica (cambio continuo que agrega funcionalidad)
- [ ] 🚨 Cambio importante (corrección o característica que haría que la funcionalidad existente no funcionara como se esperaba)
- [ ] 📝 Este cambio requiere una actualización de documentación
- [ ] Mi código sigue las pautas de estilo de este proyecto
- [ ] He realizado una auto-revisión de mi propio código
- [ ] He comentado mi código, particularmente en áreas difíciles de entender
- [ ] He realizado los cambios correspondientes a la documentación.
- [ ] Mis cambios no generan nuevas advertencias
- [ ] ¿Actualizó CHANGELOG en caso de un cambio importante?
assignees : ${{ github.actor }}
reviewers : ${{ github.actor }}
Ahora vamos a una imagen svg con el que podamos tener un gráfico de burbujas del contenido de nuestro repositorio.
- name : '☁️ checkout repository'
uses : actions/checkout@v3
uses : githubocto/repo-visualizer@0.7.1
output_file : 'diagram.svg'
excluded_paths : 'dist,node_modules'
En el siguiente job, lo más relevante bajo mi punto de vista, sería el hecho de usar una variable en otro step.
- name : '☁️ checkout repository'
uses : actions/checkout@v3
- name : Version Increment
echo "**********************"
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
npm version minor -m "v%s"
version=$(node -p "require('./package.json').version")
echo "::set-output name=version::${version}"
echo "**********************"
uses : release-drafter/release-drafter@v5
version : ${{ steps.version.outputs.version }}
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
uses : stefanzweifel/changelog-updater-action@v1
latest-version : ${{ steps.release.outputs.tag_name }}
release-notes : ${{ steps.release.outputs.body }}
- name : Commit updated CHANGELOG
uses : stefanzweifel/git-auto-commit-action@v4
branch : ${{ github.event.release.target_commitish }}
commit_message : Update CHANGELOG
file_pattern : CHANGELOG.md
Este paso nos evitará tener que ir borrando manualmente antiguas release y tags.
💡 Si queremos tener todas las release y tags, solo tendremos que eliminar este fichero y también de la parte de ci.yml
name : Delete tag and release
- uses : dev-drprasad/delete-older-releases@v0.2.0
GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
Este job ya nos suena, ya que lo hemos empleado anteriormente.
- name : '☁️ checkout repository'
uses : actions/checkout@v3
- name : 'Create temporary directory'
run : mkdir -p ${{ github.workspace }}/lighthouse/artifacts
uses : foo-software/lighthouse-check-action@master
outputDirectory : ${{ github.workspace }}/lighthouse/artifacts
urls : 'https://terminal.danieljsaldaña.com'
uses : actions/upload-artifact@master
path : ${{ github.workspace }}/lighthouse/artifacts
Ahora sí, llego el momento de crear nuestra imagen y subirla a nuestro Registry.
name : Build and push Docker image to GitHub Packages
IMAGE_NAME : ${{ github.repository }}
build_and_push_to_registry :
name : Build and push Docker image to GitHub Packages
- name : Checkout repository
uses : actions/checkout@v3
- name : Log into registry ${{ env.REGISTRY }}
uses : docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
registry : ${{ env.REGISTRY }}
username : ${{ github.repository_owner }}
password : ${{ secrets.GITHUB_TOKEN }}
- name : Extract Docker metadata
uses : docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
images : ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
type=raw,value=1.0.${{ github.run_number }},priority=1000
uses : docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
tags : ${{ steps.meta.outputs.tags }}
labels : ${{ steps.meta.outputs.labels }}
- name : Push Docker image
uses : docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
tags : ${{ steps.meta.outputs.tags }}
labels : ${{ steps.meta.outputs.labels }}
En este caso vamos a utilizar Trivy, pero podemos utilizar la herramienta que queramos para analizar nuestra imagen.
💡 Este job podríamos agregarlo a la parte de build y analizar nuestra imagen antes de publicarla. De esta forma, si tuviera vulnerabilidades críticas, evitaríamos su publicación.
IMAGE_NAME : ${{ github.repository }}
build_and_push_to_registry :
name : Build and push Docker image to GitHub Packages
- name : Checkout repository
uses : actions/checkout@v3
- name : 'Create temporary directory'
run : mkdir -p ${{ github.workspace }}/trivy-image/artifacts
- name : Run Trivy vulnerability scanner
uses : aquasecurity/trivy-action@master
image-ref : '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest'
output : trivy-image/artifacts/trivy-image.log
severity : 'CRITICAL,HIGH'
TRIVY_USERNAME : ${{ github.repository_owner }}
TRIVY_PASSWORD : ${{ secrets.GITHUB_TOKEN }}
uses : actions/upload-artifact@master
name : Trivy image reports
path : ${{ github.workspace }}/trivy-image/artifacts
Ahora sí, hemos terminado nuestra automatización. Espero que os haya parecido interesante este lavatorio.