Lekcja 3 · Dzień 3

CI/CD GitHub Actions na flecie 20+ serwisów

To będzie konkretniejsze niż „Jenkins”. JD: reusable workflows, matrix, cache, Java/Kotlin. Plus OIDC zamiast kluczy i promocja PR → staging → prod.

Reusable workflows

Jeden kontrakt dla 20 repo (albo monorepo z 20 katalogami):

# .github/workflows/service.yml  (w repo serwisu)
jobs:
  build:
    uses: org/platform-workflows/.github/workflows/java-service.yml@v3
    with:
      service: payments-api
      java-version: "21"
      environment: ${{ github.ref == 'refs/heads/main' && 'staging' || 'dev' }}
    secrets: inherit  # lepiej: named secrets albo OIDC bez secrets
    permissions:
      id-token: write
      contents: read

Zasady, które warto powiedzieć:

Matrix (java / env / service)

strategy:
  fail-fast: false
  matrix:
    service: [ledger, cards, limits, receipts]
    java: ["17", "21"]
    exclude:
      - service: cards
        java: "17"

fail-fast: false na flecie: jeden czerwony serwis nie ma ukrywać reszty. Pytanie z raportu: pipeline pada tylko na jednym serwisie w matrixie — pełna odpowiedź jest w pytaniach. Szkielet diagnostyki:

  1. Czy pada ten sam serwis lokalnie na tym samym JDKu i z czystym Gradle?
  2. Czy pada tylko w CI? → cache, secrets, OIDC, runner, sieć do Artifactory.
  3. Czy pada tylko jedna komórka matrixa? → wersja Javy, exclude, zła ścieżka.
  4. Czy pada flaky? → rerun ≠ fix. Test isolation, czas, testcontainers vs. niedostępny Docker layer.

Cache Gradle/Maven i kiedy kłamie

actions/setup-java + cache: gradle albo actions/cache na ~/.gradle/caches. Klucz: lockfile + OS + java version.

Kiedy cache kłamie:

Mitigacja: cache tylko dependencies (caches/modules-2), nie build/. Restore-keys ostrożnie. Przy podejrzeniu: job z cache: false jako kontrola. Dokumentuj „jak zabić cache” w runbooku — to będzie pytanie operacyjne.

OIDC do AWS zamiast long-lived keys

Przepływ:

  1. W AWS: OIDC provider https://token.actions.githubusercontent.com, audience sts.amazonaws.com.
  2. Rola z trust policy: sub ograniczony do repo:org/repo:environment:prod (albo ref). Nie do całego org.
  3. Job: permissions: id-token: write + aws-actions/configure-aws-credentials z role-to-assume.
  4. GitHub Environments: protection rules na prod (reviewers, wait timer). OIDC claim environment musi zgadzać się z trust policy.

Dla PCI: self-hosted runner w PCI VPC, albo deploy z account CI przez assume-role do PCI z bardzo wąską polityką (ECR push + ECS update, bez RDS). Public GitHub-hosted runner nie powinien mieć roli, która czyta CHD.

Promocja: PR → staging → prod

Promotion by digest: staging i prod palą ten sam SHA. „Na stagingu działało” bez tego samego obrazu nic nie znaczy.

Jak debugujesz pipeline, który „działa lokalnie”

  1. Porównaj wersje: JDK, Gradle, OS, Docker. java -version w logu CI.
  2. Wymuś czysty build: --no-build-cache --refresh-dependencies.
  3. Sprawdź sekrety: lokalnie masz ~/.aws, w CI masz OIDC. Inny caller, inny IAM.
  4. Sieć: runner nie widzi internal Maven, VPC endpoint, IP allow-list Artifactory.
  5. Timezones, locale, line.separator — rzadziej, ale JUnit to lubi.
  6. Akt runner vs. Twój laptop: 2 vCPU vs. 16. Test timing, Testcontainers OOM.

Ćwiczenie

Na kartce: sygnatura reusable workflow (inputs, permissions, environments). Jedna linijka trust policy OIDC z sub dla prod. Opowiedz, jak debugujesz padający jeden serwis w matrixie — zegar 3 minuty.