# Coordinator Pipeline - Mosaic Stack # Quality gates, build, and Docker publish for mosaic-coordinator (Python) # # Triggers on: apps/coordinator/** # Security chain: bandit + pip-audit + Trivy container scan when: - event: [push, pull_request, manual] path: include: - "apps/coordinator/**" - ".woodpecker/coordinator.yml" variables: - &python_image "python:3.11-slim" - &activate_venv | cd apps/coordinator . venv/bin/activate - &kaniko_setup | mkdir -p /kaniko/.docker echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$GITEA_USER\",\"password\":\"$GITEA_TOKEN\"}}}" > /kaniko/.docker/config.json steps: # === Quality Gates === install: image: *python_image commands: - cd apps/coordinator - python -m venv venv - . venv/bin/activate - pip install --no-cache-dir --upgrade "pip>=25.3" - pip install --no-cache-dir --extra-index-url https://git.mosaicstack.dev/api/packages/mosaic/pypi/simple/ -e ".[dev]" - pip install --no-cache-dir bandit pip-audit ruff-check: image: *python_image commands: - *activate_venv - ruff check src/ tests/ depends_on: - install mypy: image: *python_image commands: - *activate_venv - mypy src/ depends_on: - install security-bandit: image: *python_image commands: - *activate_venv - bandit -r src/ -c bandit.yaml -f screen depends_on: - install security-pip-audit: image: *python_image commands: - *activate_venv - pip-audit depends_on: - install test: image: *python_image commands: - *activate_venv - pytest depends_on: - install # === Docker Build & Push === docker-build-coordinator: image: gcr.io/kaniko-project/executor:debug environment: GITEA_USER: from_secret: gitea_username GITEA_TOKEN: from_secret: gitea_token CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH} CI_COMMIT_TAG: ${CI_COMMIT_TAG} commands: - *kaniko_setup - | DESTINATIONS="" if [ -n "$CI_COMMIT_TAG" ]; then DESTINATIONS="--destination git.mosaicstack.dev/mosaic/stack-coordinator:$CI_COMMIT_TAG" elif [ "$CI_COMMIT_BRANCH" = "main" ]; then DESTINATIONS="--destination git.mosaicstack.dev/mosaic/stack-coordinator:latest" fi /kaniko/executor --context apps/coordinator --dockerfile apps/coordinator/Dockerfile --snapshot-mode=redo $DESTINATIONS when: - branch: [main] event: [push, manual, tag] depends_on: - ruff-check - mypy - security-bandit - security-pip-audit - test # === Container Security Scan === security-trivy-coordinator: image: aquasec/trivy:latest environment: GITEA_USER: from_secret: gitea_username GITEA_TOKEN: from_secret: gitea_token CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH} CI_COMMIT_TAG: ${CI_COMMIT_TAG} commands: - | if [ -n "$$CI_COMMIT_TAG" ]; then SCAN_TAG="$$CI_COMMIT_TAG" elif [ "$$CI_COMMIT_BRANCH" = "main" ]; then SCAN_TAG="latest" else SCAN_TAG="latest" fi mkdir -p ~/.docker echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$$GITEA_USER\",\"password\":\"$$GITEA_TOKEN\"}}}" > ~/.docker/config.json trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed \ --ignorefile .trivyignore \ git.mosaicstack.dev/mosaic/stack-coordinator:$$SCAN_TAG when: - branch: [main] event: [push, manual, tag] depends_on: - docker-build-coordinator # === Package Linking === link-packages: image: alpine:3 environment: GITEA_TOKEN: from_secret: gitea_token commands: - apk add --no-cache curl - sleep 10 - | set -e link_package() { PKG="$$1" echo "Linking $$PKG..." for attempt in 1 2 3; do STATUS=$$(curl -s -o /tmp/link-response.txt -w "%{http_code}" -X POST \ -H "Authorization: token $$GITEA_TOKEN" \ "https://git.mosaicstack.dev/api/v1/packages/mosaic/container/$$PKG/-/link/stack") if [ "$$STATUS" = "201" ] || [ "$$STATUS" = "204" ]; then echo " Linked $$PKG" return 0 elif [ "$$STATUS" = "400" ]; then echo " $$PKG already linked" return 0 elif [ "$$STATUS" = "404" ] && [ $$attempt -lt 3 ]; then echo " $$PKG not found yet, retrying in 5s (attempt $$attempt/3)..." sleep 5 else echo " FAILED: $$PKG status $$STATUS" cat /tmp/link-response.txt return 1 fi done } link_package "stack-coordinator" when: - branch: [main] event: [push, manual, tag] depends_on: - security-trivy-coordinator