Crear una canalización de CI/CD con GitHub Actions
Tags: devops, github, spanish, ci
Published: 2023-11-13 07:35:05

---
title: Crear una canalización de CI/CD con GitHub Actions
published: true
date: 2022-12-27 17:44:00 UTC
tags: DevOps, GitHub, Spanish, CI
canonical_url: https://danieljsaldana.dev/crear-una-canalizacion-de-ci-cd-con-github-actions/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3htelyupassxa8lp2eg.png
---

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](https://github.com/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.

```
name: CI + CD

on:
  push:
    branches:
      - "**"
      - "!production"
  pull_request:
    branches:
      - "**"
    types:
      - synchronize
      - closed

concurrency:
  group: ci-tests-${{ github.ref }}-1
  cancel-in-progress: true

jobs:
  gitguardian:
    name: GitGuardian scan
    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
    secrets:
      GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}

  super_linter:
    name: Super-Linter scan
    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

  create_pull_request:
    name: Create pull request
    if: contains(github.event.head_commit.message, 'issue resolved')
    needs: [super_linter, gitguardian]
    uses: ./.github/workflows/create-pull-request.yml

  repo_visualizer:
    name: Update diagram
    if: github.event.pull_request.merged == true
    uses: ./.github/workflows/repo_visualizer.yml

  release:
    name: Create release
    if: github.event.pull_request.merged == true
    needs: [repo_visualizer]
    uses: ./.github/workflows/release.yml

  delete_older_releases:
    name: Delete older releases
    if: github.event.pull_request.merged == true
    needs: [release]
    uses: ./.github/workflows/delete-tag-and-release.yml

  lighthouse:
    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
    needs: [release]
    uses: ./.github/workflows/build.yml

  trivy_scan:
    name: Trivy image scan
    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: GitGuardian scan

on:
  workflow_call:
    secrets:
      GITGUARDIAN_API_KEY:
        required: false

jobs:
  security:
    name: GitGuardian scan
    runs-on: ubuntu-latest
    steps:
      - name: "☁️ checkout repository"
        uses: actions/checkout@v3
        with:
          fetch-depth: 0 # fetch all history so multiple commits can be scanned

      - name: GitGuardian scan
        uses: GitGuardian/gg-shield-action@master
        env:
          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: Lint Code scan

on:
  workflow_call:

jobs:
  super-linter:
    runs-on: ubuntu-latest
    steps:
      - name: "☁️ checkout repository"
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Lint Code Base
        uses: github/super-linter@v4
        env:
          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
        with:
          name: MegaLinter reports
          path: |
            super-linter.log

```

Ahora vamos a automatizar la apertura de la pull request.

```
name: Create pull request

on:
  workflow_call:

jobs:
  create-pull-request:
    runs-on: ubuntu-latest
    steps:
      - name: "☁️ checkout repository"
        uses: actions/checkout@v3

      - name: Version Increment
        id: version
        run: |
          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
        id: cpr
        uses: peter-evans/create-pull-request@v4
        with:
          commit-message: Create pull request
          committer: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
          author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
          signoff: false
          branch: ${{ github.ref }}
          delete-branch: true
          base: production
          title: "v${{ steps.version.outputs.version }} release"
          body: |
            # Cambios
            <!-- 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. -->

            Fixes # (issue)

            ## De qué se trata este PR

            - Ingrese una breve descripción para este PR

            ### Ejecuciones de prueba

            - [Run actions](<>)

            ## Tipo de cambio
            <!-- Elimine las opciones que no sean relevantes. -->
            - [] 📚 Actualización de documentación
            - [] 🧪 Casos de prueba
            - [] 🐞 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

            ## Checklist

            - [] 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?
          labels: |
            automated pr
          assignees: ${{ github.actor }}
          reviewers: ${{ github.actor }}
          draft: false

```

Ahora vamos a una imagen svg con el que podamos tener un gráfico de burbujas del contenido de nuestro repositorio.

```
name: Repo Visualizer

on:
  workflow_call:

jobs:
  repo-visualizer:
    name: Repo Visualizer
    runs-on: ubuntu-latest
    steps:
      - name: "☁️ checkout repository"
        uses: actions/checkout@v3

      - name: Update diagram
        uses: githubocto/repo-visualizer@0.7.1
        with:
          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: Create release

on:
  workflow_call:

jobs:
  version:
    permissions:
      contents: write
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - name: "☁️ checkout repository"
        uses: actions/checkout@v3

      - name: Version Increment
        id: version
        run: |
          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")
          git tag ${VERSION}
          git push --force
          echo "::set-output name=version::${version}"
          echo " **********************"

      - name: "Create release"
        uses: release-drafter/release-drafter@v5
        id: release
        with:
          version: ${{ steps.version.outputs.version }}
          publish: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Update Changelog
        uses: stefanzweifel/changelog-updater-action@v1
        with:
          latest-version: ${{ steps.release.outputs.tag_name }}
          release-notes: ${{ steps.release.outputs.body }}

      - name: Commit updated CHANGELOG
        uses: stefanzweifel/git-auto-commit-action@v4
        with:
          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

on:
  workflow_call:

jobs:
  delete-tag-and-release:
    runs-on: ubuntu-latest
    steps:
      - uses: dev-drprasad/delete-older-releases@v0.2.0
        with:
          keep_latest: 10
          delete_tags: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

```

Este job ya nos suena, ya que lo hemos empleado anteriormente.

```
name: Lighthouse check

on:
  workflow_call:

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - name: "☁️ checkout repository"
        uses: actions/checkout@v3

      - name: "Create temporary directory"
        run: mkdir -p ${{ github.workspace }}/lighthouse/artifacts

      - name: Lighthouse
        uses: foo-software/lighthouse-check-action@master
        with:
          outputDirectory: ${{ github.workspace }}/lighthouse/artifacts
          urls: "https://terminal.danieljsaldaña.com"

      - name: Upload artifacts
        uses: actions/upload-artifact@master
        with:
          name: Lighthouse reports
          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

on:
  workflow_call:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build_and_push_to_registry:
    name: Build and push Docker image to GitHub Packages
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Log into registry ${{ env.REGISTRY }}
        uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=raw,value=1.0.${{ github.run_number }},priority=1000
            type=ref,event=branch
            type=sha
            type=raw,value=latest

      - name: Build image
        uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
        with:
          context: .
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Push Docker image
        uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
        with:
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          push: true

```

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.

```
name: Trivy scan image

on:
  workflow_call:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build_and_push_to_registry:
    name: Build and push Docker image to GitHub Packages
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - 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
        with:
          image-ref: "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
          format: "table"
          exit-code: "0"
          ignore-unfixed: true
          vuln-type: "os,library"
          output: trivy-image/artifacts/trivy-image.log
          severity: "CRITICAL,HIGH"
        env:
          TRIVY_USERNAME: ${{ github.repository_owner }}
          TRIVY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload artifacts
        uses: actions/upload-artifact@master
        with:
          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.