Czym jest CI/CD?
CI/CD (Continuous Integration / Continuous Deployment) to praktyka automatycznego:
- CI (Continuous Integration) – integracja zmian kodu, uruchamianie testów, sprawdzanie jakości
- CD (Continuous Deployment) – automatyczne wdrażanie przetestowanego kodu do produkcji
Korzyści z CI/CD:
- 🔍 Wczesne wykrywanie błędów – testy uruchamiane przy każdym commicie
- 🚀 Szybsze wdrożenia – automatyzacja procesu release
- 🛡️ Bezpieczeństwo – kod jest testowany przed wdrożeniem
- 📊 Przejrzystość – każdy widzi status testów i wdrożeń
- ⏱️ Oszczędność czasu – nie trzeba ręcznie uruchamiać testów
Jeśli dopiero zaczynasz z testowaniem, sprawdź najpierw wprowadzenie do unittest lub testowanie z pytest.
Wprowadzenie do GitHub Actions
GitHub Actions to platforma CI/CD wbudowana w GitHub. Używa plików YAML do definiowania workflow (procesów automatyzacji).
Struktura workflow
Workflow w GitHub Actions składa się z:
- Events – kiedy workflow ma się uruchomić (push, pull_request, etc.)
- Jobs – zadania do wykonania (np. testy, build)
- Steps – kroki w każdym job (np. instalacja, uruchomienie testów)
- Actions – gotowe komponenty (np. setup-python)
Gdzie umieścić workflow?
Workflow definiuje się w plikach YAML w katalogu:
Bash1.github/workflows/
Przykład struktury:
Bash1 2 3 4 5 6 7 8 9projekt/ ├── .github/ │ └── workflows/ │ ├── tests.yml │ ├── coverage.yml │ └── deploy.yml ├── src/ ├── tests/ └── README.md
Podstawowy workflow dla testów Python
Stwórzmy podstawowy workflow do uruchamiania testów:
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: Tests on: push: branches: [main, develop] pull_request: branches: [main, develop] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 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 --cov=src --cov-report=xml --cov-report=term
Wyjaśnienie workflow
name– nazwa workflow (wyświetlana w GitHub)on– events uruchamiające workflow
push– przy pushu do określonych branchypull_request– przy tworzeniu/aktualizacji PR
jobs– lista zadańruns-on– system operacyjny (ubuntu-latest, windows-latest, macos-latest)strategy.matrix– uruchomienie testów na wielu wersjach Pythonasteps– kroki wykonania:
checkout@v3– pobranie kodusetup-python@v4– instalacja Pythona- Instalacja zależności i uruchomienie testów
Zaawansowany workflow z caching
Optymalizacja czasu wykonania dzięki cache'owaniu zależnoś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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54name: Tests on: push: branches: [main, develop] pull_request: branches: [main, develop] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: 'pip' cache-dependency-path: | requirements.txt - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-cov flake8 black - name: Lint with flake8 run: | flake8 src tests --count --select=E9,F63,F7,F82 --show-source --statistics flake8 src tests --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Format check with black run: | black --check src tests - name: Run tests run: | pytest --cov=src --cov-report=xml --cov-report=term --cov-report=html -v - name: Upload coverage reports uses: codecov/codecov-action@v3 with: file: ./coverage.xml flags: unittests name: codecov-umbrella
Workflow z warunkowym deploymentem
Automatyczne wdrożenie tylko gdy testy przejdą:
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# .github/workflows/deploy.yml name: Deploy on: push: branches: [main] workflow_run: workflows: ["Tests"] types: - completed jobs: deploy: runs-on: ubuntu-latest if: > github.event_name == 'push' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install dependencies run: | pip install -r requirements.txt - name: Deploy to production run: | echo "Deploying to production..." # Twoje komendy deploymentu
Testowanie z bazą danych
Testy wymagające bazy danych:
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# .github/workflows/tests-db.yml name: Tests with Database on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest services: postgres: image: postgres:15 env: POSTGRES_USER: testuser POSTGRES_PASSWORD: testpass POSTGRES_DB: testdb options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 redis: image: redis:7 options: >- --health-cmd "redis-cli ping || exit 1" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 6379:6379 steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' cache: 'pip' - name: Install dependencies run: | pip install -r requirements.txt pip install pytest pytest-cov - name: Run tests env: DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb REDIS_URL: redis://localhost:6379 run: | pytest --cov=src
Publikowanie artefaktów
Zapisywanie wyników testów i coverage jako artefaktów:
YAML1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16- name: Generate coverage report run: pytest --cov=src --cov-report=html --cov-report=xml - name: Upload coverage HTML uses: actions/upload-artifact@v3 with: name: coverage-report path: htmlcov/ - name: Upload test results if: always() uses: actions/upload-artifact@v3 with: name: test-results path: test-results.xml
Status checks i branch protection
Aby wymusić, że PR musi mieć przechodzące testy:
- Przejdź do Settings → Branches
- Dodaj rule dla branch (np.
main) - Zaznacz: "Require status checks to pass before merging"
- Wybierz workflow do wymagania
Wtedy PR nie może być zmergowany, dopóki workflow nie przejdzie.
Workflow dla Django
Specjalny workflow dla projektów Django:
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# .github/workflows/django.yml name: Django CI on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest services: postgres: image: postgres:15 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: testdb options: >- --health-cmd "pg_isready -U postgres -d testdb" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 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-django pytest-cov - name: Run migrations env: DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb run: | python manage.py migrate - name: Run tests env: DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb SECRET_KEY: test-secret-key run: | pytest --cov=. --cov-report=xml
Workflow dla FastAPI
Workflow dla projektów FastAPI:
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# .github/workflows/fastapi.yml name: FastAPI CI 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: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: 'pip' cache-dependency-path: | requirements.txt - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-asyncio httpx pytest-cov - name: Run tests run: | pytest --cov=src --cov-report=xml -v - name: Upload coverage uses: codecov/codecov-action@v3 with: file: ./coverage.xml
Integracja z Codecov
Codecov śledzi coverage w czasie i pokazuje trendy:
- Zarejestruj się na codecov.io
- Dodaj repozytorium
- Skopiuj token
- Dodaj token jako secret w GitHub: Settings → Secrets → Actions
- Użyj w workflow:
YAML1 2 3 4 5 6 7- name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} file: ./coverage.xml flags: unittests name: codecov-umbrella
Workflow z matrix dla wielu środowisk
Testowanie na wielu systemach i wersjach:
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# .github/workflows/matrix-test.yml name: Matrix Tests on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: ['3.10', '3.11', '3.12'] exclude: - os: windows-latest python-version: '3.12' # Wykluczenie kombinacji steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 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 - name: Run tests run: pytest
Najlepsze praktyki
1. Cache'uj zależności
YAML1 2 3- uses: actions/setup-python@v4 with: cache: 'pip'# Automatyczne cache'owanie pip
2. Używaj matrix dla wielu wersji
Testuj na różnych wersjach Pythona i systemach operacyjnych.
3. Rozdzielaj zadania
Oddzielne joby dla linting, testów i deploymentu pozwalają na równoległe wykonanie.
4. Używaj conditionals
YAML1 2 3- name: Deploy if: github.ref == 'refs/heads/main' && github.event_name == 'push' run: echo "Deploying..."
5. Zabezpieczaj sekrety
Nigdy nie commituj tokenów czy haseł. Używaj GitHub Secrets.
6. Zapisuj artefakty
Używaj upload-artifact do zapisywania raportów coverage, logów, etc.
7. Ustaw timeouty
YAML1 2 3jobs: test: timeout-minutes: 30
8. Powiadomienia
YAML1 2 3 4 5- name: Notify on failure if: failure() run: | # Wyślij powiadomienie (email, Slack, Discord, etc.) echo "Tests failed!"
Debugowanie workflow
Włączanie debugowania
YAML1 2 3env: ACTIONS_STEP_DEBUG: true ACTIONS_RUNNER_DEBUG: true
Sprawdzanie logów
Wszystkie logi są dostępne w zakładce "Actions" w GitHub.
Lokalne testowanie
Użyj act do lokalnego uruchamiania workflow:
Bash1act push
Podsumowanie
GitHub Actions to potężne narzędzie do automatyzacji testów i deploymentu. Kluczowe punkty:
- ✅ Workflow definiuje się w plikach YAML w
.github/workflows/ - ✅ Używaj matrix do testowania na wielu wersjach/environmentach
- ✅ Cache'uj zależności dla szybszego wykonania
- ✅ Rozdzielaj zadania na osobne joby
- ✅ Integruj z narzędziami jak Codecov
- ✅ Zabezpieczaj sekrety przez GitHub Secrets
- ✅ Używaj branch protection do wymuszania status checks
CI/CD to fundament nowoczesnego developmentu. Automatyzacja testów gwarantuje, że kod jest zawsze przetestowany przed wdrożeniem.
Co dalej?
Teraz, gdy znasz GitHub Actions, zastosuj to w praktyce:
- Testowanie z pytest – wykorzystaj pytest w workflow
- Coverage i mierzenie jakości testów – integruj coverage z GitHub Actions
- Testy integracyjne API – testuj API w CI/CD
Zacznij od prostego workflow i stopniowo dodawaj zaawansowane funkcje. Automatyzacja testów to inwestycja, która szybko się zwraca!



