# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
# Genkit endpoints sample (REST + gRPC) — run `just` to see all commands.
#
# Install just: https://github.com/casey/just#installation
# brew install just # macOS
# cargo install just # Rust
# pipx install rust-just # Python
set dotenv-load := true
set shell := ["bash", "-euo", "pipefail", "-c"]
# Ports used by this sample.
APP_PORT := env("PORT", "8080")
GRPC_PORT := env("GRPC_PORT", "50051")
GENKIT_PORT := "4000"
JAEGER_UI_PORT := "16686"
JAEGER_OTLP_PORT := "4318"
# Default: show available commands.
default:
@just --list
# Start all services: Jaeger, REST (Swagger UI), gRPC (grpcui), Genkit DevUI.
start *ARGS:
./run.sh start {{ ARGS }}
# Start with Litestar and hot reload.
start-litestar *ARGS:
./run.sh start --framework litestar {{ ARGS }}
# Start with Quart and hot reload.
start-quart *ARGS:
./run.sh start --framework quart {{ ARGS }}
# Start production multi-worker server via gunicorn (REST only).
# Run the gRPC server separately if needed.
prod *ARGS:
uv run gunicorn -c gunicorn.conf.py 'src.asgi:create_app()' {{ ARGS }}
# Stop all services (app, gRPC, Genkit DevUI, Jaeger).
stop:
#!/usr/bin/env bash
echo "Stopping all services..."
# Kill processes on our ports.
for port in {{ APP_PORT }} {{ GRPC_PORT }} {{ GENKIT_PORT }}; do
pid=$(lsof -ti tcp:"$port" 2>/dev/null || true)
if [ -n "$pid" ]; then
echo " Killing process on port $port (PID $pid)"
kill "$pid" 2>/dev/null || true
fi
done
# Stop Jaeger container.
if command -v podman &>/dev/null || command -v docker &>/dev/null; then
./scripts/jaeger.sh stop 2>/dev/null || true
fi
echo "All services stopped."
# Run pytest (unit + telemetry tests).
test *ARGS:
uv run pytest tests/ -xvs {{ ARGS }}
# Run tests with coverage report (terminal + HTML).
coverage *ARGS:
uv run pytest tests/ --cov=src --cov-report=term-missing --cov-report=html {{ ARGS }}
# Open the HTML coverage report in the default browser.
coverage-open: coverage
open htmlcov/index.html
# Run REST integration tests against a local or remote server.
test-endpoints BASE_URL=("http://localhost:" + APP_PORT):
BASE_URL={{ BASE_URL }} ./test_endpoints.sh
# Run gRPC integration tests against the gRPC server.
test-grpc-endpoints GRPC_ADDR=("localhost:" + GRPC_PORT):
GRPC_ADDR={{ GRPC_ADDR }} ./test_grpc_endpoints.sh
# Run both REST and gRPC integration tests.
test-all BASE_URL=("http://localhost:" + APP_PORT) GRPC_ADDR=("localhost:" + GRPC_PORT):
#!/usr/bin/env bash
echo "═══ REST endpoint tests ═══"
BASE_URL={{ BASE_URL }} ./test_endpoints.sh
echo ""
echo "═══ gRPC endpoint tests ═══"
GRPC_ADDR={{ GRPC_ADDR }} ./test_grpc_endpoints.sh
# Regenerate Python gRPC stubs from protos/genkit_sample.proto.
proto:
./scripts/generate_proto.sh
# Open grpcui web UI for interactive gRPC testing.
grpcui GRPC_ADDR=("localhost:" + GRPC_PORT):
@echo "Opening grpcui for {{ GRPC_ADDR }}..."
grpcui -plaintext {{ GRPC_ADDR }}
# List all gRPC services and methods via reflection.
grpc-list GRPC_ADDR=("localhost:" + GRPC_PORT):
grpcurl -plaintext {{ GRPC_ADDR }} list
@echo ""
grpcurl -plaintext {{ GRPC_ADDR }} describe genkit.sample.v1.GenkitService
# Build the container image (podman preferred, docker fallback).
build TAG="genkit-endpoints":
#!/usr/bin/env bash
if command -v podman &>/dev/null; then cmd=podman
elif command -v docker &>/dev/null; then cmd=docker
else echo "Error: podman or docker is required" >&2; exit 1; fi
$cmd build -f Containerfile -t {{ TAG }} .
# Run the container locally (podman preferred, docker fallback).
run-container TAG="genkit-endpoints":
#!/usr/bin/env bash
if command -v podman &>/dev/null; then cmd=podman
elif command -v docker &>/dev/null; then cmd=docker
else echo "Error: podman or docker is required" >&2; exit 1; fi
$cmd run -p {{ APP_PORT }}:{{ APP_PORT }} -p {{ GRPC_PORT }}:{{ GRPC_PORT }} -e GEMINI_API_KEY="${GEMINI_API_KEY}" {{ TAG }}
# Deploy to Google Cloud Run.
deploy-cloudrun *ARGS:
./deploy_cloudrun.sh {{ ARGS }}
# Deploy to Google App Engine (Flex).
deploy-appengine *ARGS:
./deploy_appengine.sh {{ ARGS }}
# Deploy via Firebase Hosting + Cloud Run proxy.
deploy-firebase *ARGS:
./deploy_firebase_hosting.sh {{ ARGS }}
# Deploy to Fly.io.
deploy-flyio *ARGS:
./deploy_flyio.sh {{ ARGS }}
# Deploy to AWS App Runner.
deploy-aws *ARGS:
./deploy_aws.sh {{ ARGS }}
# Deploy to Azure Container Apps.
deploy-azure *ARGS:
./deploy_azure.sh {{ ARGS }}
# Run all lint checks (mirrors workspace bin/lint).
lint:
#!/usr/bin/env bash
set -euo pipefail
echo "── ruff check ──"
uv run ruff check --fix --preview --unsafe-fixes .
echo "── ruff format ──"
uv run ruff format --preview .
echo "── lockfile ──"
uv lock --check
echo "── ty ──"
uv run ty check .
echo "── pyrefly ──"
uv run pyrefly check .
echo "── pyright ──"
uv run pyright src/ tests/
# pysentry-rs reads version ranges from pyproject.toml and treats
# ">=2.0.0" as "v2.0.0", producing false positives. Feed it frozen
# (exact) versions from the installed environment instead.
echo "── pysentry-rs (security) ──"
if uv run pysentry-rs --version &>/dev/null; then
_freeze_dir=$(mktemp -d)
uv pip freeze > "$_freeze_dir/requirements.txt"
uv run pysentry-rs "$_freeze_dir"
rm -rf "$_freeze_dir"
else
echo "⚠️ pysentry-rs not installed — install with: uv pip install pysentry-rs"
exit 1
fi
echo "── license headers (addlicense) ──"
if command -v addlicense &>/dev/null; then
addlicense \
-check \
-c "Google LLC" \
-s \
-l apache \
-ignore '**/__pycache__/**/*' \
-ignore '**/.venv/**/*' \
-ignore '**/.ruff_cache/**/*' \
-ignore '**/.pytest_cache/**/*' \
-ignore '**/dist/**/*' \
-ignore '**/build/**/*' \
-ignore '**/site/**/*' \
-ignore '**/generated/**/*' \
-ignore '**/htmlcov/**/*' \
-ignore '**/*.toml' \
-ignore '**/*.yaml' \
.
else
echo "⚠️ addlicense not installed (go install github.com/google/addlicense@latest) — skipping"
fi
echo "── liccheck (dependency licenses) ──"
uv run liccheck -s pyproject.toml
echo "── shellcheck ──"
if command -v shellcheck &>/dev/null; then
shellcheck -x -e SC1091 *.sh scripts/*.sh
else
echo "⚠️ shellcheck not installed (brew install shellcheck) — skipping"
fi
echo "── All lint checks passed ──"
# Format Python code with ruff (src + tests).
fmt:
uv run ruff format --preview .
uv run ruff check --fix --preview --unsafe-fixes .
# Run type checkers only (ty, pyrefly, pyright).
typecheck:
#!/usr/bin/env bash
set -euo pipefail
echo "── ty ──"
uv run ty check .
echo "── pyrefly ──"
uv run pyrefly check .
echo "── pyright ──"
uv run pyright src/ tests/
# Scan dependencies for known vulnerabilities (CVEs).
audit:
uv run --extra dev pip-audit
# Check dependency licenses against an allowlist.
licenses:
uv run --extra dev pip-licenses --allow-only="Apache-2.0;Apache Software License;MIT;MIT License;BSD License;BSD-3-Clause;BSD-2-Clause;PSF-2.0;ISC;Python-2.0;Python Software Foundation License;Mozilla Public License 2.0 (MPL 2.0)"
# Run all security checks (audit + licenses + pysentry-rs).
security: audit licenses
uv run pysentry-rs .
# Serve docs locally with live reload (http://localhost:8000).
docs-serve:
uv run --extra docs mkdocs serve
# Build docs into site/ directory.
docs-build:
uv run --extra docs mkdocs build --strict
# Eject from the monorepo into a standalone project.
eject *ARGS:
./scripts/eject.sh {{ ARGS }}
# Preview eject changes without modifying files.
eject-dry-run:
./scripts/eject.sh --dry-run
# Clean build artifacts and caches.
clean:
rm -rf __pycache__ .ruff_cache .pytest_cache dist build site *.egg-info .venv
# Start Jaeger v2 container (auto-starts podman machine).
jaeger-start:
./scripts/jaeger.sh start
# Stop Jaeger container.
jaeger-stop:
./scripts/jaeger.sh stop
# Show Jaeger status and ports.
jaeger-status:
./scripts/jaeger.sh status
# Open Jaeger UI in browser.
jaeger-open:
./scripts/jaeger.sh open
# Tail Jaeger container logs.
jaeger-logs:
./scripts/jaeger.sh logs