Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ updates:
# springdoc 3.x requires Spring Boot 4 (not planned), so its majors cannot pass CI.
- dependency-name: org.springdoc:springdoc-openapi-starter-webmvc-ui
update-types: ["version-update:semver-major"]
- dependency-name: org.springdoc:springdoc-openapi-starter-webflux-ui
update-types: ["version-update:semver-major"]
# Spring Cloud minor trains switch Spring Boot majors (CI: NoClassDefFoundError from a
# Boot 4 package) - bumped manually together with a Boot migration.
- dependency-name: org.springframework.cloud:spring-cloud-dependencies
update-types: ["version-update:semver-major", "version-update:semver-minor"]

- package-ecosystem: maven
directory: /services/user-service
Expand Down Expand Up @@ -86,6 +92,10 @@ updates:
# protobuf majors must move in lockstep with the grpcio toolchain - bumped manually.
- dependency-name: protobuf
update-types: ["version-update:semver-major"]
# grpcio minors regenerate gencode against protobuf 7 while the runtime stays on 6
# (CI: protobuf Gencode/Runtime VersionError) - the whole stack is bumped together, manually.
- dependency-name: "grpcio*"
update-types: ["version-update:semver-major", "version-update:semver-minor"]
# The engine depends on exact py-draughts behavior (even patch bumps break rule tests) -
# bumped manually with a full test review.
- dependency-name: py-draughts
Expand All @@ -109,8 +119,9 @@ updates:
# Java 21 LTS is the project's deliberate target; a new Java major/minor is a manual decision.
- dependency-name: eclipse-temurin
update-types: ["version-update:semver-major", "version-update:semver-minor"]
# The builder tag pins the JDK in its suffix (3.9-eclipse-temurin-21); semver on the
# Maven version cannot see a JDK change, so this image is bumped manually.
- dependency-name: maven
update-types: ["version-update:semver-major", "version-update:semver-minor"]

- package-ecosystem: docker
directory: /services/user-service
Expand All @@ -120,8 +131,9 @@ updates:
ignore:
- dependency-name: eclipse-temurin
update-types: ["version-update:semver-major", "version-update:semver-minor"]
# The builder tag pins the JDK in its suffix (3.9-eclipse-temurin-21); semver on the
# Maven version cannot see a JDK change, so this image is bumped manually.
- dependency-name: maven
update-types: ["version-update:semver-major", "version-update:semver-minor"]

- package-ecosystem: docker
directory: /services/game-service
Expand All @@ -131,8 +143,9 @@ updates:
ignore:
- dependency-name: eclipse-temurin
update-types: ["version-update:semver-major", "version-update:semver-minor"]
# The builder tag pins the JDK in its suffix (3.9-eclipse-temurin-21); semver on the
# Maven version cannot see a JDK change, so this image is bumped manually.
- dependency-name: maven
update-types: ["version-update:semver-major", "version-update:semver-minor"]

- package-ecosystem: docker
directory: /services/matchmaking-service
Expand All @@ -142,8 +155,9 @@ updates:
ignore:
- dependency-name: eclipse-temurin
update-types: ["version-update:semver-major", "version-update:semver-minor"]
# The builder tag pins the JDK in its suffix (3.9-eclipse-temurin-21); semver on the
# Maven version cannot see a JDK change, so this image is bumped manually.
- dependency-name: maven
update-types: ["version-update:semver-major", "version-update:semver-minor"]

- package-ecosystem: docker
directory: /services/game-engine-service
Expand Down
101 changes: 100 additions & 1 deletion .github/workflows/protobuf-contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ on:
- "services/matchmaking-service/src/**"
- "services/matchmaking-service/pom.xml"
- "services/game-engine-service/**"
- "services/*/Dockerfile"
- ".dockerignore"
- ".github/workflows/protobuf-contracts.yml"
pull_request:
branches: [dev, main]
Expand All @@ -30,6 +32,8 @@ on:
- "services/matchmaking-service/src/**"
- "services/matchmaking-service/pom.xml"
- "services/game-engine-service/**"
- "services/*/Dockerfile"
- ".dockerignore"
- ".github/workflows/protobuf-contracts.yml"

concurrency:
Expand Down Expand Up @@ -86,8 +90,44 @@ jobs:
python -m pip install --disable-pip-version-check check-jsonschema
check-jsonschema --check-metaschema \
checkers-contracts/http/v1/api-error.schema.json \
checkers-contracts/auth/v1/contract.schema.json \
checkers-contracts/auth/v1/access-token-claims.schema.json \
checkers-contracts/auth/v1/refresh-token-claims.schema.json
checkers-contracts/auth/v1/refresh-token-claims.schema.json \
checkers-contracts/ws/v1/game-state.schema.json \
checkers-contracts/ws/v1/match-found.schema.json
check-jsonschema \
--schemafile checkers-contracts/auth/v1/contract.schema.json \
checkers-contracts/auth/v1/contract.json
python - <<'PY'
import base64
import json
import os
from pathlib import Path

vectors = json.loads(
Path("checkers-contracts/auth/v1/test-vectors.json").read_text(encoding="utf-8")
)
encoded_payload = vectors["accessToken"].split(".")[1]
padding = "=" * (-len(encoded_payload) % 4)
token_claims = json.loads(base64.urlsafe_b64decode(encoded_payload + padding))
if token_claims != vectors["accessClaims"]:
raise SystemExit("accessClaims does not match the compact accessToken payload")

output = Path(os.environ["RUNNER_TEMP"]) / "auth-claim-instances"
output.mkdir()
(output / "access.json").write_text(
json.dumps(vectors["accessClaims"]), encoding="utf-8"
)
(output / "refresh.json").write_text(
json.dumps(vectors["refreshClaims"]), encoding="utf-8"
)
PY
check-jsonschema \
--schemafile checkers-contracts/auth/v1/access-token-claims.schema.json \
"$RUNNER_TEMP/auth-claim-instances/access.json"
check-jsonschema \
--schemafile checkers-contracts/auth/v1/refresh-token-claims.schema.json \
"$RUNNER_TEMP/auth-claim-instances/refresh.json"
- name: Reject breaking changes on pull requests
if: github.event_name == 'pull_request'
# Same baseline guard as the push variant below: a PR whose base predates the versioned
Expand All @@ -109,6 +149,65 @@ jobs:
echo "Establishing the initial checkers.v1 compatibility baseline."
fi

openapi-contracts:
name: OpenAPI snapshots and compatibility
runs-on: ubuntu-latest
env:
BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "21"
cache: maven
- name: Capture the REST contract baseline
run: |
mkdir -p "$RUNNER_TEMP/openapi-base"
if git cat-file -e "$BASE_SHA:checkers-contracts/openapi/user-service.json"; then
for service in user-service game-service matchmaking-service; do
git show "$BASE_SHA:checkers-contracts/openapi/$service.json" \
> "$RUNNER_TEMP/openapi-base/$service.json"
done
echo "OPENAPI_BASELINE=true" >> "$GITHUB_ENV"
else
echo "OPENAPI_BASELINE=false" >> "$GITHUB_ENV"
echo "Establishing the initial REST compatibility baseline."
fi
- name: Regenerate OpenAPI snapshots
run: |
bash services/user-service/mvnw --batch-mode \
-f services/user-service/pom.xml -Dtest=OpenApiSnapshotTest test
bash services/game-service/mvnw --batch-mode \
-f services/game-service/pom.xml '-Dtest=GameControllerMockMvcTest#snapshotsOpenApiContract' test
bash services/matchmaking-service/mvnw --batch-mode \
-f services/matchmaking-service/pom.xml '-Dtest=QueueControllerMockMvcTest#snapshotsOpenApiContract' test
- name: Reject snapshot drift
run: git diff --exit-code -- checkers-contracts/openapi
- name: Reject breaking REST changes
if: env.OPENAPI_BASELINE == 'true'
# Escape hatch for deliberate spec corrections (e.g. a springdoc upgrade starts emitting
# `required` that Bean Validation always enforced): the openapi-breaking-approved label on
# a PR, or an [openapi-rebaseline] marker in a pushed commit message, downgrades the check
# to report-only for that run. The diff is still printed for review.
env:
BREAKING_APPROVED: ${{ (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'openapi-breaking-approved')) || (github.event_name == 'push' && contains(github.event.head_commit.message, '[openapi-rebaseline]')) }}
run: |
FAIL_FLAG="--fail-on-incompatible"
if [ "$BREAKING_APPROVED" = "true" ]; then
echo "Breaking-change rejection disabled for this run (approved spec correction)."
FAIL_FLAG=""
fi
for service in user-service game-service matchmaking-service; do
docker run --rm \
-v "$RUNNER_TEMP/openapi-base:/baseline:ro" \
-v "$PWD/checkers-contracts/openapi:/current:ro" \
openapitools/openapi-diff:2.1.0 \
$FAIL_FLAG "/baseline/$service.json" "/current/$service.json"
done

java-services:
name: Java build & tests (${{ matrix.service }})
runs-on: ubuntu-latest
Expand Down
Loading