Saltar a contenido

Actions

GitHub Actions

GitHub Actions es una herramienta que nos permite automatizar tareas. Se pueden crear workflows que se ejecutan cuando se produce un evento, como un push a una rama, un pull request o un issue. Los workflows se pueden crear en un fichero de texto plano, en YAML, que se almacena en el repositorio. Los podemos ejecutar en un entorno de ejecución, que puede ser un contenedor de Docker o una máquina virtual.

Ejemplo de un workflow

name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
    build:

    runs-on: ubuntu-latest

    steps:
        - uses: actions/checkout@v2
        - name: Run a one-line script
    run: echo Hello, world!
        - name: Run a multi-line script
    run: |
        echo Add other actions to build,
        echo test, and deploy your project.

En este repositorio vamos a tener varias actions distintas, que vamos a ver a continuación.

GitHub Pages

GitHub Pages es una herramienta que nos permite alojar páginas web estáticas en GitHub. Esto se puede hacer de dos formas, mediante una GitHub Action o mediante una rama de un repositorio. En este caso, vamos a ver cómo se puede hacer mediante el uso de una GitHub Action.

Para poder utilizar GitHub Pages en nuestro repositorio mediante el uso de una action, tendremos que habilitar la opción de GitHub Pages en el apartado de Settings del repositorio. Look:

GitHub Pages Settings

Una vez habilitada la opción de GitHub Pages, tendremos que crear un fichero de configuración en el directorio .github/workflows del repositorio. Este fichero de configuración se va a llamar pages.yml y va a tener el siguiente contenido:

name: Publish to GitHub Pages

on:
  # Esta acción se ejecuta cuando se hace un push a la rama main
  push:
    branches: ["main"]

permissions:
  # Esta acción necesita permisos para escribir en el repositorio
  contents: read
  pages: write
  id-token: write

jobs:
  # Tarea para hacer un build de la página web
  create-docs:
    environment: Deploy docs
    runs-on: ubuntu-latest
    steps:
      # Empezamos clonando el repositorio en la máquina virtual que se encargará de todo
      # y luego instalamos las dependencias necesarias para hacer el build.
      - name: Checkout
        uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.9
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r mkdocs-requirements.txt
      # Una vez instaladas las dependencias, hacemos el build de la página web y la subimos
      - name: Build docs
        run: mkdocs build
      - name: Setup Pages
        uses: actions/configure-pages@v2
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v1
        with:
          path: './site'
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v1

Cabe destacar que este fichero de configuración publicará una web que tengamos escrita con ficheros Markdown en el directorio docs. Si queremos publicar una web escrita con otro lenguaje, tendremos que modificar el fichero de configuración. A parte, vamos a utilizar una herramienta llamada mkdocs para generar la web a partir de los ficheros Markdown, además de utilizar un tema llamado Material for MkDocs.

Para poder utilizar mkdocs y Material for MkDocs, tendremos que crear un fichero de configuración llamado mkdocs.yml en el directorio principal del repositorio. Este fichero de configuración tendrá el siguiente contenido:

# mkdocs.yml - https://realpython.com/python-project-documentation-with-mkdocs

site_name: Formación en GitHub
theme:
  name: material
  features:
    - content.code.annotate
  palette:
    primary: blue
    accent: orange
  font:
    text: Exo 2
    code: Hack
  language: es
  favicon: images/Isotipo-DEEPSUA_SinFondo.png
  logo: images/Isologo-DEEPSUA_SinFondo.png

plugins: 
  - mkdocstrings:
      handlers:
        python:
          options:
            show_source: true
            show_root_heading: true
            heading_level: 3

markdown_extensions:
  - pymdownx.highlight:
      anchor_linenums: 
        true
  - pymdownx.superfences
  - pymdownx.inlinehilite
  - pymdownx.snippets
  - toc:
      permalink: true
      toc_depth: 6
      baselevel: 2
  - tables

nav:
  - Home: index.md
  - Introducción: intro.md
  - Projects: projects.md
  - Actions: actions.md

extra: 
  generator: false

Además, tendremos que crear un fichero llamado mkdocs-requirements.txt en el directorio principal del repositorio. Este fichero de configuración tendrá el siguiente contenido:

mkdocs
mkdocs-material
mkdocstrings
mkdocstrings-python

Simplemente, cuando llegue un push a la rama main, se ejecutará la action que hemos creado y se publicará la web en GitHub Pages.

Code Testing

En este apartado vamos a ver cómo podemos utilizar GitHub Actions para ejecutar tests en nuestro repositorio. Para ello, vamos a utilizar la herramienta unittest.

En la raiz del repositorio tenemos una carpeta llamada src que contiene la clase Triangulo. Esta clase tiene un método llamado tipoTriangulo que nos dice el tipo de triángulo que es, en función de la longitud de los lados que le enviemos por parámetros. Por otra parte, tenemos una carpeta llamada tests que contiene los tests que vamos a ejecutar para comprobar que el método tipoTriangulo funciona correctamente.

Si nos situamos en la raiz del repositorio, podemos ejecutar los tests con el siguiente comando:

python -m unittest discover -s tests -p "test*.py"

Este comando nos va a ejecutar todos los tests que estén en el directorio tests y que empiecen por test. Si queremos ejecutar un test en concreto, podemos ejecutar el siguiente comando:

python -m unittest tests/test_Triangulo.py

Para poder ejecutar los tests con GitHub Actions, tendremos que crear un fichero de configuración en el directorio .github/workflows del repositorio. Este fichero de configuración se va a llamar tests.yml y va a tener el siguiente contenido:

name: Tests

on:
  push:
    branches: ["main"]
  pull_request:
    branches: ["main"]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.9
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      - name: Run tests
        run: python -m unittest discover -s tests -p "test*.py"

Este fichero de configuración va a ejecutar los tests cada vez que se haga un push a la rama main o cuando se haga un pull request a la rama main. Además, va a ejecutar los tests en una máquina virtual con Ubuntu.

Estiquetado de versiones

Cuando tumbamos código de la rama de desarrollo a la rama principal, es muy importante etiquetar la versión del código que estamos subiendo. Esto nos va a permitir tener un control de las versiones que vamos subiendo a la rama principal.

Para realizar esto, utilizamos los tags de Git. Para crear un tag en Git, podemos ejecutar el siguiente comando:

git tag -a v1.0.0 -m "Versión 1.0.0"

Este comando va a crear un tag llamado v1.0.0 en el repositorio. Si queremos ver todos los tags que tenemos en el repositorio, podemos ejecutar el siguiente comando:

git tag

Si queremos subir el tag a GitHub, podemos ejecutar el siguiente comando:

git push origin v1.0.0

En este repositorio vamos a tener un fichero YAML llamado 'auto-prerelease.yml' en el directorio '.github/workflows'. Este fichero de configuración va a crear un tag cada vez que se haga un push a la rama main.

Este tag siempre será una pre-release. Esto significa que no será una versión estable, sino que será una versión de desarrollo que tiene pendiente alguna mejora o corrección antes de ser etiquetada como una versión estable.

El fichero de configuración tendrá el siguiente contenido:

name: "pre-release"

on:
  push:
    branches:
      - "main"
      - "develop"

permissions:
  id-token: "write"
  contents: "write"
  packages: "write"
  pull-requests: "read"

jobs:
  pre-release:
    name: "Pre Release"
    runs-on: "ubuntu-latest"

    steps:
      - name: "Build & test"
        run: |
          echo "done!"

      - uses: "marvinpinto/action-automatic-releases@latest"
        with:
          repo_token: "${{ secrets.GITHUB_TOKEN }}"
          automatic_release_tag: "latest"
          prerelease: true
          title: "Development Build"

En el caso de que queramos etiquetar una versión estable, se subirá un tag a la rama main y se creará una nueva release en GitHub. Para ello vamos a utilizar el fichero de configuración auto-release.yml que se encuentra en el directorio de los workflows.

Este fichero de configuración tendrá el siguiente contenido:

name: "auto-release"

on:
  push:
    tags:
      - "v*"

permissions:
  id-token: "write"
  contents: "write"
  packages: "write"
  pull-requests: "read"

jobs:
  tagged-release:
    name: "Tagged Release"
    runs-on: "ubuntu-latest"

    steps:
      - name: "Build & test"
        run: |
          echo "done!"

      - uses: "marvinpinto/action-automatic-releases@latest"
        with:
          repo_token: "${{ secrets.GITHUB_TOKEN }}"
          prerelease: false

Easter egg: Avisos por Telegram

Mediante el uso de las GitHub Actions podemos enviar avisos por un chat de Telegram cada vez que se monte una nueva release. Para ello, vamos a utlizar el fichero de configuración telegram.yml que se encuentra en el directorio de los workflows.

También se podría configurar para otros menesteres como avisar cuando se abre una nueva issue o cuando se hace un pull request, entre otras cosas.

Este fichero de configuración tendrá el siguiente contenido:

name: notify-on-telegram
on:
  workflow_run:
    workflows: ["auto-release"]
    types:
      - completed

permissions:
  id-token: "write"
  contents: "write"
  packages: "write"
  pull-requests: "read"

jobs:
  send_message:
    name: "Notify on Telegram"
    runs-on: "ubuntu-latest"
    steps:
      - name: "Send Telegram message"
        uses: "appleboy/telegram-action@master"
        with:
          to: "${{ secrets.TELEGRAM_TO }}"
          token: "${{ secrets.TELEGRAM_TOKEN }}"
          format: "html"
          dissable_web_page_preview: true
          message: |
            "¡Actualizaciones en <strong>${{ github.event.repository.name }}</strong>!
            Se ha publicado una versión nueva: <code>${{ github.ref_name }}</code>.
            Puedes ver los cambios en el <a href="https://github.com/${{ github.repository }}/releases/latest">changeLog</a>. 
            Además, no te olvides de consultar la <a href="https://deepsua.github.io/formacion_github/">documentación</a>."

De este fichero de configuración cabe destacar el uso de dos variables de entorno que se encuentran en el apartado de Settings de nuestro repositorio. Estas variables son: - TELEGRAM_TO: Es el identificador del chat de Telegram al que queremos enviar los mensajes. - TELEGRAM_TOKEN: Es el token de nuestro bot de Telegram.

Estas variables se crean en el apartado de Settings de nuestro repositorio. Para crearlas, vamos a Settings > Security > Secrets and variables > Actions y le damos click al botón New repository secret.

Para obtener el token de nuestro bot de Telegram, vamos a BotFather y le damos click al botón New bot. Le damos un nombre al bot y le damos click al botón Create bot. Una vez creado el bot, le damos click al botón API Token y copiamos el token que aparece en la pantalla.

Para obtener el identificador del chat de Telegram al que queremos enviar los mensajes, podemos utilizar el siguiente script con Python:

# Instalar la librería python-telegram-bot: pip3 install python-telegram-bot
# Guardar el script en un fichero llamado telegram.py
from telegram.ext import Updater, CommandHandler, CallbackContext
from telegram import Update

updater = Updater(token='TOKEN', use_context=True) # TOKEN es el token de nuestro bot de Telegram
dp = updater.dispatcher
jq = updater.job_queue

def currentChatInfo(update: Update, context: CallbackContext):
    update.effective_message.reply_text(update.effective_chat.to_json())

dp.add_handler(CommandHandler('currentChatInfo', currentChatInfo))

updater.start_polling()
updater.idle()
# Ejecutar el script: python3 telegram.py
# Escribir al bot de Telegram: /currentChatInfo

Una vez ejecutado el script, escribimos al bot de Telegram el comando /currentChatInfo y nos devolverá un JSON con la información del chat. En este JSON, el campo id es el identificador del chat de Telegram al que queremos enviar los mensajes.

Tenemos el script de Python en el directorio src del repositorio.

Ahora ya sabes como crear un bot de Telegram y tienes un modelo muy básico para hacer pruebas. ¡Te animo a que juegues con Python y Telegram para crear tus propios bots!

Una vez añadimos las variables de entorno, ya podemos hacer etiquetar una nueva versión en el repositorio y ver como se envía un mensaje al chat de Telegram.