Parte 1 : Impostare un pipeline CI/CD semplice per Python e PyPI
Publié le 17 July 2025
Introduzione
L’automazione dei processi di sviluppo è diventata indispensabile nei progetti moderni. Un pipeline CI/CD ben progettato non solo permette di individuare le regressioni presto nel ciclo di sviluppo, ma anche di automatizzare completamente il processo di distribuzione.
In questo articolo, esploreremo come configurare una pipeline completa con GitHub Actions per un’applicazione Python, dall’integrazione continua (CI) al deployment continuo (CD) su PyPI.
Architettura della pipeline
Il nostro pipeline è composto da due workflows distinti:
-
Pipeline CI: Eseguito su ogni push e pull request
-
pipeline CD: Attivato solo durante le release di GitHub
Configurazione della Pipeline di Integrazione Continua (CI)
Struttura del workflow CI
Il workflow CI è progettato per validare ogni contributo al codice. Ecco la sua configurazione completa:
name: CI/CD Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install ruff pytest pytest-mock
- name: Run Linting (Ruff)
run: ruff check .
- name: Run Tests (Pytest)
run: pytest
Analisi delle fasi CI
1. Trigger (on)
----
----
on:
push:
branches:
- main
pull_request:
branches:
- main
----
Il pipeline si attiva su : - Ogni push sul ramo`main` - Ogni pull request verso`main`
Questo approccio garantisce che il codice principale rimanga stabile e che ogni contributo venga convalidato prima dell'integrazione.
==== 2. Ambiente di esecuzione
----
runs-on: ubuntu-latest
Ubuntu Latest offre un buon compromesso tra prestazioni, costo e compatibilità per la maggior parte dei progetti Python.
==== 3. Checkout del Codice
```yaml
----
----
- name: Checkout code
uses: actions/checkout@v4
----
L'azione`checkout@v4`recupera il codice sorgente del repository. La versione v4 apporta miglioramenti delle prestazioni e della sicurezza.
==== 4. Configurazione Python
----
- name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x'
L'utilizzo di`'3.x'`consente di utilizzare automaticamente l'ultima versione stabile di Python 3, semplificando la manutenzione.
==== 5. Installazione delle Dipendenze
```yaml
----
----
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install ruff pytest pytest-mock
----
Questa fase: - Aggiorna pip all'ultima versione - Installa le dipendenze del progetto - Aggiungi gli strumenti di sviluppo (linting e test)
==== 6. Linting con Ruff
----
- name: Run Linting (Ruff) run: ruff check .
**Ruff**è un linter Python ultra-veloce scritto in Rust. Combina le funzionalità di diversi strumenti (Flake8, Black, isort) in un unico strumento performante.
==== 7. Esecuzione dei test
```yaml
----
----
- name: Run Tests (Pytest)
run: pytest
----
Pytest esegue l'intera suite di test, garantendo che le modifiche non introducano regressioni.
== Configurazione del pipeline di distribuzione (CD)
=== Struttura del flusso di lavoro CD
Il workflow CD si attiva solo durante le release di GitHub e automatizza la pubblicazione su PyPI:
[source,yaml]
----
name: Publish to PyPI
on:
release:
types:
- published
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
----
=== Analisi delle fasi CD
==== 1. Scatto Rilascio
----
on: release: types: - published
Il pipeline CD si attiva solo alla pubblicazione di una release GitHub. Questo approccio assicura un controllo preciso dei deployment.
==== 2. Installazione degli Strumenti di Build
```yaml
----
----
pip install setuptools wheel twine
----
- **setuptools**: Strumenti di packaging Python - **wheel**: Formato di distribuzione Python moderno - **spago**: Strumento sicuro per caricare su PyPI
==== 3. Configurazione dell'autenticazione
----
env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
L'autenticazione utilizza un token API PyPI memorizzato come segreto GitHub, più sicuro delle credenziali tradizionali.
==== 4. Build e Pubblicazione
```yaml
----
----
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
----
- `sdist`: Crea una distribuzione sorgente - `bdist_wheel`: Crea una wheel (distribuzione binaria) - `twine upload`Pubblica le distribuzioni su PyPI
== Configurazione del pacchetto Python
=== Struttura di setup.py
Per far funzionare il pipeline, il tuo progetto deve includere un file`setup.py`</think>
:
[source,python]
----
from setuptools import setup, find_packages
with open("README.adoc", "r", encoding="utf-8") as fh:
long_description = fh.read()
setup(
name="playlist-downloader",
version="1.0.0",
author="Votre Nom",
author_email="[email protected]",
description="CLI tool for managing YouTube playlists",
long_description=long_description,
long_description_content_type="text/plain",
url="https://github.com/cheroliv/playlist-downloader",
packages=find_packages(),
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
],
python_requires=">=3.8",
install_requires=[
"typer>=0.9.0",
"yt-dlp>=2023.1.6",
"google-api-python-client>=2.70.0",
"google-auth-oauthlib>=0.7.1",
"pymonad>=2.4.0",
"pyyaml>=6.0",
],
entry_points={
"console_scripts": [
"playlist-downloader=cli:app",
],
},
)
----
=== Punti chiave del setup.py
1. **Metadati**Nome, versione, autore, descrizione
2. **Dipendenze**Elenco dei pacchetti richiesti
3. **Punti di ingresso**: Comandi CLI esposti
4. **classificatori**: Metadati per PyPI
== Sicurezza con i GitHub Secrets
=== Configurazione del Token PyPI
1. **Creare un token API su PyPI** :
- Accedi a PyPI - Vai in Account Settings > API tokens - Crea un nuovo token con le autorizzazioni appropriate
1. **Aggiungere il segreto in GitHub** :
- Impostazioni del repository > Segreti e variabili > Azioni - Crea un nuovo segreto chiamato`PYPI_API_TOKEN` - Incolla il tuo token PyPI
[plantuml, secrets-flow, svg]
----
@startuml
!theme plain
actor Developer as dev
participant "Repository GitHub" as repo
participant "GitHub Actions" as actions
participant PyPI
dev -> repo : Configure PYPI_API_TOKEN secret
repo -> actions : Trigger CD pipeline on release
actions -> actions : Access secret securely
actions -> PyPI : Authenticate with token
PyPI -> PyPI : Validate and publish package
note over actions, PyPI
Token never exposed in logs
Automatic rotation possible
end note
@enduml
----
== Workflow di Distribuzione Completa
=== Sequenza di distribuzione
[plantuml, deployment-sequence, svg]
----
@startuml
!theme plain
actor Developer as dev
participant "Git locale" as git
participant "GitHub" as github
participant "GitHub Actions" as actions
participant PyPI
participant "Utenti finali" as users
dev -> git : git tag v1.0.0
dev -> git : git push origin v1.0.0
git -> github : Push tag
dev -> github : Create release from tag
github -> actions : Trigger CD pipeline
actions -> actions : Checkout code
actions -> actions : Setup Python environment
actions -> actions : Install build tools
actions -> actions : Build distributions (sdist + wheel)
actions -> PyPI : Upload to PyPI with token
PyPI -> PyPI : Validate and publish
users -> PyPI : pip install playlist-downloader
note over dev, github
Release creation can be automated
or done manually through GitHub UI
end note
@enduml
----
=== Stati del pipeline
[plantuml, pipeline-states, svg]
----
@startuml
!theme plain
[*] --> Idle
Idle --> CI_Running : Push/PR created
CI_Running --> CI_Success : All checks pass
CI_Running --> CI_Failed : Linting/Tests fail
CI_Success --> Idle : Merge completed
CI_Failed --> Idle : Fix and retry
Idle --> CD_Running : Release published
CD_Running --> CD_Success : Package published
CD_Running --> CD_Failed : Build/Upload error
CD_Success --> Idle : Package available on PyPI
CD_Failed --> Idle : Fix and retry release
note on link #red : Blocks merge
note on link #green : Allows deployment
@enduml
----
== Buone Pratiche e Ottimizzazioni
=== 1. Gestione delle Versioni
Utilizza tag Git semantici :
----
git tag -a v1.2.3 -m "Release version 1.2.3" git push origin v1.2.3
=== 2. Test di matrice
Per testare su più versioni di Python:
```yaml
----
----
strategy:
matrix:
python-version: [3.8, 3.9, "3.10", "3.11"]
----
=== 3. Cache delle Dipendenze
Accelerate i build con la cache :
----
- name: Cache pip dependencies uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
=== 4. Ambienti di distribuzione
Utilizza gli ambienti GitHub per le distribuzioni controllate:
```yaml
----
----
jobs:
deploy:
environment: production
runs-on: ubuntu-latest
----
== Caso d'uso e architettura
=== Diagramma dei casi d'uso
[plantuml, use-cases, svg]
----
@startuml
!theme plain
left to right direction
actor "Sviluppatore" as dev
actor "GitHub Actions" as ga
actor "utente finale" as user
package "Sistema CI/CD" {
usecase "Esegui il linting" as lint
usecase "Esegui i test" as test
usecase "Costruisci pacchetto" as build
usecase "Pubblica su PyPI" as publish
usecase "Crea Release" as release
}
dev --> lint : Pushes code
dev --> test : Pushes code
dev --> release : Creates release
ga --> build : On release trigger
ga --> publish : After successful build
user --> publish : Downloads package
lint .> test : triggers
test .> build : on success
build .> publish : on success
@enduml
----
=== Architettura del deployment
[plantuml, deployment-architecture, svg]
----
@startuml
!theme plain
cloud "GitHub" {
[Source Repository]
[GitHub Actions]
[Secrets Store]
}
cloud "PyPI" {
[Package Registry]
[Distribution Files]
}
node "CI/CD Pipeline" {
[Linting]
[Testing]
[Building]
[Publishing]
}
[Source Repository] --> [GitHub Actions] : Triggers
[GitHub Actions] --> [Linting]
[Linting] --> [Testing]
[Testing] --> [Building]
[Building] --> [Publishing]
[Publishing] --> [Package Registry] : Uploads
[Secrets Store] --> [Publishing] : Provides token
note as N1
Secure token-based
authentication
end note
[Secrets Store] .. N1
@enduml
----
== Monitoraggio e debugging
=== Log e Monitoraggio
GitHub Actions fornisce log dettagliati per ogni passaggio. Per il debug:
1. **Esamini i log**di ogni step
2. **Attiva il debug**con`ACTIONS_STEP_DEBUG`
3. **Utilizza gli artefatti**per salvare i file di build
----
- name: Upload build artifacts uses: actions/upload-artifact@v3 if: failure() with: name: build-logs path: build/
=== Notifiche
Aggiungi notifiche Slack o email :
```yaml
----
----
- name: Notify on failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
----
== Conclusione
L'implementazione di un pipeline CI/CD robusto con GitHub Actions trasforma radicalmente l'esperienza di sviluppo. Automatizzando il linting, i test e il deployment, tu:
- **Riduci gli errori**in produzione - **Accelerate i cicli**di sviluppo - **Migliorate la fiducia**nei vostri rilasci - **Facilita la collaborazione**in squadra
Questo pipeline può essere adattato a diversi tipi di progetti Python regolando gli strumenti di linting, i framework di test o le destinazioni di distribuzione.
L'investimento iniziale nella configurazione di questi flussi di lavoro è rapidamente ripagato dal guadagno di tempo e dalla riduzione degli errori manuali durante i rilasci.
== Risorse Complementari
- https://docs.github.com/en/actions[Documentazione GitHub Actions] - https://packaging.python.org/[Python Packaging Guide] - https://docs.pytest.org/[Documentazione Pytest] - https://docs.astral.sh/ruff/[Documentazione Ruff] - https://twine.readthedocs.io/[Documentazione Twine]
✅ Pipeline funzionale raggiunto ! Ora hai un semplice pipeline CI/CD che ti permette di automatizzare i test e di pubblicare il tuo pacchetto Python su PyPI direttamente da GitHub Actions.
Tuttavia, questo pipeline rimane volontariamente minimalista. Non copre ancora alcuni aspetti indispensabili in un contesto professionale:
Test di più versioni di Python
Analisi di sicurezza automatica,
Distribuzione progressiva tramite Test PyPI,
Monitoraggio e metriche della pipeline,
Automazione del versioning e integrazione delle buone pratiche moderne (pyproject.toml).
Nella prossima parte, passeremo al livello successivo. Imparerai a trasformare questo pipeline di base in una vera catena di distribuzione industriale, robusta e sicura, pronta per progetti Python di produzione.
----
Articoli correlati
14 May 2026