Cykl: Automatyzacja procesów z n8n — od danych do AI w jednym narzędziu · Część 10/26

Automatyzacja CI/CD w Pythonie: GitHub Actions

Kacper Sieradziński
Kacper Sieradziński24 stycznia 2025 · 5 min czytania
Streszczenie
  • Czym jest CI/CD w projekcie Python?
  • Struktura plików GitHub Actions
  • Podstawowy workflow CI dla Pythona
  • Testowanie na wielu wersjach Pythona
Automatyzacja CI/CD w Pythonie: GitHub Actions

Automatyzacja CI/CD w Pythonie: GitHub Actions

Automatyzacja CI/CD w Pythonie pozwala uruchamiać testy, sprawdzać jakość kodu, budować paczki i wdrażać aplikację bez ręcznego wykonywania tych samych komend po każdym commicie. GitHub Actions jest jednym z najprostszych sposobów na taki pipeline, bo działa bezpośrednio w repozytorium GitHub i wymaga tylko pliku YAML w katalogu .github/workflows/.

Dobrze przygotowany workflow CI/CD sprawia, że każdy push lub pull request może automatycznie uruchomić testy, lintowanie, formatowanie, sprawdzanie typów, raport pokrycia kodu i build paczki. Dzięki temu błędy są wykrywane szybciej, kod jest bardziej spójny, a proces wydania nowej wersji staje się powtarzalny.

W tym artykule zobaczysz, jak stworzyć pipeline CI/CD dla projektu Python: od podstawowego workflow, przez testy na wielu wersjach Pythona, po lint, cache zależności, build paczki i deployment.

Kurs · 24 lekcje8h 14m
Kurs

Kurs Python od podstaw — PyStart

Zacznij programować w Pythonie od zera. Praktyczny kurs wideo z ćwiczeniami — bez wcześniejszego doświadczenia.

  • 24 lekcje wideo + 80 ćwiczeń
  • Realne bazy z e-commerce
  • Społeczność i code-review
699 zł
Zacznij kurs Pythona

Czym jest CI/CD w projekcie Python?

CI oznacza Continuous Integration, czyli ciągłą integrację. W praktyce chodzi o automatyczne sprawdzanie kodu po każdej zmianie. Typowy workflow CI uruchamia testy, lintery i narzędzia jakości kodu.

CD oznacza Continuous Delivery albo Continuous Deployment. W tym przypadku pipeline nie tylko sprawdza kod, ale może też przygotować paczkę, opublikować ją w PyPI, zbudować obraz Dockera albo wdrożyć aplikację na serwer.

W projekcie Python CI/CD może obejmować:

  • uruchamianie testów przez pytest,
  • sprawdzanie pokrycia kodu testami,
  • formatowanie przez black,
  • lintowanie przez ruff lub flake8,
  • sortowanie importów,
  • sprawdzanie typów przez mypy,
  • budowanie paczki przez python -m build,
  • publikację do PyPI,
  • deployment aplikacji,
  • uruchamianie pipeline tylko na wybranych branchach.

Struktura plików GitHub Actions

Workflow GitHub Actions zapisuje się w katalogu:

1 .github/workflows/

Przykładowa struktura projektu Python:

1 2 3 4 5 6 7 8 9 10 11 12 my_project/ ├── .github/ │ └── workflows/ │ └── ci.yml ├── src/ │ └── my_app/ │ └── __init__.py ├── tests/ │ └── test_app.py ├── pyproject.toml ├── requirements.txt └── README.md

Plik ci.yml opisuje, kiedy workflow ma się uruchomić, na jakim systemie ma działać i jakie kroki ma wykonać.

Podstawowy workflow CI dla Pythona

Najprostszy workflow powinien pobrać kod, zainstalować Pythona, zainstalować zależności i uruchomić testy.

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 name: CI on: push: branches: [main, develop] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest - name: Run tests run: | pytest tests/

To minimalny, ale bardzo praktyczny start. Od tego momentu każdy push na main lub develop oraz każdy pull request do main uruchomi testy automatycznie.

Testowanie na wielu wersjach Pythona

W projektach bibliotekowych warto sprawdzać kod na kilku wersjach Pythona. Do tego służy matrix.

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 name: Python tests on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.10", "3.11", "3.12"] steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-cov - name: Run tests run: | pytest tests/ --cov=src --cov-report=xml

Dzięki temu GitHub Actions uruchomi ten sam zestaw testów osobno dla każdej wersji Pythona z listy. To pomaga szybko wykryć problemy kompatybilności.

Cache zależności pip

Instalowanie zależności przy każdym uruchomieniu workflow może wydłużać pipeline. GitHub Actions pozwala włączyć cache dla pip.

YAML
1 2 3 4 5 6 7 8 9 10 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" cache: "pip" - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt

Cache przyspiesza kolejne uruchomienia, szczególnie gdy projekt ma dużo zależności.

Lintowanie i formatowanie kodu

Kurs · 24 lekcje8h 14m
Kurs

Kurs Python dla początkujących — PyStart

Zacznij programować w Pythonie! Idealne dla osób bez doświadczenia. Praktyczne zadania, projekty i wsparcie społeczności.

  • 24 lekcje wideo + 80 ćwiczeń
  • Realne bazy z e-commerce
  • Społeczność i code-review
699 zł
Rozpocznij naukę

Samo uruchamianie testów nie wystarczy. Warto też sprawdzać jakość i styl kodu. Popularne narzędzia to black, ruff, flake8, isortmypy.

Przykład osobnego joba do kontroli jakości:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 name: Code quality on: push: branches: [main] pull_request: branches: [main] jobs: quality: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" cache: "pip" - name: Install quality tools run: | python -m pip install --upgrade pip pip install black ruff mypy pytest - name: Check formatting run: | black --check src tests - name: Run Ruff run: | ruff check src tests - name: Type checking run: | mypy src

Takie sprawdzenie działa jak quality gate. Jeśli kod nie przejdzie testów jakości, pull request nie powinien zostać scalony.

Kompletny pipeline CI: testy, lint i typy

W większym projekcie możesz połączyć kilka kontroli w jednym workflow. Poniżej przykład kompletnego CI dla aplikacji Python.

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 name: CI on: push: branches: [main, develop] pull_request: branches: [main] jobs: test: name: Tests on Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: matrix: python-version: ["3.10", "3.11", "3.12"] steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: "pip" - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-cov - name: Run tests run: | pytest tests/ --cov=src --cov-report=xml quality: name: Code quality runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" cache: "pip" - name: Install quality tools run: | python -m pip install --upgrade pip pip install black ruff mypy - name: Check code style run: | black --check src tests - name: Lint code run: | ruff check src tests - name: Check types run: | mypy src

Ten workflow uruchamia testy na kilku wersjach Pythona i osobno sprawdza jakość kodu. Dzięki podziałowi na joby łatwiej zobaczyć, co dokładnie się nie udało.

Workflow z budowaniem paczki Python

Jeśli tworzysz bibliotekę, warto dodać workflow, który sprawdza, czy paczka buduje się poprawnie.

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 name: Build package on: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install build tools run: | python -m pip install --upgrade pip pip install build - name: Build package run: | python -m build - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: python-package path: dist/

Po wykonaniu workflow pliki z katalogu dist/ będą dostępne jako artifact. To przydatne, gdy chcesz pobrać paczkę i sprawdzić ją przed publikacją.

Publikacja paczki do PyPI

Publikacja paczki powinna być wykonywana ostrożnie. Najczęściej uruchamia się ją dopiero po utworzeniu release albo taga.

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 name: Publish package on: release: types: [published] jobs: publish: runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" - name: Install build tools run: | python -m pip install --upgrade pip pip install build twine - name: Build package run: | python -m build - name: Publish to PyPI env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: | twine upload dist/*

Token do PyPI należy zapisać w GitHub Secrets, np. jako PYPI_API_TOKEN. Nie wolno wpisywać tokenów bezpośrednio w pliku YAML.

GitHub Secrets w CI/CD

Sekrety służą do przechowywania tokenów, haseł, kluczy API i danych dostępowych. W GitHubie dodasz je w ustawieniach repozytorium:

1 Settings → Secrets and variables → Actions → New repository secret

Przykładowe sekrety:

1 2 3 4 5 PYPI_API_TOKEN DEPLOY_HOST DEPLOY_USER DEPLOY_SSH_KEY API_TOKEN

W workflow odwołujesz się do nich tak:

YAML
1 2 env: API_TOKEN: ${{ secrets.API_TOKEN }}

Sekrety nie powinny być logowane ani wypisywane komendą echo. To jedna z najważniejszych zasad bezpieczeństwa w CI/CD.

Kompletny pipeline CI/CD z deploymentem

Poniżej przykład pipeline, który najpierw sprawdza jakość kodu i testy, a dopiero potem uruchamia deployment. Deployment działa tylko na branchu main.

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 name: Full CI/CD Pipeline on: push: branches: [main] pull_request: branches: [main] jobs: quality-gate: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" cache: "pip" - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-cov black ruff mypy - name: Run tests run: | pytest tests/ --cov=src --cov-report=xml - name: Check formatting run: | black --check src tests - name: Lint code run: | ruff check src tests - name: Type checking run: | mypy src deploy: needs: quality-gate runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' && github.event_name == 'push' steps: - name: Checkout repository uses: actions/checkout@v4 - name: Deploy application run: | echo "Tutaj dodaj kroki deploymentu" echo "Przykład: SSH, Docker, platforma PaaS albo cloud provider"

Parametr needs: quality-gate oznacza, że deployment uruchomi się dopiero wtedy, gdy job quality-gate zakończy się sukcesem.

Deployment przez SSH - prosty przykład

Jeśli aplikacja działa na własnym serwerze, możesz wdrożyć ją przez SSH. W praktyce warto używać gotowych akcji lub własnego skryptu deploymentu.

Przykładowy krok:

YAML
1 2 3 4 5 6 7 8 9 10 11 12 - name: Deploy over SSH uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} script: | cd /var/www/myapp git pull origin main source venv/bin/activate pip install -r requirements.txt sudo systemctl restart myapp

W takim wariancie dane dostępowe są przechowywane w GitHub Secrets, a sam workflow zawiera tylko opis kroków.

Uruchamianie workflow ręcznie

Czasem warto uruchomić pipeline ręcznie, np. dla deploymentu testowego. Do tego służy workflow_dispatch.

YAML
1 2 3 4 5 6 7 8 9 10 11 12 13 name: Manual deployment on: workflow_dispatch: jobs: deploy: runs-on: ubuntu-latest steps: - name: Run manual deployment run: | echo "Deployment uruchomiony ręcznie"

Po dodaniu tego zdarzenia w zakładce Actions pojawi się przycisk umożliwiający ręczne uruchomienie workflow.

Dobre praktyki CI/CD w Pythonie

Dobrze przygotowany pipeline powinien być szybki, czytelny i bezpieczny. Nie musi od razu wdrażać aplikacji. Najważniejsze, aby automatycznie wykrywał błędy przed scaleniem kodu.

Najważniejsze praktyki:

  • uruchamiaj testy na pull requestach,
  • sprawdzaj formatowanie i lint przed merge,
  • używaj cache dla zależności,
  • testuj najważniejsze wersje Pythona,
  • nie zapisuj sekretów w repozytorium,
  • rozdziel CI i deployment, jeśli projekt tego wymaga,
  • uruchamiaj deployment tylko po przejściu quality gate,
  • zapisuj buildy jako artifacts,
  • stosuj czytelne nazwy jobów,
  • unikaj zbyt długich i nieczytelnych plików YAML.

Podsumowanie

Newsletter · co środę

Python co tydzień — newsletter dla programistów

Otrzymuj codzienne ćwiczenia, ciekawostki z ekosystemu Pythona i wskazówki do rozmów rekrutacyjnych.

2 312 czytelników · ⭐ 4,8

Automatyzacja CI/CD w Pythonie z GitHub Actions pozwala sprawdzać kod po każdej zmianie, uruchamiać testy, kontrolować styl, budować paczki i wdrażać aplikację w powtarzalny sposób. Wszystko opisujesz w plikach YAML przechowywanych w repozytorium.

Najlepszy start to prosty workflow z testami pytest. Następnie warto dodać matrix dla kilku wersji Pythona, lintowanie, formatowanie, sprawdzanie typów, cache zależności i build paczki. Deployment powinien być uruchamiany dopiero po przejściu quality gate.

Jeśli chcesz wdrożyć CI/CD praktycznie, zacznij od pliku .github/workflows/ci.yml, który uruchamia testy i lint na pull requestach. To szybka zmiana, która od razu poprawia jakość projektu i zmniejsza ryzyko wprowadzania błędów do głównej gałęzi.

➡️ Następny artykuł

Gratulacje! Opanowałeś kluczowe techniki automatyzacji w Pythonie. Jeśli chcesz pogłębić wiedzę, sprawdź pozostałe artykuły w sekcji automatyzacji:

Automatyzacja zadań w Pythonie — Praktyczne przykłady — dodatkowe przykłady i zaawansowane techniki automatyzacji.

Część 11 z 26

Automatyzacja Excela w Pythonie: odczyt, zapis, walidacja danych

druga lekcja cyklu „Automatyzacja procesów z n8n — od danych do AI w jednym narzędziu"

Czytaj kolejny →