diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..6c6bd39 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,130 @@ +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: | + echo "${CI_REGISTRY_PASSWORD}" | docker login "${GITEA_SERVER_URL}" -u "${CI_REGISTRY_USER}" --password-stdin + + - 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. + SHA_TAG="git.42i.org/spine/llmrouter:sha-${GITHUB_SHA:0:7}" + 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"