# ⚙️ Como automatizar tu librería en PyPI con GitHub Actions

Continuando con un post anterior donde publicamos nuestra librería a **PyPI,** vamos a ir un paso más allá en el proceso de construcción y despliegue (o, mejor dicho, **build** y **release).** En este tutorial, crearemos nuestro propio **workflow** en **GitHub Actions**, aprovechando que nuestro repositorio ya está en **GitHub,** para automatizar este proceso y hacerlo más eficiente y menos propenso a errores.

Al final de este tutorial tendremos configurado un **workflow** que construye y publica en **PyPI** de forma automática con cada nueva **release** que se cree en el repositorio de **GitHub.**

👉 Si aún no publicaste tu librería en **PyPI**, te recomiendo comenzar por este tutorial previo:

%[https://blog.rafnixg.dev/como-publicar-tu-propia-libreria-de-python-guia-paso-a-paso] 

## ⚙️ ¿Qué es GitHub Actions?

**GitHub Actions** es una plataforma de **CI/CD (Integración y Entrega Continua)** integrada directamente en **GitHub**. Permite automatizar tareas dentro del ciclo de vida de tu proyecto, como ejecutar tests, construir paquetes, y desplegar código, todo sin salir de tu repositorio.

La magia está en los **workflows**, que son conjuntos de instrucciones definidas en archivos **YAML**. Estos **workflows** se ejecutan en respuesta a eventos específicos, como hacer un push, crear un tag, abrir un pull request o crear un nuevo release.

En el caso de una librería de Python, esto nos permite automatizar pasos como:

* Construir los paquetes (`sdist` y `wheel`)
    
* Publicar automáticamente en **PyPI** o **TestPyPI** cuando subimos una nueva versión
    

Con unas pocas líneas de configuración, podemos lograr que todo esto suceda de forma automática cada vez que haces un **release**, reduciendo errores y ganando tiempo.

## 📦 Automatizando la publicación en PyPI con GitHub Actions

### ✅ Requisitos previos

Antes de iniciar, asegúrate de tener:

* Un repositorio de **GitHub** con tu librería
    
* Una cuenta de [**PyPI**](https://pypi.org/)
    
* Un [**API Token**](https://blog.rafnixg.dev/como-publicar-tu-propia-libreria-de-python-guia-paso-a-paso#heading-creando-api-key) generado de tu cuenta de **PyPI**
    

👉 Si ya seguiste el tutorial anterior, deberías tener todo esto listo. Si no, te recomiendo revisarlo antes de seguir.

### 🔐 Agregar tu token de PyPI como secret en GitHub

Debemos configurar en nuestro repositorio un nuevo **secret** para usarlo en los **workflows**, para esto solo debemos seguir los siguientes pasos:

1. Ir a tu repositorio en **GitHub**.
    
2. Hacer clic en **Settings**.
    
3. En el menú lateral, clic en **Secrets and variables → Actions**.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744062734086/d786bef0-ce2d-4134-af17-168dc5463672.png align="center")

4. Clic en **New repository secret**.
    
5. El nombre será `PYPI_API_TOKEN` y en el valor pegá tu **API key** de **PyPI**.
    
6. Clic en **Add secret.**
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744062932644/10d2376a-8a74-406a-be2c-c6ce23db20a5.png align="center")

Esto es lo que usa **GitHub Actions** para autenticar y publicar la librería.

### 📁 Estructura de archivos

Ya con todo esto listo podemos empezar, lo primero que debemos saber es que para que nuestra **Github Actions** funcione los **workflows** deben estar en una carpeta llama `.github/workflows` en la raíz de nuestro repositorio, dentro de esta carpeta ira nuestro archivo de **workflow** llamado `python-publish.yml`.

Podemos crear todo usando los siguientes comandos:

```bash
$ mkdir -p .github/workflows
$ touch .github/workflows/python-publish.yml
```

La estructura de archivos quedaría similar a esta:

```plaintext
rafnixg-lib/
│
├── .github/
│   └── workflows/
│       └── python-publish.yml
├── src/
│   └── rafnixg/
│       ├── __init__.py
│       ├── __main__.py
│       └── rafnixg.py
│
├── LICENSE
├── README.md
└── pyproject.toml
```

## 🚀 Configurando el workflow

Vamos a iniciar creando el contenido de nuestro archivo de **workflow** `python-publish.yml`.

### 🎯Activando el workflow al publicar un release

```yaml
name: Publicar Paquete a PyPI

on:
  release:
    types: [published]
```

Esto indica el nombre que tendrá nuestro **workflow** y también define que solo se va a ejecutar cuando las `release` sean `published` con esto limitamos que solo estas acciones sean las que disparen nuestro **workflow.**

### 🛡️Definir permisos mínimos necesarios

```yaml
permissions:
  contents: read
```

Acá le decimos a GitHub Actions que este workflow solo necesita **leer el contenido del repositorio**. Es una buena práctica limitar los permisos al mínimo necesario.

### 🛠️Crear el job y configurar el entorno

```yaml
jobs:
  deploy:
    runs-on: ubuntu-latest
```

Creamos un **job** llamado `deploy`, que se ejecutará en un entorno virtual Ubuntu. Este **job** va a encargarse de construir y publicar nuestro paquete.

### 📋Agregar los pasos del workflow

Los **steps** son partes del proceso que irán ejecutando de forma secuencia y serán lo que ejecutarán los pasos para publicar nuestra librería en **PyPI** luego de ser construida.

En los **steps** se puede hacer uso de otros **actions** creados por otros usuarios como si de librerías se tratase, solo debemos indicarlo con `uses: actions/nombre-del-actions@version` y tambien podemos usar comando sobre la máquina virtual donde se está ejecutando la **actions** con el comando `run: comando bash`

```yaml
steps:
    - name: Clonando repositorio
      uses: actions/checkout@v3

    - name: Configurando Python 3
      uses: actions/setup-python@v3
      with:
        python-version: '3.x'

    - name: Instalando dependencias
      run: |
        python -m pip install --upgrade pip
        pip install build

    - name: Construir Paquete
      run: python -m build

    - name: Publicar Paquete
      uses: pypa/gh-action-pypi-publish@release/v1
      with:
        user: __token__
        password: ${{ secrets.PYPI_API_TOKEN }}
```

#### 📂 Clonando el repositorio

```yaml
- name: Clonando repositorio
  uses: actions/checkout@v3
```

Usamos la acción oficial para **clonar el código del repositorio**, paso necesario para poder construir el paquete.

#### 🐍 Configurar el entorno de Python

```yaml
- name: Configurando Python 3
  uses: actions/setup-python@v3
  with:
    python-version: '3.x'
```

Le decimos a GitHub Actions que configure un entorno de Python (versión 3.x). Esto es clave para poder instalar dependencias y ejecutar los comandos de build.

#### 📥 Instalando las dependencias necesarias

```yaml
- name: Instalando dependencias
  run: |
    python -m pip install --upgrade pip
    pip install build
```

Actualizamos `pip` y luego instalamos el módulo `build`, que nos permite crear los archivos `.tar.gz` y `.whl` que necesita PyPI.

#### 🏗️Construir el paquete

```yaml
- name: Construir Paquete
  run: python -m build
```

Este paso ejecuta `python -m build`, que genera el paquete listo para ser publicado en la carpeta `dist/`.

#### 📤Publicar el paquete en PyPI

```yaml
- name: Publicar Paquete
  uses: pypa/gh-action-pypi-publish@release/v1
  with:
    user: __token__
    password: ${{ secrets.PYPI_API_TOKEN }}
```

Finalmente, usamos la acción oficial para subir el paquete a [PyPI](https://pypi.org). Aquí usamos el API Key que se guarda como **Secret** en el repositorio de GitHub (`PYPI_API_TOKEN`), para evitar exponer credenciales.

## 📄 **Archivo completo del workflow** (`python-publish.yml`)

El archivo `python-publish.yml` finalmente queda de la siguiente manera:

```yaml
name: Publicar Paquete a PyPI
on:
  release:
    types: [published]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
        - name: Clonando repositorio
          uses: actions/checkout@v3
    
        - name: Configurando Python 3
          uses: actions/setup-python@v3
          with:
            python-version: '3.x'
    
        - name: Instalando dependencias
          run: |
            python -m pip install --upgrade pip
            pip install build
    
        - name: Construir Paquete
          run: python -m build
    
        - name: Publicar Paquete
          uses: pypa/gh-action-pypi-publish@release/v1
          with:
            user: __token__
            password: ${{ secrets.PYPI_API_TOKEN }}
```

## 💾 Subiendo los cambios al repositorio

Ya con nuestro archivo creado, solo nos resta subir un **commit** con los cambios que tenemos:

```bash
$ git add .github/workflows/python-publish.yml
$ git commit -m "Add GitHub Actions workflow for PyPI release"
$ git push origin
```

Creamos un **tag** y lo subimos:

```bash
$ git tag v1.0.1
$ git push origin --tags
```

Revisamos nuestro repositorio de **GitHub** y nos aseguramos de que se subió todo correctamente, para seguir con la creación de una nueva **release**.

### 🏷️ Crear una nueva release en GitHub

En nuestro repositorio hacemos clic en **Releases.** para ingresar a la página de todas las **releases** que vamos a haciendo.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744064075909/cb1a102c-99cf-48f6-8a2e-75921cb6a725.png align="center")

En esta página hacemos clic en **“draf a new release“**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744064165133/3071befb-dd18-4bd1-a46c-fb9bf8508299.png align="center")

Esto nos abre el formulario para crear nuevas releases, acá debemos seleccionar el tag correspondiente con la release en **“Choose a tag“** y hacer clic en **“Generate release notes“,** esto genera una descripción usando los **commits** y los **Pull Request.**

Por último, revisa la descripción ajusta si lo necesitas y dale clic a **“Publish release“.**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744064263542/5fbdee1e-64b2-405d-987a-4e96c7f2df79.png align="center")

## ✅ Verificar el resultado de la publicación

Cada vez que crees una nueva **release**, se publicará una nueva versión de tu paquete en **PyPI**. En la tab **“Actions“** de nuestro repositorio vamos a ver cómo se ejecuta el **workflow** que lo publica.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744064448660/629e622d-d515-42e0-92ed-64b5f214d844.png align="center")

## 🧠 **Conclusión**

Automatizar la publicación con **GitHub Actions** no solo te ahorra tiempo, también reduce errores y hace que todo el proceso sea mucho más limpio y profesional. Una vez configurado, solo tienes que enfocarte en mejorar tu librería y dejar que la publicación se maneje sola ✨

## 🔍Referencias

#### 🔗 Documentación oficial GitHub Actions

* [https://docs.github.com/en/actions/about-github-actions/understanding-github-actions](https://docs.github.com/en/actions/about-github-actions/understanding-github-actions)
    

#### 🔗 Documentación oficial PyPI usando GitHub Actions

* [https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows](https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows)
    

#### 🔗 Documentacion del GitHub Actions PyPI Publish

* [https://github.com/pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish)
