Skip to content

deps(actions): bump codecov/codecov-action from 5 to 6 #736

deps(actions): bump codecov/codecov-action from 5 to 6

deps(actions): bump codecov/codecov-action from 5 to 6 #736

Workflow file for this run

name: Stack Tests
on:
push:
branches: [main]
tags: ['v*']
paths:
- 'backend/**'
- 'frontend/**'
- 'cert-generator/**'
- 'docker-compose.yaml'
- 'deploy.sh'
- '.github/workflows/stack-tests.yml'
- '.github/actions/**'
pull_request:
branches: [main]
paths:
- 'backend/**'
- 'frontend/**'
- 'cert-generator/**'
- 'docker-compose.yaml'
- 'deploy.sh'
- '.github/workflows/stack-tests.yml'
- '.github/actions/**'
workflow_dispatch:
env:
REGISTRY: ghcr.io
MONGO_IMAGE: mongo:8.0
REDIS_IMAGE: redis:7-alpine
KAFKA_IMAGE: confluentinc/cp-kafka:7.8.2
K3S_VERSION: v1.32.11+k3s1
K3S_INSTALL_SHA256: d75e014f2d2ab5d30a318efa5c326f3b0b7596f194afcff90fa7a7a91166d5f7
KUEUE_VERSION: v0.16.1
KUEUE_MANIFEST_SHA256: 3201a66ff731be440ecfcf3c0fa5979d001b834f68389208fe7ee18017fbcfe8
jobs:
# Fast unit tests (no infrastructure needed)
backend-unit:
name: Backend Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: "backend/uv.lock"
- name: Install Python dependencies
run: |
cd backend
uv python install 3.12
uv sync --frozen --group test --no-dev
- name: Run unit tests
timeout-minutes: 5
run: |
cd backend
uv run --no-sync pytest tests/unit -v -rs \
--durations=0 \
--cov=app \
--cov-report=xml --cov-report=term
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v6
if: always()
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: backend/coverage.xml
flags: backend-unit
name: backend-unit-coverage
fail_ci_if_error: false
verbose: true
frontend-unit:
name: Frontend Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
working-directory: frontend
run: npm ci
- name: Run unit tests with coverage
working-directory: frontend
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v6
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: frontend/coverage/lcov.info
flags: frontend-unit
name: frontend-unit-coverage
fail_ci_if_error: false
verbose: true
# Build all images, push to GHCR with immutable SHA tag.
# Fork PRs skip GHCR push (no write access) — E2E tests require pushed images.
build-images:
name: Build & Push Images
needs: [backend-unit, frontend-unit]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
sha-tag: ${{ steps.tags.outputs.sha-tag }}
image-prefix: ${{ steps.tags.outputs.image-prefix }}
steps:
- uses: actions/checkout@v6
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
if: ${{ !github.event.pull_request.head.repo.fork }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Compute image tags
id: tags
run: |
PREFIX="${GITHUB_REPOSITORY_OWNER,,}/integr8scode"
SHA_TAG="sha-${GITHUB_SHA::7}"
echo "sha-tag=$SHA_TAG" >> "$GITHUB_OUTPUT"
echo "image-prefix=$PREFIX" >> "$GITHUB_OUTPUT"
# ── Base image (cached separately — rarely changes) ──────────────
- name: Cache base image
uses: actions/cache@v5
id: base-cache
with:
path: /tmp/base-image.tar.zst
key: base-image-${{ runner.os }}-${{ hashFiles('backend/Dockerfile.base', 'backend/pyproject.toml', 'backend/uv.lock') }}
- name: Load base image from cache
if: steps.base-cache.outputs.cache-hit == 'true'
run: zstd -d -c /tmp/base-image.tar.zst | docker load
- name: Build base image
if: steps.base-cache.outputs.cache-hit != 'true'
uses: docker/build-push-action@v6
with:
context: ./backend
file: ./backend/Dockerfile.base
load: true
tags: integr8scode-base:latest
cache-from: type=gha,scope=backend-base
cache-to: type=gha,mode=max,scope=backend-base
- name: Save base image to cache
if: steps.base-cache.outputs.cache-hit != 'true'
run: docker save integr8scode-base:latest | zstd -T0 -3 > /tmp/base-image.tar.zst
# ── Backend (depends on local base image) ───────────────
- name: Build backend image
run: |
docker build -t integr8scode-backend:latest \
--build-context base=docker-image://integr8scode-base:latest \
-f ./backend/Dockerfile ./backend
# ── Utility images (GHA-cached, independent of base) ────────────
- name: Build cert-generator image
uses: docker/build-push-action@v6
with:
context: ./cert-generator
file: ./cert-generator/Dockerfile
load: true
tags: integr8scode-cert-generator:latest
cache-from: type=gha,scope=cert-generator
cache-to: type=gha,mode=max,scope=cert-generator
# ── Frontend (nginx + SSL) ─────────────────────────────────────
- name: Build frontend image
uses: docker/build-push-action@v6
with:
context: ./frontend
file: ./frontend/Dockerfile
load: true
tags: integr8scode-frontend:latest
cache-from: type=gha,scope=frontend
cache-to: type=gha,mode=max,scope=frontend
# ── Push all images to GHCR in parallel ────────────────────────
- name: Push all images to GHCR
if: ${{ !github.event.pull_request.head.repo.fork }}
env:
TAG: ${{ steps.tags.outputs.sha-tag }}
IMG: ${{ env.REGISTRY }}/${{ steps.tags.outputs.image-prefix }}
run: |
# Tag all images for GHCR
docker tag integr8scode-base:latest "$IMG/base:$TAG"
docker tag integr8scode-backend:latest "$IMG/backend:$TAG"
docker tag integr8scode-cert-generator:latest "$IMG/cert-generator:$TAG"
docker tag integr8scode-frontend:latest "$IMG/frontend:$TAG"
# Push all 4 images in parallel, tracking each PID
declare -A PIDS
for name in base backend cert-generator frontend; do
docker push "$IMG/$name:$TAG" &
PIDS[$name]=$!
done
FAILED=0
for name in "${!PIDS[@]}"; do
if ! wait "${PIDS[$name]}"; then
echo "::error::Failed to push $name"
FAILED=1
fi
done
[ "$FAILED" -eq 0 ] || exit 1
# Parallel E2E test jobs — compose pulls from GHCR using IMAGE_TAG
backend-e2e:
name: Backend E2E Tests
needs: [build-images]
if: ${{ !github.event.pull_request.head.repo.fork }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
steps:
- uses: actions/checkout@v6
- name: Boot E2E environment
uses: ./.github/actions/e2e-boot
with:
image-tag: ${{ needs.build-images.outputs.sha-tag }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Finalize k3s & start stack
uses: ./.github/actions/e2e-ready
with:
image-tag: ${{ needs.build-images.outputs.sha-tag }}
- name: Run E2E tests
timeout-minutes: 15
run: |
docker compose exec -T backend \
sh -c 'uv sync --group test --no-dev --frozen --no-install-project && pytest tests/e2e -v -rs \
--durations=0 \
--cov=app \
--cov-report=xml:coverage-e2e.xml \
--cov-report=term'
- name: Copy coverage
if: always()
run: docker compose cp backend:/app/coverage-e2e.xml backend/coverage-e2e.xml || true
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v6
if: always()
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: backend/coverage-e2e.xml
flags: backend-e2e
name: backend-e2e-coverage
fail_ci_if_error: false
- name: Collect logs on failure
if: failure()
run: |
mkdir -p logs
docker compose logs --timestamps > logs/docker-compose.log 2>&1
for svc in backend mongo redis kafka \
k8s-worker pod-monitor result-processor \
saga-orchestrator event-replay dlq-processor; do
docker compose logs --timestamps "$svc" > "logs/$svc.log" 2>&1 || true
done
kubectl get events --sort-by='.metadata.creationTimestamp' -A > logs/k8s-events.log 2>&1 || true
- name: Upload logs
if: failure()
uses: actions/upload-artifact@v7
with:
name: backend-e2e-logs
path: logs/
frontend-e2e:
name: Frontend E2E (${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
needs: [build-images]
if: ${{ !github.event.pull_request.head.repo.fork }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2]
shardTotal: [2]
steps:
- uses: actions/checkout@v6
# Phase 1: kick off image pull + infra + k3s in background
- name: Boot E2E environment
uses: ./.github/actions/e2e-boot
with:
image-tag: ${{ needs.build-images.outputs.sha-tag }}
github-token: ${{ secrets.GITHUB_TOKEN }}
# Phase 2: Node + Playwright setup (overlaps with k3s boot + image pull)
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: Cache Playwright browsers
uses: actions/cache@v5
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('frontend/package-lock.json') }}
- name: Install Playwright system dependencies
working-directory: frontend
run: npx playwright install-deps chromium
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: frontend
run: npx playwright install chromium
# Phase 3: finalize k3s + start stack (k3s has been booting since e2e-boot)
- name: Finalize k3s & start stack
uses: ./.github/actions/e2e-ready
with:
image-tag: ${{ needs.build-images.outputs.sha-tag }}
wait-for-frontend: 'true'
install-kueue: 'false'
- name: Run Playwright tests
timeout-minutes: 10
working-directory: frontend
run: CI=true npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
- name: Upload Playwright report
uses: actions/upload-artifact@v7
if: always()
with:
name: playwright-report-${{ matrix.shardIndex }}
path: frontend/playwright-report/
- name: Collect logs on failure
if: failure()
run: |
mkdir -p logs
docker compose logs --timestamps > logs/docker-compose.log 2>&1
for svc in backend frontend; do
docker compose logs --timestamps "$svc" > "logs/$svc.log" 2>&1 || true
done
- name: Upload logs
if: failure()
uses: actions/upload-artifact@v7
with:
name: frontend-e2e-logs-${{ matrix.shardIndex }}
path: logs/