Loading .gitignore +4 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,10 @@ __pycache__/ .mypy_cache/ .ruff_cache/ .pytest_cache/ .gitlab-ci-local/ .coverage htmlcov/ coverage.xml src/__pycache__/ src/models/__pycache__/ src/controllers/__pycache__/ Loading .gitlab-ci.yml +70 −65 Original line number Diff line number Diff line # GitLab CI/CD Pipeline for Federation Manager # This pipeline builds and pushes Docker images for the federation-manager service # Mirrors the OEG and SRM pipeline; keep the three in step. default: image: python:3.12-slim cache: paths: - .cache/uv before_script: - cd "$CI_PROJECT_DIR/${BACKEND_DIR:-.}" - pip install uv - export UV_SYSTEM_CERTS=1 - uv sync --locked --extra dev - . .venv/bin/activate stages: - check - build - type - architecture - lint - format - test - build-and-push variables: UV_CACHE_DIR: "$CI_PROJECT_DIR/.cache/uv" type: stage: type script: - mypy architecture: stage: architecture script: - lint-imports lint: stage: lint script: - ruff check src/ tests/ # Lint, type-check and test on every push and merge request (issue #10). check: stage: check image: python:3.12 tags: - docker format: stage: format script: - ruff format --check src/ tests/ test: stage: test services: - name: postgres:16-alpine alias: postgres - name: nats:2.10-alpine alias: nats command: ["-js"] - name: quay.io/keycloak/keycloak:26.1.4 alias: keycloak command: ["start-dev"] variables: POSTGRES_USER: fm POSTGRES_PASSWORD: fm POSTGRES_DB: fm_db KC_BOOTSTRAP_ADMIN_USERNAME: admin KC_BOOTSTRAP_ADMIN_PASSWORD: admin KEYCLOAK_URL: "http://keycloak:8080" FM_POSTGRES_URL: "postgresql+asyncpg://fm:fm@postgres:5432/fm_db" FM_POSTGRES_ROOT: "postgresql+asyncpg://fm:fm@postgres:5432" FM_NATS_URL: "nats://nats:4222" before_script: - python -m pip install --upgrade pip - pip install -e ".[dev]" FM_KEYCLOAK_ISSUER: "http://keycloak:8080/realms/federation" script: - ruff format --check src/federation_manager tests - ruff check src/federation_manager tests - mypy # Keycloak is not available here, so the tests needing it are deselected. - pytest -q -k "not two_stack" variables: IMAGE_NAME: federation-manager REGISTRY_IMAGE: $CI_REGISTRY_IMAGE/$IMAGE_NAME # services cannot bind-mount the realm file - python scripts/import_keycloak_realm.py - pytest --cov=src/federation_manager --cov-branch --cov-report=term-missing coverage: '/TOTAL.+ ([0-9]{1,3}(?:\.[0-9]+)?%)/' # Build Docker image for merge requests (no push) build-mr: stage: build image: name: gcr.io/kaniko-project/executor:v1.14.0-debug entrypoint: [""] tags: - docker before_script: - echo "Skip pushing for merge requests" script: - echo "Building image for merge request validation..." - /kaniko/executor --context "$CI_PROJECT_DIR" --dockerfile "$CI_PROJECT_DIR/Dockerfile" --no-push - echo "Image built successfully for MR validation" only: - merge_requests # Build and push Docker image for main/develop/tags with specific tagging build-and-push: stage: build image: name: gcr.io/kaniko-project/executor:v1.14.0-debug entrypoint: [""] tags: - docker stage: build-and-push image: docker:cli variables: DOCKER_HOST: tcp://docker:2375 DOCKER_TLS_CERTDIR: "" services: - docker:dind before_script: - mkdir -p /kaniko/.docker - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json - | if [ "$CI_COMMIT_REF_NAME" = "main" ]; then export IMAGE_TAG="latest" elif [ "$CI_COMMIT_REF_NAME" = "develop" ]; then export IMAGE_TAG="develop" elif [ -n "$CI_COMMIT_TAG" ]; then export IMAGE_TAG="$CI_COMMIT_TAG" else export IMAGE_TAG="$CI_COMMIT_REF_NAME" fi echo "Using image tag: $IMAGE_TAG" - docker info script: - echo "Building image and pushing to registry..." - /kaniko/executor --context "$CI_PROJECT_DIR" --dockerfile "$CI_PROJECT_DIR/Dockerfile" --destination "$REGISTRY_IMAGE:$IMAGE_TAG" only: - main - develop - tags - export TEST_IMAGE_TAG="ci-${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}" - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin - docker buildx build --provenance=false --network=host -t "$CI_REGISTRY_IMAGE:$TEST_IMAGE_TAG" --push . - docker logout "$CI_REGISTRY" rules: - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop"' CONTRIBUTING.md +22 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,28 @@ We welcome contributions from the community to help improve and evolve this open 4. **Update documentation** as needed to reflect your changes. 5. **Submit a pull request** with a clear description of your changes. ## Local checks CI runs these, in this order. Run them before opening a merge request: ```bash uv sync --locked --extra dev # or: pip install -e ".[dev]" mypy lint-imports # hexagonal layering contracts ruff check src/ tests/ ruff format --check src/ tests/ pytest --cov=src/federation_manager --cov-branch --cov-report=term-missing ``` Tests marked `integration` need the dev stack: ```bash docker compose -f docker-compose.dev.yaml up -d postgres nats keycloak ``` `uv.lock` is committed and CI installs with `--locked`, so run `uv lock` whenever you change dependencies in `pyproject.toml`. ## Code Guidelines - Follow [PEP8](https://peps.python.org/pep-0008/) coding style. Loading README.md +1 −1 Original line number Diff line number Diff line Loading @@ -76,7 +76,7 @@ uvicorn federation_manager.main:app --reload --port 8082 ```bash pytest # everything pytest -m "not integration" # unit tests only, no infrastructure needed ruff check src tests && mypy mypy && lint-imports && ruff check src/ tests/ ``` Integration tests need the dev stack above. The FM ↔ SRM loop test additionally needs a running Loading pyproject.toml +21 −0 Original line number Diff line number Diff line Loading @@ -20,9 +20,11 @@ dependencies = [ [project.optional-dependencies] dev = [ "import-linter>=2.0", "mypy>=1.11", "pytest>=8.0", "pytest-asyncio>=0.24", "pytest-cov>=6.0", "pyyaml>=6.0", "ruff>=0.6", ] Loading @@ -44,6 +46,25 @@ strict = true ignore_missing_imports = true files = ["src/federation_manager", "tests"] [tool.importlinter] root_package = "federation_manager" [[tool.importlinter.contracts]] name = "Domain independence" type = "forbidden" source_modules = ["federation_manager.domain"] forbidden_modules = [ "federation_manager.application", "federation_manager.api", "federation_manager.adapters", ] [[tool.importlinter.contracts]] name = "Application cannot import infrastructure" type = "forbidden" source_modules = ["federation_manager.application"] forbidden_modules = ["federation_manager.api", "federation_manager.adapters"] [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" Loading Loading
.gitignore +4 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,10 @@ __pycache__/ .mypy_cache/ .ruff_cache/ .pytest_cache/ .gitlab-ci-local/ .coverage htmlcov/ coverage.xml src/__pycache__/ src/models/__pycache__/ src/controllers/__pycache__/ Loading
.gitlab-ci.yml +70 −65 Original line number Diff line number Diff line # GitLab CI/CD Pipeline for Federation Manager # This pipeline builds and pushes Docker images for the federation-manager service # Mirrors the OEG and SRM pipeline; keep the three in step. default: image: python:3.12-slim cache: paths: - .cache/uv before_script: - cd "$CI_PROJECT_DIR/${BACKEND_DIR:-.}" - pip install uv - export UV_SYSTEM_CERTS=1 - uv sync --locked --extra dev - . .venv/bin/activate stages: - check - build - type - architecture - lint - format - test - build-and-push variables: UV_CACHE_DIR: "$CI_PROJECT_DIR/.cache/uv" type: stage: type script: - mypy architecture: stage: architecture script: - lint-imports lint: stage: lint script: - ruff check src/ tests/ # Lint, type-check and test on every push and merge request (issue #10). check: stage: check image: python:3.12 tags: - docker format: stage: format script: - ruff format --check src/ tests/ test: stage: test services: - name: postgres:16-alpine alias: postgres - name: nats:2.10-alpine alias: nats command: ["-js"] - name: quay.io/keycloak/keycloak:26.1.4 alias: keycloak command: ["start-dev"] variables: POSTGRES_USER: fm POSTGRES_PASSWORD: fm POSTGRES_DB: fm_db KC_BOOTSTRAP_ADMIN_USERNAME: admin KC_BOOTSTRAP_ADMIN_PASSWORD: admin KEYCLOAK_URL: "http://keycloak:8080" FM_POSTGRES_URL: "postgresql+asyncpg://fm:fm@postgres:5432/fm_db" FM_POSTGRES_ROOT: "postgresql+asyncpg://fm:fm@postgres:5432" FM_NATS_URL: "nats://nats:4222" before_script: - python -m pip install --upgrade pip - pip install -e ".[dev]" FM_KEYCLOAK_ISSUER: "http://keycloak:8080/realms/federation" script: - ruff format --check src/federation_manager tests - ruff check src/federation_manager tests - mypy # Keycloak is not available here, so the tests needing it are deselected. - pytest -q -k "not two_stack" variables: IMAGE_NAME: federation-manager REGISTRY_IMAGE: $CI_REGISTRY_IMAGE/$IMAGE_NAME # services cannot bind-mount the realm file - python scripts/import_keycloak_realm.py - pytest --cov=src/federation_manager --cov-branch --cov-report=term-missing coverage: '/TOTAL.+ ([0-9]{1,3}(?:\.[0-9]+)?%)/' # Build Docker image for merge requests (no push) build-mr: stage: build image: name: gcr.io/kaniko-project/executor:v1.14.0-debug entrypoint: [""] tags: - docker before_script: - echo "Skip pushing for merge requests" script: - echo "Building image for merge request validation..." - /kaniko/executor --context "$CI_PROJECT_DIR" --dockerfile "$CI_PROJECT_DIR/Dockerfile" --no-push - echo "Image built successfully for MR validation" only: - merge_requests # Build and push Docker image for main/develop/tags with specific tagging build-and-push: stage: build image: name: gcr.io/kaniko-project/executor:v1.14.0-debug entrypoint: [""] tags: - docker stage: build-and-push image: docker:cli variables: DOCKER_HOST: tcp://docker:2375 DOCKER_TLS_CERTDIR: "" services: - docker:dind before_script: - mkdir -p /kaniko/.docker - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json - | if [ "$CI_COMMIT_REF_NAME" = "main" ]; then export IMAGE_TAG="latest" elif [ "$CI_COMMIT_REF_NAME" = "develop" ]; then export IMAGE_TAG="develop" elif [ -n "$CI_COMMIT_TAG" ]; then export IMAGE_TAG="$CI_COMMIT_TAG" else export IMAGE_TAG="$CI_COMMIT_REF_NAME" fi echo "Using image tag: $IMAGE_TAG" - docker info script: - echo "Building image and pushing to registry..." - /kaniko/executor --context "$CI_PROJECT_DIR" --dockerfile "$CI_PROJECT_DIR/Dockerfile" --destination "$REGISTRY_IMAGE:$IMAGE_TAG" only: - main - develop - tags - export TEST_IMAGE_TAG="ci-${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}" - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin - docker buildx build --provenance=false --network=host -t "$CI_REGISTRY_IMAGE:$TEST_IMAGE_TAG" --push . - docker logout "$CI_REGISTRY" rules: - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop"'
CONTRIBUTING.md +22 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,28 @@ We welcome contributions from the community to help improve and evolve this open 4. **Update documentation** as needed to reflect your changes. 5. **Submit a pull request** with a clear description of your changes. ## Local checks CI runs these, in this order. Run them before opening a merge request: ```bash uv sync --locked --extra dev # or: pip install -e ".[dev]" mypy lint-imports # hexagonal layering contracts ruff check src/ tests/ ruff format --check src/ tests/ pytest --cov=src/federation_manager --cov-branch --cov-report=term-missing ``` Tests marked `integration` need the dev stack: ```bash docker compose -f docker-compose.dev.yaml up -d postgres nats keycloak ``` `uv.lock` is committed and CI installs with `--locked`, so run `uv lock` whenever you change dependencies in `pyproject.toml`. ## Code Guidelines - Follow [PEP8](https://peps.python.org/pep-0008/) coding style. Loading
README.md +1 −1 Original line number Diff line number Diff line Loading @@ -76,7 +76,7 @@ uvicorn federation_manager.main:app --reload --port 8082 ```bash pytest # everything pytest -m "not integration" # unit tests only, no infrastructure needed ruff check src tests && mypy mypy && lint-imports && ruff check src/ tests/ ``` Integration tests need the dev stack above. The FM ↔ SRM loop test additionally needs a running Loading
pyproject.toml +21 −0 Original line number Diff line number Diff line Loading @@ -20,9 +20,11 @@ dependencies = [ [project.optional-dependencies] dev = [ "import-linter>=2.0", "mypy>=1.11", "pytest>=8.0", "pytest-asyncio>=0.24", "pytest-cov>=6.0", "pyyaml>=6.0", "ruff>=0.6", ] Loading @@ -44,6 +46,25 @@ strict = true ignore_missing_imports = true files = ["src/federation_manager", "tests"] [tool.importlinter] root_package = "federation_manager" [[tool.importlinter.contracts]] name = "Domain independence" type = "forbidden" source_modules = ["federation_manager.domain"] forbidden_modules = [ "federation_manager.application", "federation_manager.api", "federation_manager.adapters", ] [[tool.importlinter.contracts]] name = "Application cannot import infrastructure" type = "forbidden" source_modules = ["federation_manager.application"] forbidden_modules = ["federation_manager.api", "federation_manager.adapters"] [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" Loading