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 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
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
rufflubflake8, - 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 12my_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.
YAML1 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 30name: 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.
YAML1 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 34name: 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.
YAML1 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 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
Samo uruchamianie testów nie wystarczy. Warto też sprawdzać jakość i styl kodu. Popularne narzędzia to black, ruff, flake8, isort i mypy.
Przykład osobnego joba do kontroli jakości:
YAML1 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 38name: 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.
YAML1 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 67name: 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.
YAML1 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 35name: 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.
YAML1 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 37name: 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:
1Settings → Secrets and variables → Actions → New repository secret
Przykładowe sekrety:
1 2 3 4 5PYPI_API_TOKEN DEPLOY_HOST DEPLOY_USER DEPLOY_SSH_KEY API_TOKEN
W workflow odwołujesz się do nich tak:
YAML1 2env: 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.
YAML1 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 57name: 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:
YAML1 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.
YAML1 2 3 4 5 6 7 8 9 10 11 12 13name: 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
Python co tydzień — newsletter dla programistów
Otrzymuj codzienne ćwiczenia, ciekawostki z ekosystemu Pythona i wskazówki do rozmów rekrutacyjnych.
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.



