name: llmrouter CI # Test- und Image-Bau nach dem Muster der live-Workflows (42i/live # .gitea/workflows/dev-image.yml): Positive paths-Listen statt paths-ignore # (live#2266), concurrency-Block für Gitea 1.26 (live#1350), buildx mit # eigenem Builder hinter --privileged und DOCKER_API_VERSION 1.43, # Registry-Login über die Repo-Secrets user/password, Verify-Pull als # Abschluss (42i/intern#1101). on: push: branches: [main] paths: - "src/**" - "test/**" - "config.json" - "key-acls.json" - "package.json" - "docker/**" - "docker-compose.yml" - ".gitea/workflows/ci.yml" pull_request: paths: - "src/**" - "test/**" - "config.json" - "key-acls.json" - "package.json" - "docker/**" - "docker-compose.yml" - ".gitea/workflows/ci.yml" workflow_dispatch: {} concurrency: group: ci-${{ github.ref }} cancel-in-progress: true jobs: test: name: bun test + build runs-on: docker container: image: git.42i.org/42i/dev:latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install run: bun install - name: Test run: bun test - name: Build (statisches Linux-Binary) run: bun run build image: # Pure CD wie in live: jeder Push nach main baut und published; # Pull Requests testen nur. if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: test name: Build and publish image runs-on: docker container: image: git.42i.org/42i/dev:latest # docker buildx build braucht Zugriff auf den Docker-Host. options: --privileged env: GITEA_SERVER_URL: https://git.42i.org CI_REGISTRY_USER: ${{ secrets.user }} CI_REGISTRY_PASSWORD: ${{ secrets.password }} DOCKER_API_VERSION: "1.43" BUILDER: llmrouter-ci IMAGE: git.42i.org/spine/llmrouter:latest steps: - name: Checkout uses: actions/checkout@v4 - name: Docker login run: | # Repo-Secrets USER/PASSWORD tragen eltons Registry-Login und # übersteuern bewusst die Org-Secrets der spine-Org (dort # liegt andreas' Zugang). Die 401er vom 2026-09-08 waren # vertauschte Secret-Werte (Passwort im USER-Secret), kein # Serverproblem; der Retry bleibt gegen echte Aussetzer. n=0 until echo "${CI_REGISTRY_PASSWORD}" | docker login "${GITEA_SERVER_URL}" -u "${CI_REGISTRY_USER}" --password-stdin; do n=$((n+1)) [ "$n" -ge 3 ] && exit 1 echo "docker login Versuch $n fehlgeschlagen, warte 5s" >&2 sleep 5 done - name: Prepare builder run: | docker buildx inspect "${BUILDER}" >/dev/null 2>&1 || \ docker buildx create --name "${BUILDER}" --driver docker-container --use docker buildx use "${BUILDER}" docker buildx inspect "${BUILDER}" --bootstrap - name: Build and push run: | # Nur linux/amd64: einziger Konsument ist der LXC auf x86. # POSIX, kein bash-Slice: die Job-Shell ist sh -e. SHA_TAG="git.42i.org/spine/llmrouter:sha-$(printf '%.7s' "$GITHUB_SHA")" docker buildx build \ --builder "${BUILDER}" \ --platform linux/amd64 \ -f docker/Dockerfile \ -t "${IMAGE}" \ -t "${SHA_TAG}" \ --push \ . - name: Prune builder cache if: always() run: | docker buildx prune --builder "${BUILDER}" -af || true # Dem Push-Erfolg allein vertrauen wir nicht (42i/intern#1101) — frisch # pullen, starten und /health im Container selbst prüfen (ohne # Host-Ports, keine Secrets nötig: der Router lauscht auch ohne). - name: Verify pull and health run: | set -u docker pull "${IMAGE}" CID=$(docker run -d --rm "${IMAGE}") OK=0 for i in $(seq 1 15); do if docker exec "$CID" bun -e 'const r = await fetch("http://127.0.0.1:4010/health"); process.exit(r.ok ? 0 : 1)'; then OK=1 break fi sleep 2 done if [ "$OK" -ne 1 ]; then echo "health check failed" >&2 docker logs "$CID" 2>&1 || true docker rm -f "$CID" 2>/dev/null || true exit 1 fi echo "llmrouter image verified: /health ok" docker stop -t 2 "$CID"