Skip to content

chore(deps): bump com.puppycrawl.tools:checkstyle from 10.21.1 to 14.0.0 in /services/auth #118

chore(deps): bump com.puppycrawl.tools:checkstyle from 10.21.1 to 14.0.0 in /services/auth

chore(deps): bump com.puppycrawl.tools:checkstyle from 10.21.1 to 14.0.0 in /services/auth #118

Workflow file for this run

name: CI
# Single PR pipeline for the whole monorepo:
# changes (dorny/paths-filter) → per-component jobs (only for what the PR
# touches) → ci-ok aggregator gate.
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read
# New pushes to the same PR cancel the still-running CI of the old commit.
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
# Detects which components the PR touches. Runs on every PR (no `paths:` on
# the trigger — a path-filtered workflow never reports, which breaks required
# checks); gating happens per job via these outputs instead.
changes:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
java_matrix: ${{ steps.java.outputs.matrix }}
email: ${{ steps.filter.outputs.email }}
genai: ${{ steps.filter.outputs.genai }}
web-client: ${{ steps.filter.outputs.web-client }}
openapi: ${{ steps.filter.outputs.openapi }}
infra: ${{ steps.filter.outputs.infra }}
e2e: ${{ steps.filter.outputs.e2e }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: dorny/paths-filter@v3
id: filter
with:
# `shared` is not a job trigger — it's spliced into every component:
# the spec regenerates committed DTOs/types (a spec change without
# regenerated code must fail the builds), and a change to this
# pipeline must re-run everything it gates.
filters: |
shared: &shared
- api/openapi.yaml
- .github/workflows/ci.yml
auth:
- *shared
- services/auth/**
application:
- *shared
- services/application/**
document:
- *shared
- services/document/**
gateway:
- *shared
- services/gateway/**
email:
- *shared
- services/email/**
genai:
- *shared
- services/genai/**
web-client:
- *shared
- web-client/**
openapi:
- api/**
- .github/workflows/ci.yml
infra:
- .github/workflows/**
- "**/Dockerfile"
- .pre-commit-config.yaml
# The e2e suite drives the whole stack through the gateway, so any service
# (or the compose wiring that stands them up) can break it.
e2e:
- *shared
- services/auth/**
- services/application/**
- services/document/**
- services/gateway/**
- e2e_tests/**
- docker-compose.yml
- name: Build java matrix
id: java
run: |
services=()
if [ "${{ steps.filter.outputs.auth }}" = "true" ]; then services+=('"auth"'); fi
if [ "${{ steps.filter.outputs.application }}" = "true" ]; then services+=('"application"'); fi
if [ "${{ steps.filter.outputs.document }}" = "true" ]; then services+=('"document"'); fi
if [ "${{ steps.filter.outputs.gateway }}" = "true" ]; then services+=('"gateway"'); fi
if [ "${{ steps.filter.outputs.email }}" = "true" ]; then services+=('"email"'); fi
matrix="[$(IFS=,; echo "${services[*]}")]"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "java matrix: $matrix"
# One leg per changed Spring service. Postgres is provided to every leg —
# auth/application/document/email run their test suites against it; gateway
# simply ignores it (cheaper than templating the container per leg).
java:
name: java (${{ matrix.service }})
needs: changes
if: needs.changes.outputs.java_matrix != '[]'
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
service: ${{ fromJson(needs.changes.outputs.java_matrix) }}
services:
postgres:
image: pgvector/pgvector:pg16
env:
POSTGRES_USER: jobready
POSTGRES_PASSWORD: jobready
POSTGRES_DB: jobready
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Set up Java 21
uses: actions/setup-java@v5
with:
java-version: 21
distribution: temurin
cache: maven
cache-dependency-path: services/${{ matrix.service }}/pom.xml
- name: Build and test
run: ./mvnw verify
working-directory: services/${{ matrix.service }}
env:
SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/jobready
SPRING_DATASOURCE_USERNAME: jobready
SPRING_DATASOURCE_PASSWORD: jobready
genai:
needs: changes
if: needs.changes.outputs.genai == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
working-directory: services/genai
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: "0.11.18"
enable-cache: true
cache-dependency-glob: "services/genai/uv.lock"
- name: Install dependencies
run: uv sync --locked --all-groups
- name: Lint (Ruff)
run: uv run --locked ruff check .
- name: Format check (Ruff)
run: uv run --locked ruff format --check .
- name: Typecheck (mypy)
run: uv run --locked mypy src/ tests/
# Gate sits below the ~81% the suite currently reaches, so it blocks a real
# regression without tripping on a line or two of churn. Raise it as coverage grows.
- name: Test (pytest)
run: uv run --locked python -m pytest tests/ --cov=src --cov-report=term-missing --cov-fail-under=78
web-client:
needs: changes
if: needs.changes.outputs.web-client == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
working-directory: web-client
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 20
cache: npm
cache-dependency-path: web-client/package-lock.json
- name: Install dependencies
run: npm ci
- name: Lint (ESLint)
run: npm run lint
- name: Format check (Prettier)
run: npm run format:check
# tsc -b inside `build` is the typecheck.
- name: Build
run: npm run build
- name: Test (Vitest)
run: npm run test
openapi:
needs: changes
if: needs.changes.outputs.openapi == 'true'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 20
- name: Lint OpenAPI spec
run: make -C api lint
# Lints the CI/CD surface itself: workflows (actionlint, via the pre-commit
# hook so the version pin lives once in .pre-commit-config.yaml) and
# Dockerfiles (hadolint binary — its pre-commit hook needs docker, so it is
# pinned here and runs only in CI).
infra-lint:
needs: changes
if: needs.changes.outputs.infra == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Cache pre-commit environments
uses: actions/cache@v6
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Lint workflows (actionlint)
run: |
pip install pre-commit
pre-commit run actionlint --all-files
- name: Lint Dockerfiles (hadolint)
run: |
curl -fsSL -o /usr/local/bin/hadolint \
https://github.com/hadolint/hadolint/releases/download/v2.14.0/hadolint-linux-x86_64
chmod +x /usr/local/bin/hadolint
git ls-files '*Dockerfile*' | xargs hadolint
# Black-box tests through the gateway against the real stack. These cover the seams
# the per-service suites structurally cannot: the jr_access cookie really becoming the
# bearer header a downstream service accepts, refresh tokens really being single-use in
# Redis, and ownership really holding with two users in one database.
#
# genai is deliberately not started — it calls a live LLM, which costs money per run and
# is not deterministic. Its logic is covered by unit tests in services/genai/tests.
e2e:
needs: changes
if: needs.changes.outputs.e2e == 'true'
runs-on: ubuntu-latest
# Four Spring services are built from source before anything can run.
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Write compose env
# Throwaway CI credentials. Compose interpolates vars for every service in the
# file — including ones this job never starts — but none are required-style
# (${VAR:?}), so the unset ones only warn. JWT keys must be generated: auth
# refuses to start without explicit keys (no ephemeral fallback), and a fresh
# throwaway pair per run is exactly what a torn-down single-replica stack needs.
run: |
cat > .env <<'EOF'
POSTGRES_USER=jobready
POSTGRES_PASSWORD=jobready
POSTGRES_DB=jobready
JWT_KEY_ID=auth-key-1
AUTH_REGISTER_MAX_ATTEMPTS=1000
EOF
./scripts/gen-jwt-keys.sh >> .env
- name: Start the stack
run: |
docker compose up -d --wait --quiet-pull \
postgres-db redis auth application document gateway
- name: Install uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
version: "0.11.18"
- name: Run end-to-end tests
env:
# Without this the suite would skip when the stack is down, and a skipped
# suite reports green — a stack that failed to boot must fail the build.
E2E_REQUIRE_STACK: "1"
run: uv run --with pytest --with httpx python -m pytest e2e_tests/ -v
- name: Service logs on failure
if: failure()
run: docker compose logs --tail=120 auth application document gateway
- name: Tear down
if: always()
run: docker compose down -v --remove-orphans
# The single check branch protection should require. `if: always()` is
# load-bearing: without it a failed dependency would SKIP this job, and
# skipped counts as passing — a gate must always run to be a gate.
ci-ok:
needs: [changes, java, genai, web-client, openapi, infra-lint, e2e]
if: always()
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Fail if any needed job failed or was cancelled
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: |
echo "::error::A CI job failed: ${{ toJSON(needs.*.result) }}"
exit 1
- run: echo "All CI jobs passed or were skipped as not affected."