Inleiding

Doel : Begeleid de lezer bij het opzetten van een minimalistisch maar functioneel CI/CD-pipeline voor een Python-toepassing, met automatische implementatie op PyPI.

De automatisering van ontwikkelprocessen is onmisbaar geworden in moderne projecten. Een goed ontworpen CI/CD-pipeline maakt niet alleen het vroegtijdig opsporen van regressies in de ontwikkelingscyclus mogelijk, maar automatiseert ook volledig het implementatieproces.

In dit artikel gaan we onderzoeken hoe we een volledig pipeline kunnen instellen met GitHub Actions voor een Python-applicatie, van continue integratie (CI) naar continue levering (CD) op PyPI.

pipeline-architectuur

Onze pipeline bestaat uit twee verschillende workflows:

  1. CI-pijplijn: Uitgevoerd bij elke push en pull request

  2. CD-pijplijn: Wordt alleen geactiveerd bij GitHub-releases

@startuml
!theme plain

package "GitHub-opslagplaats" {
  [Source Code] as source
  [GitHub Actions] as actions
}

package "CI-pipeline" {
  [Checkout] as checkout_ci
  [Setup Python] as python_ci
  [Install Dependencies] as deps_ci
  [Linting (Ruff)] as lint
  [Unit Tests] as tests
}

package "CD Pipeline" {
  [Checkout] as checkout_cd
  [Setup Python] as python_cd
  [Build Package] as build
  [Publish to PyPI] as pypi
}

source --> actions : Push/PR
actions --> checkout_ci
checkout_ci --> python_ci
python_ci --> deps_ci
deps_ci --> lint
lint --> tests

source --> actions : Release
actions --> checkout_cd : On Release
checkout_cd --> python_cd
python_cd --> build
build --> pypi

@enduml

Configuratie van de CI-pipeline (Continuous Integration)

Structuur van de CI-workflow

De CI-workflow is ontworpen om elke bijdrage aan de code te valideren. Dit is de volledige configuratie:

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

Analyse van de CI-stappen

1. Triggers (on)

----
----
----
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
----

De pipeline wordt geactiveerd op : - Elke push op de tak`main` - Elk pull request naar`main`

Deze aanpak garandeert dat de hoofdbroncode stabiel blijft en dat elke bijdrage wordt gevalideerd voordat deze wordt geïntegreerd.

==== 2. Uitvoeringsomgeving

----

runs-on: ubuntu-latest

Ubuntu Latest biedt een goed compromis tussen prestaties, kosten en compatibiliteit voor de meeste Python-projecten.

==== 3. Uitchecken van de code

----

- name: Checkout code uses: actions/checkout@v4

De actie`checkout@v4`haalt de broncode van de repository op. Versie v4 brengt verbeteringen aan op het gebied van prestaties en beveiliging.

==== 4. Configuratie van Python

- name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x'

Het gebruik van`'3.x'`stelt automatisch gebruik mogelijk van de nieuwste stabiele versie van Python 3, waardoor onderhoud wordt vereenvoudigd.

==== 5. Installatie van afhankelijkheden

```yaml
----
----
----
- name: Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
    pip install ruff pytest pytest-mock
----

Deze stap: - Werk pip bij naar de nieuwste versie - Installeer de afhankelijkheden van het project - Voeg de ontwikkeltools (linting en tests) toe

==== 6. Linting met Ruff

----

- name: Run Linting (Ruff) run: ruff check .

**Ruff**is een ultrasnelle Python-linter geschreven in Rust. Het combineert de functionaliteiten van meerdere tools (Flake8, Black, isort) in één krachtig hulpmiddel.

==== 7. Uitvoering van de tests

----

- name: Run Tests (Pytest) run: pytest

Pytest voert de volledige testsuite uit, waardoor wordt gegarandeerd dat wijzigingen geen regressies introduceren.

== Configuratie van de implementatie-pipeline (CD)

=== Structuur van de CD-workflow

De CD-workflow wordt alleen geactiveerd bij GitHub-releases en automatiseert de publicatie naar 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/*
----

=== Analyse van de stappen CD

==== Trigger Release

on: release: types: - published

De CD-pipeline wordt alleen geactiveerd bij de publicatie van een GitHub-release. Deze aanpak garandeert een nauwkeurige controle van de implementaties.

==== 2. Installatie van de Build-tools

```yaml
----
----
----
pip install setuptools wheel twine
----

- **setuptools**: Tools voor Python packaging - **wiel**: Moderne Python-distributieformaat - **touw**: Veilig hulpmiddel voor het uploaden naar PyPI

==== 3. Configuratie van Authenticatie

----

env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}

Authenticatie gebruikt een PyPI API‑token dat is opgeslagen als een GitHub‑secret, wat veiliger is dan de traditionele referenties.

==== 4. Bouw en Publicatie

----

run: | python setup.py sdist bdist_wheel twine upload dist/*

- `sdist`: Maak een broncode-distributie - `bdist_wheel`: Maak een wheel (binaire distributie) - `twine upload`: Publiceert de distributies op PyPI

== Configuratie van het Python-pakket

=== Structuur van setup.py

Om de pijplijn te laten werken, moet uw project een bestand bevatten`setup.py`:

[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",
        ],
    },
)
----

=== Sleutelpunten van setup.py

1. **Metadata**Naam, versie, auteur, beschrijving
2. **afhankelijkheden**Lijst van vereiste pakketten
3. **Invoerpunten**: Blootgestelde CLI-opdrachten
4. **classificatoren**: metadata voor PyPI

== Beveiliging met de GitHub Secrets

=== Configuratie van het PyPI-token

1. **Maak een API-token aan op PyPI** :

- Log in bij PyPI - Ga naar Account Settings > API tokens - Maak een nieuwe token met de juiste machtigingen

1. **Voeg het geheim toe in GitHub**:

- Repository-instellingen > Geheimen en variabelen > Acties - Maak een nieuw geheim met de naam`PYPI_API_TOKEN` - Plak je PyPI-token

[plantuml, secrets-flow, svg]
----
@startuml
!theme plain

actor Developer as dev
participant "GitHub-opslagplaats" 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
----

== Volledig implementatiewerkstroom

=== Implementatiereeks

[plantuml, deployment-sequence, svg]
----
@startuml
!theme plain

actor Developer as dev
participant "Lokale Git" as git
participant "GitHub" as github
participant "GitHub Actions" as actions
participant PyPI
participant "Eindgebruikers" 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
----

=== Staten van de 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
----

== Goede Praktijken en Optimalisaties

=== 1. Versiebeheer

Gebruik semantische Git-tags:

git tag -a v1.2.3 -m "Release version 1.2.3" git push origin v1.2.3

=== 2. Matrix-tests

Om te testen op meerdere versies Python:

1. **Een API-token maken op PyPI**Please provide the text you would like me to translate.

jobs: deploy: environment: production runs-on: ubuntu-latest

== Gebruikscase en Architectuur

=== Gebruikscasendiagram

[plantuml, use-cases, svg]
----
@startuml
!theme plain

left to right direction

actor "Ontwikkelaar" as dev
actor "GitHub Actions" as ga
actor "Eindgebruiker" as user

package "CI/CD-systeem" {
  usecase "Linting uitvoeren" as lint
  usecase "Tests uitvoeren" as test
  usecase "Pakket bouwen" as build
  usecase "Publiceer naar PyPI" as publish
  usecase "Release maken" 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
----

=== Architectuur van de implementatie

[plantuml, deployment-architecture, svg]
----
@startuml
!theme plain

cloud "GitHub" {
  [Source Repository]
  [GitHub Actions]
  [Secrets Store]
}

cloud "PyPI" {
  [Package Registry]
  [Distribution Files]
}

node "CI/CD-pijplijn" {
  [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
----

== Monitoring en Debugging

=== Logboeken en Monitoring

GitHub Actions levert gedetailleerde logs voor elke stap. Voor debuggen :

1. **Controleer de logs**van elke stap
2. **Activeer de debug**met`ACTIONS_STEP_DEBUG`
3. **Gebruik artefacten**om de buildbestanden op te slaan

- name: Upload build artifacts uses: actions/upload-artifact@v3 if: failure() with: name: build-logs path: build/

=== Meldingen

Voeg Slack- of e-mailnotificaties toe :

```yaml
----
----
----
- name: Notify on failure
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    webhook_url: ${{ secrets.SLACK_WEBHOOK }}
----

== Conclusie

Het opzetten van een robuuste CI/CD-pipeline met GitHub Actions verandert de ontwikkelervaring radicaal. Door linting, tests en implementatie te automatiseren, jij :

- **Verminder de fouten**in productie - **Versnel de cycli**van ontwikkeling - **Verbeter het vertrouwen**in uw releases - **Faciliteer de samenwerking**in een team

Deze pijplijn kan worden aangepast aan verschillende soorten Python-projecten door de linttools, testframeworks of implementatiedoelen aan te passen.

De initiële investering in de configuratie van deze workflows wordt snel terugverdiend dankzij de tijdsbesparing en de vermindering van handmatige fouten tijdens de implementaties.

== Aanvullende Bronnen

- https://docs.github.com/en/actions[Documentatie GitHub Actions] - https://packaging.python.org/[Python-verpakkinggids] - https://docs.pytest.org/[Documentatie Pytest] - https://docs.astral.sh/ruff/[Documentatie Ruff] - https://twine.readthedocs.io/[Documentatie Twine]

✅ Werkende pijpleiding bereikt ! Je hebt nu een eenvoudige CI/CD-pijplijn waarmee je je tests kunt automatiseren en je Python-pakket direct vanaf GitHub Actions naar PyPI kunt publiceren.

Desondanks blijft deze pijplijn bewust minimalistisch. Hij dekt nog niet alle essentiële aspecten die in een professionele context vereist zijn:

Tests van meerdere versies van Python,

automatische veiligheidsanalyse

Progressieve uitrol via Test PyPI,

Monitoring en metrieken van de pijplijn,

Automatisering van versiebeheer en integratie van moderne best practices (pyproject.toml).

In het volgende gedeelte gaan we naar de volgende stap. Je leert hoe je deze basis-pipeline kunt omvormen tot een echte industriële implementatieketen, robuust en veilig, klaar voor productie-Pythonprojecten.
----
----

Om te testen op meerdere Python-versies:

```yaml
----
strategy:
  matrix:
    python-version: [3.8, 3.9, "3.10", "3.11"]
----

=== 3. Cache van Afhankelijkheden

Versnel builds met de cache:

```yaml
----
- name: Cache pip dependencies
  uses: actions/cache@v3
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
----

=== 4. Implementatieomgevingen

Gebruik GitHub-omgevingen voor gecontroleerde implementaties:

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

== Gebruiksscenario's en Architectuur

=== Use Case-diagram

[plantuml, use-cases, svg]
----
@startuml
!theme plain

left to right direction

actor "Ontwikkelaar" as dev
actor "GitHub Actions" as ga
actor "Eindgebruiker" as user

package "CI/CD-systeem" {
  usecase "Linting uitvoeren" as lint
  usecase "Tests uitvoeren" as test
  usecase "Pakket bouwen" as build
  usecase "Publiceren naar PyPI" as publish
  usecase "Release maken" 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
----

=== Implementatiearchitectuur

[plantuml, deployment-architecture, svg]
----
@startuml
!theme plain

cloud "GitHub" {
  [Source Repository]
  [GitHub Actions]
  [Secrets Store]
}

cloud "PyPI" {
  [Package Registry]
  [Distribution Files]
}

node "CI/CD-pijplijn" {
  [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
Beveiligde token-gebaseerde authenticatie
end note

[Secrets Store] .. N1

@enduml
----

== Monitoring en Debugging

=== Logs en Monitoring

GitHub Actions biedt gedetailleerde logs voor elke stap. Om te debuggen:

1. **Bekijk de logs**van elke step
2. **Activeer debug**met`ACTIONS_STEP_DEBUG`
3. **Gebruik artifacts**om de build-bestanden op te slaan

```yaml
----
- name: Upload build artifacts
  uses: actions/upload-artifact@v3
  if: failure()
  with:
    name: build-logs
    path: build/
----

=== Meldingen

Voeg Slack- of e-mailmeldingen toe:

```yaml
----
- name: Notify on failure
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    webhook_url: ${{ secrets.SLACK_WEBHOOK }}
----

== Conclusie

Het opzetten van een robuuste CI/CD-pipeline met GitHub Actions transformeert de ontwikkelervaring radicaal. Door linting, tests en implementatie te automatiseren, doet u het volgende:

- **Verminder de fouten**in productie - **Versnel de cycli**van de ontwikkeling - **Verbeter het vertrouwen**in uw releases - **Faciliteer de samenwerking**in teamverband

Deze pipeline kan worden aangepast aan verschillende soorten Python-projecten door de linting-tools, testframeworks of implementatiebestemmingen aan te passen.

De initiële investering in het configureren van deze workflows wordt snel terugverdiend door de tijdwinst en de vermindering van handmatige fouten tijdens de implementaties.

== Aanvullende bronnen

- https://docs.github.com/en/actions[GitHub Actions-documentatie] - https://packaging.python.org/[Python Packaging Gids] - https://docs.pytest.org/[Pytest-documentatie] - https://docs.astral.sh/ruff/[Documentatie Ruff] - https://twine.readthedocs.io/[Twine-documentatie]

✅ Functionele pipeline bereikt! U heeft nu een eenvoudige CI/CD-pipeline waarmee u uw tests kunt automatiseren en uw Python-pakket rechtstreeks vanuit GitHub Actions op PyPI kunt publiceren.

Deze pipeline blijft echter bewust minimalistisch. Het bestrijkt nog niet sommige onmisbare aspecten in een professionele context:

Multi-versie tests van Python,

Automatische beveiligingsanalyse,

Geleidelijke uitrol via Test PyPI,

Monitoring en metrieken van de pipeline,

Automatisering van versiebeheer en integratie van moderne best practices (`pyproject.toml`).

In het volgende deel gaan we een stap verder. Je leert hoe je deze basispipeline kunt transformeren in een echte industriële deployment-chain, robuust en beveiligd, klaar voor Python-productieprojecten.

Gerelateerde artikelen