Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Implementa el workflow CI/CD con GitHub Actions en tus aplicaciones

Implementa el workflow CI/CD con GitHub Actions en tus aplicaciones

Jose María Flores Zazo

June 02, 2021
Tweet

More Decks by Jose María Flores Zazo

Other Decks in Technology

Transcript

  1. Bienvenidos Acerca de… ¡Hola! Gracias por entrar en “Implementa el

    workflow CI/CD de GitHub Actions en tus aplicaciones”. Espero poder aportarte los conocimientos mínimos y necesarios con este workshop para que puedas ponerlo en práctica. Jose María Flores Zazo, autor
  2. ¿Por qué? Presentación(1/2) No necesita presentación, es el proveedor de

    repositorios de codigo más utilizado del planeta. Donde podrás encontrar mucho Open Source y donde muchas compañías guardan sus repositorios privados. Hoy en día el código no vive sin una integración continua / implementación continua (CI/CD) ya que es una práctica normalizada (afortunadamente) entre otras cosas por su agilidad. Los repositorios de GitHub se pueden integrar con herramienta de terceros: Jenkins o Azure DevOps, por poner las más conocidas. Desde que Microsoft adquirió GitHub la relación esta siendo de una absoluta simbiosis, por eso me lleva a escribir este workshop y por que en las últimas semanas (antes del #MSBuild2021) en foros de partners de DevOps vimos que el movimiento de Microsoft es potenciar GitHub y que durante el #MSBuild2021 había muchas más charlas y comentarios que nunca, eso me hace pensar durante #MSBuild2021 que el movimiento generado en el canal de partners era algo a tener en cuenta. Eso que os cuento y por qué es muy complicado que Microsoft pueda mover a todos los repositorios de Open Source a su plataforma de Azure DevOps. Creo que GitHub Actions puede convertirse en el sucesor de Azure DevOps para las Pipelines. GitHub – https://github.com/
  3. ¿Cómo? Presentación(2/2) Los beneficios principales de las acciones son: ▪

    Las acciones nos permiten tener workflows de trabajo para CI/CD dentro de GitHub. ▪ Las acciones nos permiten trabajar con Pull Request, mejorando la estabilidad de la rama del repositorio y garantizando la compilación del codigo al realizar un merge. En este paseo por GitHub Actions, voy a explicar como implementar flujos de trabajo CI/CD. Vamos a ver desde soluciones para crear builds con diferentes tecnologías, pautas para implementar componentes como acciones, GitHub hosted mahines (runners) para ejecutar workflows, etc. Todo ello con explicación de las acciones disponibles y la sintaxis, además llegaremos a ver la implementación de acciones personalizadas que puedan cubrir tus necesidades. Lógicamente GitHub dispone de una ayuda que nos permitirá profundizar mucho en cualquier tema de los que trate en este workshop: https://docs.github.com/es/actions Actions – Acciones
  4. ¿Qué es CI/CD? Introducción(1/5) Integración continua y entrega continua son

    conceptos que ya debes conocer sin embargo permíteme que de una breve explicación. En el mundo del desarrollo del software actual, varios miembros del equipo solemos trabajar o contribuir en la creación de una misma funcionalidad. Cuando muchos de nosotros trabajamos sobre una funcionalidad es obligatorio imponer unas reglas que aseguren la integridad del código y que compile correctamente. Por tanto, dos aspectos debemos mantener para asegurar la estabilidad del código: ▪ Que el codigo siempre compile sin errores. ▪ Que todas las pruebas que validan el código pasan correctamente. Se debe crear un pipeline para cada commit/check-in o para cada Pull Request, según sea tu opción de trabajo. Esto nos asegurará que el codigo esta estable ya que pasa las dos premisas anteriores. Esto se suele llamar build de CI. Otro aspecto importante es la verificación de seguridad. Por ejemplo, vulnerabilidades originadas por las importaciones de paquetes de terceros. La integración supone un salto de calidad en nuestro software y que siempre veo olvidado en los proyectos. CI/CD – Continuos Integration/Continuous Delivery
  5. ¿Qué es CI/CD? Introducción(2/5) Y como no la calidad inherente

    al codigo, con herramientas como SonarQube a parte de testear las vulnerabilidades de seguridad podemos revisar el codigo con reglas que nos permiten localizar partes de codigo con problema conocidos por la enorme base de datos de herramientas como SonarQube. Es decir, que podemos implementar una integración de código muy robusta usando unos simples pasos y con un costo muy bajo que revierte siempre en beneficio. La idea es que también los desarrolladores entremos en ciclos cortos de integración y que sean ágiles. Cuando un ciclo se alarga mucho, podemos mantener el repo ordenado usando las técnicas de Git como unir 100 commit en un solo, o cosas similares que no solo dependen del proceso de CI, se dan en un paso anterior. Debemos velar por su correcta aplicación. Para CI es de vital importancia implementar un proceso sencillo y reutilizable que incidirá en: reducción de costes, tiempos y riesgos a la hora de entregar el software.
  6. ¿Qué es CI/CD? Introducción(3/5) La otra parte que queda por

    explicar CD puede ser aplicado de dos formas: ▪ Continuos Delivery (entrega continua) garantiza que los cambios que se implementan en producción quedan suspendidos hasta que se apruebe una implementación de forma manual. ▪ Continuos Deployment (implementación continua) no necesitan ninguna aprobación y deberían estar implementados en el mismo proceso de CI. Fuente: https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment
  7. ¿Por qué es importante CI/CD? Introducción(4/5) Entre otras cosas la

    automatización de entregas implica algunos procesos que hemos visto: validación del codigo, estabilidad del codigo, calidad y seguridad del codigo, … Y otros tantos como: IaC, testing de Integración, testing E2E, ... Que verifican que las necesidades han sido satisfechas y que son parte esencial del DevOps. Y no tiene ningún sentido hablarlo en este workshop. Lo fundamental es que esta métrica: Cost Of Bugs, es una métrica que hace tiempo se usa para ilustrar el porqué del CI/CD. Fuente: https://www.researchgate.net/figure/IBM-System-Science-Institute-Relative-Cost-of-Fixing-Defects_fig1_255965523 Cost of bugs – El coste de los bugs
  8. Vocabulario Introducción(5/5) ▪ Action, pequeña porción de un workflow que

    permite realizar una tarea (task). Estas tareas o pasos combinadas generan un workflow. Puedes utilizar las que proporciona GitHub o crear las tuyas. Una acción debe usarse como un paso (step) de un flujo de trabajo. ▪ Artifacts, archivos generados cuando realizar la build de un proyecto. Pueden contener binarios o cualquier archivo de forme parte de la aplicación. Los artefactos se pueden crear en un trabajo (job) para que lo use otro que forma parte de tu workflow. ▪ Event, un evento desencadena un workflow de GitHub Actions. Puedes usar Webhooks para implementar triggers externos. ▪ GitHub-Hosted Runners, son similares a los agentes (“maquinas virtuales”) de Azure DevOps. Tenemos runners de tipo Linux, Windows, macOS. No se pueden personalizar. Si quieres ver más información, sigue este enlace. ▪ Job, un trabajo es un conjunto de pasos configurados para que se ejecuten en un runner. Contiene una o más acciones, se puede ejecutar en paralelo y depender de otros Jobs. Un trabajo dependiente nunca se ejecutará si fallan las dependencias. Cada trabajo ejecuta en una instancia nueva de un runner. Obligatoriamente se debe especificar. ▪ Self-Hosted Runners, lógicamente puedes crear tus propios runners. ▪ Step, una task que es una acción o un comando se le denomina paso. Todos los pasos de un job se ejecutan en un mismo runner. El sistema de ficheros de múltiples pasos se comparte para ese job. ▪ Workflow, flujo de trabajo. Es el proceso que queda configurado en el YAML. Esta programado para que se ejecute en función de un desencadenador / evento. Puede tener uno o más Jobs para que se ejecuten en paralelo o de forma secuencia. JERGA – Toda tecnología tiene sus palabras
  9. Usando una plantilla Manos a la obra(1/13) Como ya apunté

    antes se trata de un archivo YAML que consta de diversas instrucciones que tratan de automatizar un proceso. Se compone de: Jobs, Events, Steps, Actios y Runners. Los pasos se identifican como taks que son ejecutadas por job, que ejecutna Actions y comandos. Un flujo de trabajo puede tener uno o varios trabajos dependientes. El archivo necesita un mecanismo que lo desencadene o eventos que decidan que desencadenar. Un runner es una máquina donde se ejecutan las GitHub Actions. Como cualquier otra plataforma, GitHub nos proporciona plantillas para ahorrar tiempo o bien para poder modificarla partiendo de una base. Si explorar podrás ver que tienes dede aplicaciones .NET, Node.js, para usar Azure, AWS, Alibaba, con Docker, … creo que cerca de un centenar de plantillas por la que puedes comenzar sin tener que escribirla desde 0. Workflow Template – Plantilla de flujo de trabajo
  10. Acciones del Markeplace Manos a la obra(2/13) Similar a tanto

    y tantos sitios que son usados por miles de usuarios, al final aparece un mercado para que puedas poner visible tus trabajos y que sean reutilizados por otras personas. Lógicamente GitHub tiene el suyo para las Acciones: https://github.com/marketplace?type=actions Marketplace – Mercado
  11. Estructura de un Workflow Manos a la obra(3/13) Si entramos

    por la opción de Actions, podrás crear un YML inicial que genera los pasos y explica cada uno de ellos, aun así, voy a explicarlos. Get started … – Pues eso ;)
  12. Estructura de un Workflow Manos a la obra(4/13) Nombre de

    la Pipeline: Una acción que se ejecuta cuando se sube a la rama main o se hace un pull request a la rama main:
  13. Estructura de un Workflow Manos a la obra(5/13) Un job

    que se ejecuta de forma secuencial, contra un runner (ubutu-latest) realiza unos pasos escribir “Hola Mundo” y ejecutar en otra tarea un script multilínea: Dejamos como esta este pipeline, que no hace nada en realidad, pero que nos servirá para seguir con los pasos estándar del proceso.
  14. Estructura de un Workflow Manos a la obra(6/13) Hemos puesto

    que se ejecute cada vez que se sube un pull a master, lo lanza la primera vez ya que hemos metido el commit el yml: Se va ejecutando:
  15. Estructura de un Workflow Manos a la obra(7/13) Podemos ir

    viendo el progreso en la propia build:
  16. Estructura de un Workflow Manos a la obra(8/13) Y si

    funciona deberíamos ver algo así: Si ha sido observador verás que pone varios desplegables: Events, Status, Branch y Actor. Que nos sirve en una organización para filtrar el estado de los pipelines, es muy útil cuando tienes muchos, completamente inútil en estos momentos. Una vez visto el proceso completo, vamos a ir profundizando en cada parte del fichero. Por cierto, puedes aprender YAML en 5 minutos: https://www.codeproject.com/Articles/1214409/Learn-YAML-in-five-minutes
  17. Estructura de un Workflow Manos a la obra(9/13) Cuando configuras

    un trigger, necesitas identificar el evento que lo dispara. Un evento se define usando la clausula on, que es obligatoria. Por ejemplo, que se ejecute con un solo evento: O con una lista de eventos: O con un schedduler
  18. Estructura de un Workflow Manos a la obra(10/13) Otro punto

    que hemos visto en el ejemplo es la clausula workflow_dispatch, que nos permite ejecutar el workflow de forma manual. Como puedes modificarlo puedes poner cosas como valore de entrada: Donde también podemos añadir una task (greetuser) y unos pasos + un runner.
  19. Estructura de un Workflow Manos a la obra(11/13) Os recomiendo

    que uséis alguna de las extensiones para VS Code. En otro caso puedes ir viendo la información con IntelliSense del editor de GitHub: A continuación, vamos a crear una aplicación .NET Core 3.1 con la que vamos a realizar un build y por tanto continuar con la estructura del workflow.
  20. Estructura de un Workflow Manos a la obra(12/13) Con la

    aplicación de ejemplo podrás ver que el YAML llamado dotnet.yml, nos prepara una build y publica un artefacto:
  21. Estructura de un Workflow Manos a la obra(13/13) Veamos por

    partes que hemos realizado: ▪ on, que se ejecute bajo unas condiciones o manual, como antes. ▪ runs-on: que use una maquina Ubuntu para trabajar el job. ▪ Los pasos que hemos realizado son: ▪ Hacer un checkout de GitHub Actions. ▪ Establecer la version de .Net Core. ▪ Restaurar dependencias. ▪ Crear una build. ▪ Testearla (nuestro ejemplo no contiene test). ▪ Publicarla ▪ Y cargar el artefacto para un posterior despliegue, por ejemplo. Con este otro ejemplo hemos podido ver como va funcionando la creación de YAML (os recomiendo entrar en el enlace donde se aprende YAML en 5 minutos).
  22. ¿Qué opciones tenemos? Variables(1/3) Como en muchos lenguajes podemos definir

    variables según el ámbito. ▪ Variables a nivel de Workflow: ▪ Variables a nivel de Job. ▪ Variables a nivel de Step. Tenéis un ejemplo llamado variables.yml Scope – Ámbito
  23. En tiempo de ejecución Variables(2/3) Existe un comando set-env que

    nos permite crear variables o modificarlas, pero cada variable no será visible en el paso en el que la estableces, será visible en las siguientes acciones o pasos del job. El ejemplo se encuentra en set-env-variables.yml.
  24. Consideraciones Variables(3/3) Existe una serie de variables: • HOME, directorio

    del runner que almacena el workflow. • GITHUB_WORKFLOW, nombre del workflow. • GITHUB_RUN_ID, en el repo cada workflow se ejecuta con un id unico. • GITHUB_ACTIONS, nos dice si un job se esta ejecutando o no. • GITHUB_ACTOR, nombre de persona que inicio el workflow. • GITHUB_WORSPACE, directorio donde corre el job en runner machine. • Y muchas más: https://docs.github.com/es/actions/reference/environment-variables Debemos tener cuidado con ciertas cosas al usar un naming de variables: • GITHUB_ como prefijo, no lo puedes usar, esta reservado. • Es case sensitive. • _PATH, se usan para definir localizaciones del filesystem. • Cuidado con los caracteres especiales.
  25. No dejemos cosas a la vista Secretos y Tokens(1/3) Guardar

    secretos en sumamente importante en cualquier herramienta de CI/CD. Protegen la información confidencial, como son: cadenas de conexión, contraseñas o cualquier configuración sensible. GitHub tiene dos niveles de secretos: ▪ A nivel de repositorios. ▪ A nivel de organización. Secrets – Secretos
  26. No dejemos cosas a la vista Secretos y Tokens(2/3) ¿Cómo

    se usa en un Workflow? Y ten cuidado conciertas limitaciones como el tamaño hasta 64KB y el máximo de 100 secretos.
  27. No dejemos cosas a la vista Secretos y Tokens(3/3) Es

    posible que en tu workflow debas enviar cambios a un repositorio o poner un label. O crear una incidencia mientras ejecutas el repositorio, cosas habituales a la hora de trabajar. Para realizar esto debemos obligar a nuestro workflow a realizar una autenticación. No necesitas crear ese TOKEN ya que automáticamente GitHub crear internamente el GITHUB_TOKEN por ti y para que puedas utilizarlos dentro de ese repositorio, si necesitas que tenga más permisos (ya lo veremos más adelante) es obligatorio utilizar tokens personales. {{ secrets.GITHUB_TOKEN }} es una variable reservada, no podrás crear ninguna con ese nombre. Gracias a GITHUB_TOKEN y una llamada a un API es como podemos crear Issues o cambiar los Status. Nada mejor que ir a la fuente original para ver unos ejemplos de uso: https://docs.github.com/es/actions/reference/authentication-in-a-workflow El ejemplo que podéis ver escribe un issue automáticamente, pero podéis completarlo usando clausulas como: if: ${{ failure() }} Tokens – GITHUB_TOKEN
  28. Guardar contenido en artefactos Artifacts(1/2) Ya hemos hablado antes de

    ellos y hemos visto donde quedan alojados. Por supuesto podemos descargarlos: Artifact – Artefactos
  29. Guardar contenido en artefactos Artifacts(2/2) Usar artefactos que van de

    un flujo a otro para implementar CI/CD es una buena práctica. Pero no es una característica que en GitHub no esta implementada. Lo que podemos hacer es compartir este artefacto entre Jobs de un workflow. Los artefactos tienen una característica importante que debes conocer: la política de retención. Con los artefactos podemos hacer 3 cosas: upload, donwload y deleting. Por ejemplo: - name: 'Upload Artifact' uses: actions/upload-artifact@v2 with: name: my-artifact path: my_file.txt retention-days: 5 Poco más se puede hacer con ellos a parte de usarlos más adelante para desplegarlos en nuestro recursos de Azure, por poner un ejemplo.
  30. Cacheado de dependencias Cache Cuando se ejecutan los Jobs en

    los GitHub-hosted runners suele usarse un entorno virtual limpio y recién creado. Un entorno limpio nos obliga a descargar todas las dependencias en cada ejecución de un job provocando que el job tarde más, que se consuma más ancho de banda y por tanto incremente el coste. Para .NET sería los NuGet y para NodeJS los npm. ¿Cómo indicamos que cache un fichero, directorio, etc.? https://docs.github.com/es/actions/guides/caching-dependencies-to-speed-up-workflows
  31. Un ejemplo práctico de su uso Self-Hosted Runners(1/8) Toda la

    información estos Runners alojada en: https://github.com/actions/virtual-environments/tree/main/images Si entráis en uno de ellos, concretamente en el fichero MD de WS2019 podrás ver la cantidad de herramientas y versiones que dispones: Runners – En otras palabras: Windows, macOS y Linux
  32. Un ejemplo práctico de su uso Self-Hosted Runners(2/8) Podemos establecer

    el uso en tres niveles: ▪ A nivel de repositorio. Dedicado a ese repositorio y no se puede usar con otros. ▪ A nivel de organización. Puedes ejecutarlo en diferentes repos de esa organización. ▪ A nivel de empresa. Pues ejecutarlo en diferentes repos de diferentes organizaciones. Vamos a crear un Self-Hosted Runner para una aplicación de ejemplo. Todos los anteriores ejemplos están en: https://github.com/jmfloreszazo/GitHubActionsWorkshop Para el siguiente ejemplo usaremos este otro: https://github.com/jmfloreszazo/GitHubActionsWorkshopSelfHostedRunner
  33. Un ejemplo práctico de su uso Self-Hosted Runners(3/8) Añadimos el

    Runner: Y seguimos las instrucciones tras pulsar el botón:
  34. Un ejemplo práctico de su uso Self-Hosted Runners(5/8) Ya podemos

    ver nuestro runner: Y añadimos una etiqueta que se podrá usar luego en nuestros YML para indicar en run-on, es muy práctico.
  35. Unos apuntes Self-Hosted Runners(7/8) Lógicamente este proceso es diferente para

    macOS y para Linux. Pero como estamos en Windows, cuando desinstales no borres nada hasta que no pulse sobre: Una vez que copies el comando lo ejecutas y ya puedes borrar sin ningún problema el runner de tu equipo. Como has podido observar, el proceso es muy sencillo, hasta cuando te topas con temas de permisos, aquí es cuando se torna un tanto tedioso; siento no poder poner algunos tópicos al respecto, pero me he encontrado diversos problemas y todos siempre se han solucionado de forma diferentes.
  36. Unos apuntes Self-Hosted Runners(8/8) Por ejemplo, uno problema que suele

    ser persistente y que si puedo aportarte información es si te da error en la acción de run: dotnet build, te sale un error indicando que “cannot be loaded because running scripts is disabled on this system”, que ejecutes Set-ExecutionPoliciy RemoteSigned como administrador desde PowerShell en el Windows Self-Hosted Runner que hemos creado. Por otro lado, y que explico por si acaso no a quedado claro, es que tener una maquina auto hospedada se resumen en: que puedo tener las versiones y las herramientas que a mi me interese y no la larga lista que nos provee las maquinas que nos proporciona GitHub. Y para finalizar, recalco que una buena práctica que se realiza en Azure DevOps, por poner un ejemplo: 1. Un pipeline para crear el artefacto. 2. Una build que se alimenta del artefacto anterior para desplegar. No se puede realizar en la actualidad en GitHub y todo el paso has de realizarlo de una sola vez, ni siquiera puedes poner una orden de pausado para continuar. Por tanto, la implementación del Continuos Deployment o Continuos Delivery no puedes aplicarla sobre un artefacto creado con anterioridad tal y como se hace un Azure DevOps. Esperemos que podamos tener esta funcionalidad en algun momento. Mientras tanto aquí tienes las opciones de triggers que puedes usar: https://docs.github.com/en/actions/reference/events-that-trigger-workflows#external-events
  37. Paso a paso con un ejemplo Paquetes(1/6) Como no podría

    ser de otro modo, GitHub nos servirá como repositorio privado o publico de paquetes, si últimamente has trabajado con paquetes NPM podrás ver que algunos de ellos comienzan a apuntar a GitHub y si no, pues esta será la primera vez que trabajes con ellos. Para ello continuamos con nuestro stack, es decir C# y .NET, con el siguiente repositorio: https://github.com/jmfloreszazo/GitHubActionsWorkshopNugetTesting Existen dos formas de generar un NuGet Package en .NET, o al menos, son las que yo conozco y las que uso: ▪ Usando dotnet Pack. ▪ Usando un fichero nuspec. Contiene un proyecto que es un nuget con dos formas de desplegarlo en GitHub y un proyecto que usa uno de los paquetes desplegados en el repositorio. Voy a pasar por alto la creación de un NuGet, no es el propósito de este Workshop. Pero si que lo es como usarlo. Packages – Paquetes
  38. Paso a paso con un ejemplo Paquetes(2/6) Cuando desplegamos con

    NugetWithDotnetPack.yml: Y con la segunda opción:
  39. Paso a paso con un ejemplo Paquetes(5/6) Y que debes

    hacer para poder usarlo en tu aplicación:
  40. Paso a paso con un ejemplo Paquetes(6/6) Salida de nuestro

    ejemplo (recuerda que en el fichero debes poner tus credenciales cuando lo descargues):
  41. Breve introducción Contenedores Los contenedores de servicio de GitHub son

    básicamente contenedores de Docker creados durante la vida útil de un workflow. Puedes alojar servicios para probar aplicaciones, por ejemplo. Los contenedores se crean y se destruyen en la vida útil del workflow, tal y como decía antes. Por tanto, el contenedor se puede ser utilizado por los Jobs de un mismo workflow. https://docs.github.com/es/actions/guides/about-service-containers Por ejemplo, puedes ve el uso de un contenedor de Redis, por mucho que yo quiera hacer un ejemplo personalizado, este que pongo a continuación explica todo: https://docs.github.com/es/actions/guides/creating-redis-service-containers Es importante que sepas que existen dos formas de usar el mecanismo de comunicación: un Job ejecutándose como un contenedor o un job corriendo directamente en la Runner Machine. Te recomiendo indagar mas en el primer enlace. Como ves es un breve apunte para que sepas que existe y para que puede ayudarte. En tus primeras acciones no creo que pongas en practica esto, pero más adelante puede que en ellos veas una solución a tu problema. Service Containers – Contenedores de servicios
  42. Paso a paso Acciones personalizadas(1/4) Cuando en el mercado de

    acciones no encuentres algo que necesitas, no te quedará más opción que hacerlo tu, así también de paso ayudas a la comunidad. Voy a introducir brevemente las acciones personalizadas. Las acciones, como bien sabes, realizan acciones especificas sobre el workflow. Gracias a las acciones personalizadas podrás interactuar con un repositorio de GitHub via API o con API externas a GitHub para realizar tus acciones. Existen tres tipos de Acciones: ▪ Acciones de Contenedor de Docker. ▪ Acciones de JavaScript. ▪ Acciones hibridas: mezclan Docker y JavaScript. Nos vamos a centrar en el uso de JavaScript para el ejemplo que vamos a realizar. Deberás tener Node y Npm funcionando en tu equipo. Mira si lo tienes instalado: node --version npm --version Custom Action – Crea tus propias acciones personalizadas
  43. Paso a paso Acciones personalizadas(2/4) Te bajas el ejemplo: https://github.com/jmfloreszazo/GitHubActionsWorkshopCustomAction

    O puedes seguir este otro:: https://docs.github.com/en/actions/creating-actions/creating-a-javascript-action Añades un fichero YML llamado action.yml: https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions Si sigues mi ejemplo, usa: npm i -g @vercel/ncc ncc build index.js --license licenses.txt Te permitirá generar la distribución:
  44. Paso a paso Acciones personalizadas(3/4) Antes de nada, debes hacer

    lo siguiente con nuestro repo que nos permitirá poder publicar la versión: Ahora vamos a utilizar esta acción personalizada en otro repo, usamos el primero de todos: https://github.com/jmfloreszazo/GitHubActionsWorkshop/blob/main/.github/workflows/greetings.yml
  45. Paso a paso Acciones personalizadas(4/4) Y nuestro resultado es: Ya

    solo queda que por tu parte investigues como hacer un trabajo con Docker y luego hacer uno hibrido: https://docs.github.com/es/actions/creating-actions/creating-a-docker-container-action