From ea78102b89d905e375b2de335ac76734e05ff04d Mon Sep 17 00:00:00 2001 From: Adil Hafeez Date: Sun, 15 Feb 2026 02:15:23 +0000 Subject: [PATCH] Consolidate GitHub Actions CI from 15 to 5 workflow files Reduce Docker builds from 10 to 1 per PR by building the image once and sharing it as an artifact across all dependent jobs. Merge duplicate Docker Hub and GHCR push workflows into single workflows that push to both registries per build. - ci.yml: replaces pre-commit, rust_tests, validate_plano_config, plano_tools_tests, docker-security-scan, e2e_tests, e2e_plano_tests, e2e_test_preference_based_routing, e2e_test_currency_convert - docker-push-main.yml: replaces old docker-push-main + ghrc-push-main - docker-push-release.yml: replaces old docker-push-release + ghrc-push-release - static.yml and publish-pypi.yml unchanged Co-Authored-By: Claude Opus 4.6 --- .github/workflows/ci.yml | 480 ++++++++++++++++++ .github/workflows/docker-push-main.yml | 65 ++- .github/workflows/docker-push-release.yml | 59 ++- .github/workflows/docker-security-scan.yml | 56 -- .github/workflows/e2e_plano_tests.yml | 72 --- .../workflows/e2e_test_currency_convert.yml | 54 -- .../e2e_test_preference_based_routing.yml | 56 -- .github/workflows/e2e_tests.yml | 203 -------- .github/workflows/ghrc-push-main.yml | 88 ---- .github/workflows/ghrc-push-release.yml | 87 ---- .github/workflows/plano_tools_tests.yml | 37 -- .github/workflows/pre-commit.yml | 17 - .github/workflows/rust_tests.yml | 34 -- .github/workflows/validate_plano_config.yml | 34 -- 14 files changed, 564 insertions(+), 778 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/docker-security-scan.yml delete mode 100644 .github/workflows/e2e_plano_tests.yml delete mode 100644 .github/workflows/e2e_test_currency_convert.yml delete mode 100644 .github/workflows/e2e_test_preference_based_routing.yml delete mode 100644 .github/workflows/e2e_tests.yml delete mode 100644 .github/workflows/ghrc-push-main.yml delete mode 100644 .github/workflows/ghrc-push-release.yml delete mode 100644 .github/workflows/plano_tools_tests.yml delete mode 100644 .github/workflows/pre-commit.yml delete mode 100644 .github/workflows/rust_tests.yml delete mode 100644 .github/workflows/validate_plano_config.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..e76d260c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,480 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + security-events: write + +env: + PLANO_DOCKER_IMAGE: katanemo/plano:e2e + DOCKER_IMAGE: katanemo/plano + +jobs: + # ────────────────────────────────────────────── + # Pre-commit (fmt, clippy, cargo test, black, yaml) + # ────────────────────────────────────────────── + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - uses: pre-commit/action@v3.0.1 + + # ────────────────────────────────────────────── + # Plano tools (CLI) tests — no Docker needed + # ────────────────────────────────────────────── + plano-tools-tests: + runs-on: ubuntu-latest-m + defaults: + run: + working-directory: ./cli + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + run: curl -LsSf https://astral.sh/uv/install.sh | sh + + - name: Install plano tools + run: uv sync --extra dev + + - name: Run tests + run: uv run pytest + + # ────────────────────────────────────────────── + # Single Docker build — shared by all downstream jobs + # ────────────────────────────────────────────── + docker-build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Free disk space on runner + run: | + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + docker system prune -af || true + docker volume prune -f || true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build plano image (with GHA cache) + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + load: true + tags: | + ${{ env.PLANO_DOCKER_IMAGE }} + ${{ env.DOCKER_IMAGE }}:0.4.6 + ${{ env.DOCKER_IMAGE }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Save image as artifact + run: docker save ${{ env.PLANO_DOCKER_IMAGE }} ${{ env.DOCKER_IMAGE }}:0.4.6 ${{ env.DOCKER_IMAGE }}:latest -o /tmp/plano-image.tar + + - name: Upload image artifact + uses: actions/upload-artifact@v4 + with: + name: plano-image + path: /tmp/plano-image.tar + retention-days: 1 + + # ────────────────────────────────────────────── + # Validate plano config + # ────────────────────────────────────────────── + validate-config: + needs: docker-build + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Download plano image + uses: actions/download-artifact@v4 + with: + name: plano-image + path: /tmp + + - name: Load plano image + run: docker load -i /tmp/plano-image.tar + + - name: Validate plano config + run: bash config/validate_plano_config.sh + + # ────────────────────────────────────────────── + # Docker security scan (Trivy) + # ────────────────────────────────────────────── + security-scan: + needs: docker-build + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download plano image + uses: actions/download-artifact@v4 + with: + name: plano-image + path: /tmp + + - name: Load plano image + run: docker load -i /tmp/plano-image.tar + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.DOCKER_IMAGE }}:latest + format: table + exit-code: ${{ github.event_name == 'pull_request' && '1' || '0' }} + ignore-unfixed: true + severity: CRITICAL,HIGH + + - name: Run Trivy scanner (SARIF for GitHub Security tab) + if: always() + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.DOCKER_IMAGE }}:latest + format: sarif + output: trivy-results.sarif + ignore-unfixed: true + severity: CRITICAL,HIGH + + - name: Upload Trivy results to GitHub Security tab + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-results.sarif + + # ────────────────────────────────────────────── + # E2E: prompt_gateway tests + # ────────────────────────────────────────────── + test-prompt-gateway: + needs: docker-build + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Free disk space on runner + run: | + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + docker system prune -af || true + docker volume prune -f || true + + - name: Download plano image + uses: actions/download-artifact@v4 + with: + name: plano-image + path: /tmp + + - name: Load plano image + run: docker load -i /tmp/plano-image.tar + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + cache-dependency-glob: | + tests/e2e/uv.lock + cli/uv.lock + + - name: Run prompt_gateway tests + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} + AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} + GROK_API_KEY: ${{ secrets.GROK_API_KEY }} + run: | + cd tests/e2e && bash run_prompt_gateway_tests.sh + + # ────────────────────────────────────────────── + # E2E: model_alias_routing tests + # ────────────────────────────────────────────── + test-model-alias-routing: + needs: docker-build + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Free disk space on runner + run: | + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + docker system prune -af || true + docker volume prune -f || true + + - name: Download plano image + uses: actions/download-artifact@v4 + with: + name: plano-image + path: /tmp + + - name: Load plano image + run: docker load -i /tmp/plano-image.tar + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + cache-dependency-glob: | + tests/e2e/uv.lock + cli/uv.lock + + - name: Run model alias routing tests + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} + AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} + GROK_API_KEY: ${{ secrets.GROK_API_KEY }} + run: | + cd tests/e2e && bash run_model_alias_tests.sh + + # ────────────────────────────────────────────── + # E2E: responses API with state tests + # ────────────────────────────────────────────── + test-responses-api-with-state: + needs: docker-build + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Free disk space on runner + run: | + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + docker system prune -af || true + docker volume prune -f || true + + - name: Download plano image + uses: actions/download-artifact@v4 + with: + name: plano-image + path: /tmp + + - name: Load plano image + run: docker load -i /tmp/plano-image.tar + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + cache-dependency-glob: | + tests/e2e/uv.lock + cli/uv.lock + + - name: Run responses API with state tests + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} + AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} + GROK_API_KEY: ${{ secrets.GROK_API_KEY }} + run: | + cd tests/e2e && bash run_responses_state_tests.sh + + # ────────────────────────────────────────────── + # E2E: plano tests (multi-Python matrix) + # ────────────────────────────────────────────── + e2e-plano-tests: + needs: docker-build + runs-on: ubuntu-latest-m + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + defaults: + run: + working-directory: ./tests/archgw + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: "pip" + + - name: Download plano image + uses: actions/download-artifact@v4 + with: + name: plano-image + path: /tmp + + - name: Load plano image + run: docker load -i /tmp/plano-image.tar + + - name: Start plano + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} + AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} + run: | + docker compose up | tee &> plano.logs & + + - name: Wait for plano to be healthy + run: | + source common.sh && wait_for_healthz http://localhost:10000/healthz + + - name: Install uv + run: curl -LsSf https://astral.sh/uv/install.sh | sh + + - name: Install test dependencies + run: uv sync + + - name: Run plano tests + run: | + uv run pytest || tail -100 plano.logs + + - name: Stop plano docker container + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: | + docker compose down + + # ────────────────────────────────────────────── + # E2E: demo — preference based routing + # ────────────────────────────────────────────── + e2e-demo-preference: + needs: docker-build + runs-on: ubuntu-latest-m + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Download plano image + uses: actions/download-artifact@v4 + with: + name: plano-image + path: /tmp + + - name: Load plano image + run: docker load -i /tmp/plano-image.tar + + - name: Install uv + run: curl -LsSf https://astral.sh/uv/install.sh | sh + + - name: Setup python venv + run: python -m venv venv + + - name: Install hurl + run: | + curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/4.0.0/hurl_4.0.0_amd64.deb + sudo dpkg -i hurl_4.0.0_amd64.deb + + - name: Install plano gateway and test dependencies + run: | + source venv/bin/activate + cd cli && echo "installing plano cli" && uv sync && uv tool install . + cd ../demos/shared/test_runner && echo "installing test dependencies" && uv sync + + - name: Run demo tests + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + ARCH_API_KEY: ${{ secrets.ARCH_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: | + source venv/bin/activate + cd demos/shared/test_runner && sh run_demo_tests.sh use_cases/preference_based_routing + + # ────────────────────────────────────────────── + # E2E: demo — currency conversion + # ────────────────────────────────────────────── + e2e-demo-currency: + needs: docker-build + runs-on: ubuntu-latest-m + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Download plano image + uses: actions/download-artifact@v4 + with: + name: plano-image + path: /tmp + + - name: Load plano image + run: docker load -i /tmp/plano-image.tar + + - name: Install uv + run: curl -LsSf https://astral.sh/uv/install.sh | sh + + - name: Setup python venv + run: python -m venv venv + + - name: Install hurl + run: | + curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/4.0.0/hurl_4.0.0_amd64.deb + sudo dpkg -i hurl_4.0.0_amd64.deb + + - name: Install plano gateway and test dependencies + run: | + source venv/bin/activate + cd cli && echo "installing plano cli" && uv sync && uv tool install . + cd ../demos/shared/test_runner && echo "installing test dependencies" && uv sync + + - name: Run demo tests + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + run: | + source venv/bin/activate + cd demos/shared/test_runner && sh run_demo_tests.sh samples_python/currency_exchange diff --git a/.github/workflows/docker-push-main.yml b/.github/workflows/docker-push-main.yml index 224f2682..d62b91d6 100644 --- a/.github/workflows/docker-push-main.yml +++ b/.github/workflows/docker-push-main.yml @@ -2,6 +2,7 @@ name: Publish docker image (latest) env: DOCKER_IMAGE: katanemo/plano + GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/plano on: push: @@ -10,9 +11,10 @@ on: permissions: contents: read + packages: write jobs: - # Build ARM64 image on native ARM64 runner. + # Build ARM64 image on native ARM64 runner — push to both registries build-arm64: runs-on: [linux-arm64] steps: @@ -25,13 +27,12 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 + - name: Log in to GHCR + uses: docker/login-action@v3 with: - images: ${{ env.DOCKER_IMAGE }} - tags: | - type=raw,value=latest # Force the tag to be "latest" + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and Push ARM64 Image uses: docker/build-push-action@v5 @@ -40,9 +41,11 @@ jobs: file: ./Dockerfile platforms: linux/arm64 push: true - tags: ${{ steps.meta.outputs.tags }}-arm64 + tags: | + ${{ env.DOCKER_IMAGE }}:latest-arm64 + ${{ env.GHCR_IMAGE }}:latest-arm64 - # Build AMD64 image on GitHub's AMD64 runner + # Build AMD64 image on GitHub's AMD64 runner — push to both registries build-amd64: runs-on: ubuntu-latest steps: @@ -55,13 +58,12 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 + - name: Log in to GHCR + uses: docker/login-action@v3 with: - images: ${{ env.DOCKER_IMAGE }} - tags: | - type=raw,value=latest # Force the tag to be "latest" + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Build and Push AMD64 Image uses: docker/build-push-action@v5 @@ -70,13 +72,14 @@ jobs: file: ./Dockerfile platforms: linux/amd64 push: true - tags: ${{ steps.meta.outputs.tags }}-amd64 + tags: | + ${{ env.DOCKER_IMAGE }}:latest-amd64 + ${{ env.GHCR_IMAGE }}:latest-amd64 - - # Combine ARM64 and AMD64 images into a multi-arch manifest + # Combine ARM64 and AMD64 images into multi-arch manifests for both registries create-manifest: runs-on: ubuntu-latest - needs: [build-arm64, build-amd64] # Wait for both builds + needs: [build-arm64, build-amd64] steps: - name: Log in to Docker Hub uses: docker/login-action@v3 @@ -84,17 +87,23 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 + - name: Log in to GHCR + uses: docker/login-action@v3 with: - images: ${{ env.DOCKER_IMAGE }} - tags: | - type=raw,value=latest # Force the tag to be "latest" + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Create Multi-Arch Manifest + - name: Create Docker Hub Multi-Arch Manifest run: | - # Combine the architecture-specific images into a "latest" manifest - docker buildx imagetools create -t ${{ steps.meta.outputs.tags }} \ + docker buildx imagetools create \ + -t ${{ env.DOCKER_IMAGE }}:latest \ ${{ env.DOCKER_IMAGE }}:latest-arm64 \ ${{ env.DOCKER_IMAGE }}:latest-amd64 + + - name: Create GHCR Multi-Arch Manifest + run: | + docker buildx imagetools create \ + -t ${{ env.GHCR_IMAGE }}:latest \ + ${{ env.GHCR_IMAGE }}:latest-arm64 \ + ${{ env.GHCR_IMAGE }}:latest-amd64 diff --git a/.github/workflows/docker-push-release.yml b/.github/workflows/docker-push-release.yml index cdaf1258..a3acbccd 100644 --- a/.github/workflows/docker-push-release.yml +++ b/.github/workflows/docker-push-release.yml @@ -2,6 +2,7 @@ name: Publish docker image (release) env: DOCKER_IMAGE: katanemo/plano + GHCR_IMAGE: ghcr.io/${{ github.repository_owner }}/plano on: release: @@ -9,9 +10,10 @@ on: permissions: contents: read + packages: write jobs: - # Build ARM64 image on native ARM64 runner + # Build ARM64 image on native ARM64 runner — push to both registries build-arm64: runs-on: [linux-arm64] steps: @@ -24,7 +26,14 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Extract metadata (tags, labels) for Docker + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker id: meta uses: docker/metadata-action@v5 with: @@ -39,9 +48,11 @@ jobs: file: ./Dockerfile platforms: linux/arm64 push: true - tags: ${{ steps.meta.outputs.tags }}-arm64 + tags: | + ${{ steps.meta.outputs.tags }}-arm64 + ${{ env.GHCR_IMAGE }}:${{ github.event.release.tag_name }}-arm64 - # Build AMD64 image on GitHub's AMD64 runner + # Build AMD64 image on GitHub's AMD64 runner — push to both registries build-amd64: runs-on: ubuntu-latest steps: @@ -54,7 +65,14 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Extract metadata (tags, labels) for Docker + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker id: meta uses: docker/metadata-action@v5 with: @@ -69,12 +87,14 @@ jobs: file: ./Dockerfile platforms: linux/amd64 push: true - tags: ${{ steps.meta.outputs.tags }}-amd64 + tags: | + ${{ steps.meta.outputs.tags }}-amd64 + ${{ env.GHCR_IMAGE }}:${{ github.event.release.tag_name }}-amd64 - # Combine ARM64 and AMD64 images into a multi-arch manifest + # Combine ARM64 and AMD64 images into multi-arch manifests for both registries create-manifest: runs-on: ubuntu-latest - needs: [build-arm64, build-amd64] # Wait for both builds + needs: [build-arm64, build-amd64] steps: - name: Log in to Docker Hub uses: docker/login-action@v3 @@ -82,7 +102,14 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Extract metadata (tags, labels) for Docker + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker id: meta uses: docker/metadata-action@v5 with: @@ -90,9 +117,17 @@ jobs: tags: | type=raw,value={{tag}} - - name: Create Multi-Arch Manifest + - name: Create Docker Hub Multi-Arch Manifest run: | - # Combine the architecture-specific images into a single manifest - docker buildx imagetools create -t ${{ steps.meta.outputs.tags }} \ + docker buildx imagetools create \ + -t ${{ steps.meta.outputs.tags }} \ ${{ steps.meta.outputs.tags }}-arm64 \ ${{ steps.meta.outputs.tags }}-amd64 + + - name: Create GHCR Multi-Arch Manifest + run: | + TAG=${{ github.event.release.tag_name }} + docker buildx imagetools create \ + -t ${{ env.GHCR_IMAGE }}:${TAG} \ + ${{ env.GHCR_IMAGE }}:${TAG}-arm64 \ + ${{ env.GHCR_IMAGE }}:${TAG}-amd64 diff --git a/.github/workflows/docker-security-scan.yml b/.github/workflows/docker-security-scan.yml deleted file mode 100644 index 03150064..00000000 --- a/.github/workflows/docker-security-scan.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Docker Security Scan - -env: - DOCKER_IMAGE: katanemo/plano - -on: - push: - branches: - - main - pull_request: - -permissions: - contents: read - security-events: write - -jobs: - scan: - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Build Docker Image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - platforms: linux/amd64 - push: false - tags: ${{ env.DOCKER_IMAGE }}:scan - - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@master - with: - image-ref: ${{ env.DOCKER_IMAGE }}:scan - format: table - # Fail on PRs so vulnerabilities block merge; on main just report - exit-code: ${{ github.event_name == 'pull_request' && '1' || '0' }} - ignore-unfixed: true - severity: CRITICAL,HIGH - - - name: Run Trivy scanner (SARIF for GitHub Security tab) - if: always() - uses: aquasecurity/trivy-action@master - with: - image-ref: ${{ env.DOCKER_IMAGE }}:scan - format: sarif - output: trivy-results.sarif - ignore-unfixed: true - severity: CRITICAL,HIGH - - - name: Upload Trivy results to GitHub Security tab - if: always() - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: trivy-results.sarif diff --git a/.github/workflows/e2e_plano_tests.yml b/.github/workflows/e2e_plano_tests.yml deleted file mode 100644 index 66e879ba..00000000 --- a/.github/workflows/e2e_plano_tests.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: e2e plano tests - -on: - push: - branches: - - main - pull_request: - -permissions: - contents: read - -jobs: - e2e_plano_tests: - runs-on: ubuntu-latest-m - strategy: - fail-fast: false - matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] - - defaults: - run: - working-directory: ./tests/archgw - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - cache: "pip" # auto-caches based on requirements files - - - name: build plano docker image - run: | - cd ../../ && docker build -f Dockerfile . -t katanemo/plano -t katanemo/plano:0.4.6 -t katanemo/plano:latest - - - name: start plano - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} - AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} - - run: | - docker compose up | tee &> plano.logs & - - - name: wait for plano to be healthy - run: | - source common.sh && wait_for_healthz http://localhost:10000/healthz - - - name: install uv - run: curl -LsSf https://astral.sh/uv/install.sh | sh - - - name: install test dependencies - run: | - uv sync - - - name: run plano tests - run: | - uv run pytest || tail -100 plano.logs - - - name: stop plano docker container - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - run: | - docker compose down diff --git a/.github/workflows/e2e_test_currency_convert.yml b/.github/workflows/e2e_test_currency_convert.yml deleted file mode 100644 index 9788bc52..00000000 --- a/.github/workflows/e2e_test_currency_convert.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: e2e demo tests currency conversion - -permissions: - contents: read - -on: - push: - branches: - - main - pull_request: - -jobs: - e2e_demo_tests: - runs-on: ubuntu-latest-m - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.12" - - - name: build plano docker image - run: | - docker build -f Dockerfile . -t katanemo/plano -t katanemo/plano:0.4.6 - - - name: install uv - run: curl -LsSf https://astral.sh/uv/install.sh | sh - - - name: setup python venv - run: | - python -m venv venv - - - name: install hurl - run: | - curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/4.0.0/hurl_4.0.0_amd64.deb - sudo dpkg -i hurl_4.0.0_amd64.deb - - - name: install plano gateway and test dependencies - run: | - source venv/bin/activate - cd cli && echo "installing plano cli" && uv sync && uv tool install . - cd ../demos/shared/test_runner && echo "installing test dependencies" && uv sync - - - name: run demo tests - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - run: | - source venv/bin/activate - cd demos/shared/test_runner && sh run_demo_tests.sh samples_python/currency_exchange diff --git a/.github/workflows/e2e_test_preference_based_routing.yml b/.github/workflows/e2e_test_preference_based_routing.yml deleted file mode 100644 index 59866592..00000000 --- a/.github/workflows/e2e_test_preference_based_routing.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: e2e demo preference based routing tests - -permissions: - contents: read - -on: - push: - branches: - - main - pull_request: - -jobs: - e2e_demo_tests: - runs-on: ubuntu-latest-m - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.12" - - - name: build plano docker image - run: | - docker build -f Dockerfile . -t katanemo/plano -t katanemo/plano:0.4.6 - - - name: install uv - run: curl -LsSf https://astral.sh/uv/install.sh | sh - - - name: setup python venv - run: | - python -m venv venv - - - name: install hurl - run: | - curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/4.0.0/hurl_4.0.0_amd64.deb - sudo dpkg -i hurl_4.0.0_amd64.deb - - - name: install plano gateway and test dependencies - run: | - source venv/bin/activate - cd cli && echo "installing plano cli" && uv sync && uv tool install . - cd ../demos/shared/test_runner && echo "installing test dependencies" && uv sync - - - name: run demo tests - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - ARCH_API_KEY: ${{ secrets.ARCH_API_KEY }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - run: | - source venv/bin/activate - cd demos/shared/test_runner && sh run_demo_tests.sh use_cases/preference_based_routing diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml deleted file mode 100644 index 22dcc910..00000000 --- a/.github/workflows/e2e_tests.yml +++ /dev/null @@ -1,203 +0,0 @@ -name: e2e tests - -on: - push: - branches: - - main - pull_request: - -permissions: - contents: read - -# Shared env vars for all jobs that run tests -env: - PLANO_DOCKER_IMAGE: katanemo/plano:e2e - -jobs: - # ────────────────────────────────────────────── - # Job 1: Build the Docker image once, with cache - # ────────────────────────────────────────────── - build: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Free disk space on runner - run: | - sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc - docker system prune -af || true - docker volume prune -f || true - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build plano image (with GHA cache) - uses: docker/build-push-action@v6 - with: - context: . - file: Dockerfile - load: true - tags: ${{ env.PLANO_DOCKER_IMAGE }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Save image as artifact - run: docker save ${{ env.PLANO_DOCKER_IMAGE }} -o /tmp/plano-image.tar - - - name: Upload image artifact - uses: actions/upload-artifact@v4 - with: - name: plano-image - path: /tmp/plano-image.tar - retention-days: 1 - - # ────────────────────────────────────────────── - # Job 2a: prompt_gateway tests - # ────────────────────────────────────────────── - test-prompt-gateway: - needs: build - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Free disk space on runner - run: | - sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc - docker system prune -af || true - docker volume prune -f || true - - - name: Download plano image - uses: actions/download-artifact@v4 - with: - name: plano-image - path: /tmp - - - name: Load plano image - run: docker load -i /tmp/plano-image.tar - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - enable-cache: true - cache-dependency-glob: | - tests/e2e/uv.lock - cli/uv.lock - - - name: Run prompt_gateway tests - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} - AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} - GROK_API_KEY: ${{ secrets.GROK_API_KEY }} - run: | - cd tests/e2e && bash run_prompt_gateway_tests.sh - - # ────────────────────────────────────────────── - # Job 2b: model_alias_routing + responses API tests - # ────────────────────────────────────────────── - test-model-alias-routing: - needs: build - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Free disk space on runner - run: | - sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc - docker system prune -af || true - docker volume prune -f || true - - - name: Download plano image - uses: actions/download-artifact@v4 - with: - name: plano-image - path: /tmp - - - name: Load plano image - run: docker load -i /tmp/plano-image.tar - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - enable-cache: true - cache-dependency-glob: | - tests/e2e/uv.lock - cli/uv.lock - - - name: Run model alias routing tests - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} - AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} - GROK_API_KEY: ${{ secrets.GROK_API_KEY }} - run: | - cd tests/e2e && bash run_model_alias_tests.sh - - # ────────────────────────────────────────────── - # Job 2c: responses API with state storage tests - # ────────────────────────────────────────────── - test-responses-api-with-state: - needs: build - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Free disk space on runner - run: | - sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc - docker system prune -af || true - docker volume prune -f || true - - - name: Download plano image - uses: actions/download-artifact@v4 - with: - name: plano-image - path: /tmp - - - name: Load plano image - run: docker load -i /tmp/plano-image.tar - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - enable-cache: true - cache-dependency-glob: | - tests/e2e/uv.lock - cli/uv.lock - - - name: Run responses API with state tests - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} - GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }} - AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} - GROK_API_KEY: ${{ secrets.GROK_API_KEY }} - run: | - cd tests/e2e && bash run_responses_state_tests.sh diff --git a/.github/workflows/ghrc-push-main.yml b/.github/workflows/ghrc-push-main.yml deleted file mode 100644 index a029e46f..00000000 --- a/.github/workflows/ghrc-push-main.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Publish docker image to ghcr (latest) - -env: - IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/plano - -on: - push: - branches: [main] - -jobs: - build-arm64: - runs-on: [linux-arm64] - permissions: { contents: read, packages: write } - steps: - - uses: actions/checkout@v4 - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.IMAGE_NAME }} - tags: | - type=raw,value=latest - - - name: Build and Push ARM64 Image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - platforms: linux/arm64 - push: true - # produce ghcr.io//plano:latest-arm64 - tags: ${{ steps.meta.outputs.tags }}-arm64 - - build-amd64: - runs-on: ubuntu-latest - permissions: { contents: read, packages: write } - steps: - - uses: actions/checkout@v4 - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.IMAGE_NAME }} - tags: | - type=raw,value=latest - - - name: Build and Push AMD64 Image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - platforms: linux/amd64 - push: true - tags: ${{ steps.meta.outputs.tags }}-amd64 - - create-manifest: - runs-on: ubuntu-latest - needs: [build-arm64, build-amd64] - permissions: { contents: read, packages: write } - steps: - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.IMAGE_NAME }} - tags: | - type=raw,value=latest - - - name: Create Multi-Arch Manifest - run: | - docker buildx imagetools create -t ${{ steps.meta.outputs.tags }} \ - ${{ env.IMAGE_NAME }}:latest-arm64 \ - ${{ env.IMAGE_NAME }}:latest-amd64 diff --git a/.github/workflows/ghrc-push-release.yml b/.github/workflows/ghrc-push-release.yml deleted file mode 100644 index 99e693a2..00000000 --- a/.github/workflows/ghrc-push-release.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: release - publish docker image to ghcr (latest) - -env: - IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/plano - -on: - release: - types: [published] - -jobs: - build-arm64: - runs-on: [linux-arm64] - permissions: { contents: read, packages: write } - steps: - - uses: actions/checkout@v4 - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.IMAGE_NAME }} - tags: | - type=raw,value={{tag}} - - - name: Build and Push ARM64 Image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - platforms: linux/arm64 - push: true - tags: ${{ steps.meta.outputs.tags }}-arm64 - - build-amd64: - runs-on: ubuntu-latest - permissions: { contents: read, packages: write } - steps: - - uses: actions/checkout@v4 - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.IMAGE_NAME }} - tags: | - type=raw,value={{tag}} - - - name: Build and Push AMD64 Image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - platforms: linux/amd64 - push: true - tags: ${{ steps.meta.outputs.tags }}-amd64 - - create-manifest: - runs-on: ubuntu-latest - needs: [build-arm64, build-amd64] - permissions: { contents: read, packages: write } - steps: - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.IMAGE_NAME }} - tags: | - type=raw,value={{tag}} - - - name: Create Multi-Arch Manifest - run: | - docker buildx imagetools create -t ${{ steps.meta.outputs.tags }} \ - ${{ steps.meta.outputs.tags }}-arm64 \ - ${{ steps.meta.outputs.tags }}-amd64 diff --git a/.github/workflows/plano_tools_tests.yml b/.github/workflows/plano_tools_tests.yml deleted file mode 100644 index 57f01048..00000000 --- a/.github/workflows/plano_tools_tests.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: plano tools tests - -permissions: - contents: read - -on: - push: - branches: - - main - pull_request: - -jobs: - plano_tools_tests: - runs-on: ubuntu-latest-m - defaults: - run: - working-directory: ./cli - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.12" - - - name: install uv - run: curl -LsSf https://astral.sh/uv/install.sh | sh - - - name: install plano tools - run: | - uv sync --extra dev - - - name: run tests - run: | - uv run pytest diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml deleted file mode 100644 index b3754b72..00000000 --- a/.github/workflows/pre-commit.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: pre-commit - -on: - pull_request: - push: - branches: [main] - -permissions: - contents: read - -jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 - - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/rust_tests.yml b/.github/workflows/rust_tests.yml deleted file mode 100644 index 5b1867e5..00000000 --- a/.github/workflows/rust_tests.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: rust tests (prompt and llm gateway) - -on: - pull_request: - push: - branches: [main] - -permissions: - contents: read - -jobs: - test: - name: Test - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./crates - - steps: - - name: Setup | Checkout - uses: actions/checkout@v4 - - - name: Setup | Rust - run: rustup toolchain install 1.92 --profile minimal - - - name: Setup | Install wasm toolchain - run: rustup target add wasm32-wasip1 - - - name: Build wasm module - run: | - cargo build --release --target=wasm32-wasip1 -p llm_gateway -p prompt_gateway - - - name: Run unit tests - run: cargo test --lib diff --git a/.github/workflows/validate_plano_config.yml b/.github/workflows/validate_plano_config.yml deleted file mode 100644 index 9d20c98a..00000000 --- a/.github/workflows/validate_plano_config.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: plano config tests - -on: - push: - branches: - - main - pull_request: - -permissions: - contents: read - -jobs: - validate_plano_config: - runs-on: ubuntu-latest - defaults: - run: - working-directory: . - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.12" - - - name: build plano docker image - run: | - docker build -f Dockerfile . -t katanemo/plano -t katanemo/plano:0.4.6 - - - name: validate plano config - run: | - bash config/validate_plano_config.sh