УРОК 26 / 30 0%

GitHub Actions: build → test → push → deploy

🎯

Цель урока

Сделать полноценный CRUD для задач с правильными HTTP-статусами (201/204/404), обработкой ошибок, и подготовить Task к подключению JPA.

🧠

Теория · для собеса

1 Что такое CI/CD

CI (Continuous Integration) — на каждый merge в main: запустить тесты, собрать проект.

CD (Continuous Deployment) — после успешной CI: задеплоить на production автоматически.

2 GitHub Actions — компоненты
  • Workflow — файл .github/workflows/*.yml
  • Job — набор шагов, выполняется на runner'е
  • Step — отдельная команда (run) или action (uses:)
  • Runner — VM, где выполняется workflow (ubuntu-latest, или self-hosted)
  • Trigger — событие запуска (on: push, on: pull_request, on: schedule)
3 Этапы нашего CI/CD
  1. build./gradlew build
  2. test./gradlew test (unit + slice)
  3. integration-test./gradlew test --tests "*IT" (Testcontainers)
  4. build-imagedocker build -t taskflow:$SHA .
  5. push-imagedocker push ghcr.io/user/taskflow:$SHA
  6. deploy (только main) — обновить deployment в k8s

---

💻

Практика: улучшаем CRUD

  1. 1
    .github/workflows/ci.yml
    
    name: CI
    
    on:
      push:
        branches: [main, develop]
      pull_request:
        branches: [main]
    
    env:
      REGISTRY: ghcr.io
      IMAGE_NAME: ${{ github.repository }}
    
    jobs:
      test:
        name: Test (unit + slice)
        runs-on: ubuntu-latest
        services:
          postgres:
            image: postgres:16-alpine
            env:
              POSTGRES_DB: taskflow_test
              POSTGRES_USER: test
              POSTGRES_PASSWORD: test
            ports: [5432:5432]
            options: >-
              --health-cmd pg_isready
              --health-interval 10s
              --health-timeout 5s
              --health-retries 5
    
        steps:
        - name: Checkout
          uses: actions/checkout@v4
    
        - name: Set up JDK 21
          uses: actions/setup-java@v4
          with:
            distribution: temurin
            java-version: 21
            cache: gradle
    
        - name: Grant execute permission for gradlew
          run: chmod +x gradlew
    
        - name: Run unit + slice tests
          run: ./gradlew test
          env:
            SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/taskflow_test
            SPRING_DATASOURCE_USERNAME: test
            SPRING_DATASOURCE_PASSWORD: test
    
        - name: Upload coverage
          if: always()
          uses: actions/upload-artifact@v4
          with:
            name: coverage
            path: build/reports/jacoco/test/
    
      integration-test:
        name: Integration tests (Testcontainers)
        runs-on: ubuntu-latest
        steps:
        - name: Checkout
          uses: actions/checkout@v4
    
        - name: Set up JDK 21
          uses: actions/setup-java@v4
          with:
            distribution: temurin
            java-version: 21
            cache: gradle
    
        - name: Run integration tests
          run: ./gradlew test --tests "*IT"
    
      build-and-push:
        name: Build & Push Docker image
        runs-on: ubuntu-latest
        needs: [test, integration-test]
        permissions:
          contents: read
          packages: write
        steps:
        - name: Checkout
          uses: actions/checkout@v4
    
        - name: Set up Docker Buildx
          uses: docker/setup-buildx-action@v3
    
        - name: Log in to GHCR
          if: github.event_name != 'pull_request'
          uses: docker/login-action@v3
          with:
            registry: ${{ env.REGISTRY }}
            username: ${{ github.actor }}
            password: ${{ secrets.GITHUB_TOKEN }}
    
        - name: Extract metadata
          id: meta
          uses: docker/metadata-action@v5
          with:
            images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
            tags: |
              type=sha,prefix=
              type=ref,event=branch
              type=ref,event=pr
              type=raw,value=latest,enable={{is_default_branch}}
    
        - name: Build and push
          uses: docker/build-push-action@v5
          with:
            context: .
            push: ${{ github.event_name != 'pull_request' }}
            tags: ${{ steps.meta.outputs.tags }}
            labels: ${{ steps.meta.outputs.labels }}
            cache-from: type=gha
            cache-to: type=gha,mode=max
    
      deploy:
        name: Deploy to k8s
        runs-on: ubuntu-latest
        needs: build-and-push
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        environment:
          name: production
          url: https://taskflow.example.com
        steps:
        - name: Checkout
          uses: actions/checkout@v4
    
        - name: Set up kubectl
          uses: azure/setup-kubectl@v4
          with:
            version: v1.29.0
    
        - name: Configure kubeconfig
          run: |
            mkdir -p ~/.kube
            echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
    
        - name: Update image in deployment
          run: |
            kubectl set image deployment/taskflow \
              taskflow=ghcr.io/${{ github.repository }}:${{ github.sha }} \
              -n taskflow
    
        - name: Wait for rollout
          run: |
            kubectl rollout status deployment/taskflow -n taskflow --timeout=5m
    
        - name: Verify deployment
          run: |
            sleep 10
            curl -f https://taskflow.example.com/actuator/health || exit 1
    
  2. 2
    GitHub Secrets (в настройках репо)
    • KUBECONFIG — base64 от ~/.kube/config
    • (GITHUB_TOKEN — авто)
  3. 3
    Branch protection

    Settings → Branches → Branch protection rules → main:

    • ✅ Require status checks to pass before merging
    • ✅ Require branches to be up to date
    • ✅ Include administrators
  4. 4
    Workflow dispatch (ручной запуск)
    
    on:
      workflow_dispatch:
        inputs:
          environment:
            description: 'Environment to deploy'
            required: true
            type: choice
            options: [dev, staging, production]
    
  5. 5
    Артефакты
    
    - name: Upload JAR
      uses: actions/upload-artifact@v4
      with:
        name: taskflow-jar
        path: build/libs/*.jar
        retention-days: 7
    
  6. 6
    Матрица (Java 21, 23)
    
    strategy:
      matrix:
        java: [21, 23]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: ${{ matrix.java }}
    
  7. 7
    Badges в README
    
    ![CI](https://github.com/USER/REPO/actions/workflows/ci.yml/badge.svg)
    

    ---

🎯

Зачем это на собесе

После урока ты должен уметь ответить на:

Каждый push проверяется — баг пойман за минуты, а не на проде через неделю.
Build → Deploy за 5-10 мин — ручной деплой с его ошибками уходит в прошлое.
Кэш Gradle/Maven в GitHub Actions — на 60-80% быстрее сборка.
GHCR — приватный registry бесплатно для публичных/приватных репо.
Rollback в одну команду: kubectl rollout undo deployment/taskflow -n taskflow (или helm rollback).

5 вопросов на углубление

Раскрой вопрос и нажми «🤔 Хочу разобрать подробнее» — он попадёт в страницу ответов.

1
CI vs CD — где граница?

Разбор внутри: build/test на push и доставка в окружение.

2
Как работает Docker cache в GitHub Actions Buildx?

Разбор внутри: cache-from type=gha и ускорение сборки image.

3
Как безопасно использовать secrets в GitHub Actions?

Разбор внутри: env, маскирование и запрет печатать секреты.

4
ArgoCD pull model vs kubectl apply из CI

Разбор внутри: GitOps-подход и прямое применение из pipeline.

5
Зачем permissions: packages: write в workflow?

Разбор внутри: права GITHUB_TOKEN для публикации image в GHCR.

Готов идти дальше?

Выбери вопросы, которые тебе интересны, и изучи их. Потом — к следующему уроку.

🚀 Перейти к Уроку 27

Сначала пройди все секции и выбери хотя бы 1 вопрос

⬅️ Назад к Уроку 25
🎉
Новый тир
Новый тир достигнут!