Files
freeway/.github/workflows/docker.yml
T
Aryan SinghandClaude Opus 5 b85542abc6 Dashboard token usage and response inspection; fix Docker CI smoke
The dashboard now answers "where did my tokens go" without opening the log:
totals, per-provider and per-model breakdown, and cache hits shown as the
requests that cost nothing. Each log row expands to the actual prompt and
response, truncated to keep the ring buffer flat.

CI's Docker smoke step supplied no virtual keys, so the container correctly
refused to bind 0.0.0.0 and the job failed. The failsafe was right and the
workflow was wrong; it now supplies keys and additionally asserts that an
unauthenticated call gets 401 and an inference key gets 403 on /api, so an
image that lost its auth boundaries cannot be published green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 22:41:49 -05:00

132 lines
4.5 KiB
YAML

name: Docker
on:
push:
branches: [main]
tags: ['v*.*.*']
pull_request:
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
concurrency:
group: docker-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: build & push image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# Required for cosign keyless signing of pushed images.
id-token: write
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
# A pull request from a fork cannot read the registry secret, so builds are
# verified without pushing. Only trusted refs publish.
- name: Log in to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Derive tags and labels
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,format=short
type=raw,value=latest,enable={{is_default_branch}}
labels: |
org.opencontainers.image.title=Freeway
org.opencontainers.image.description=Self-hosted OpenAI-compatible gateway for free-tier LLM providers
- name: Build and push
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: true
sbom: true
# Smoke-test the image we just built, on the native arch only — an arm64
# image cannot be started on an amd64 runner without emulation overhead.
- name: Smoke-test the image
if: github.event_name != 'pull_request'
run: |
set -euo pipefail
IMAGE="$(echo '${{ steps.meta.outputs.tags }}' | head -n1)"
# The gateway refuses to bind a public interface with no virtual keys —
# a deliberate failsafe, and the container binds 0.0.0.0. CI has to
# supply one, exactly as a real deployment would.
docker run -d --name freeway-smoke -p 8787:8787 --platform linux/amd64 \
-e FREEWAY_KEYS=fw-ci-smoke-only \
-e FREEWAY_ADMIN_KEY=fw-ci-admin-only \
"$IMAGE"
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:8787/healthz >/dev/null 2>&1; then
echo "gateway is healthy"
curl -fsS http://127.0.0.1:8787/healthz; echo
# An unauthenticated call must be refused, and an app key must not
# reach the admin surface. If either regresses, the image is unsafe
# to publish however green everything else looks.
code=$(curl -s -o /dev/null -w '%{http_code}' -X POST http://127.0.0.1:8787/v1/chat/completions \
-H 'content-type: application/json' -d '{"model":"auto","messages":[]}')
[ "$code" = "401" ] || { echo "expected 401 without a key, got $code" >&2; docker rm -f freeway-smoke; exit 1; }
code=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8787/api/logs \
-H 'authorization: Bearer fw-ci-smoke-only')
[ "$code" = "403" ] || { echo "expected 403 for an inference key on /api, got $code" >&2; docker rm -f freeway-smoke; exit 1; }
echo "auth boundaries hold"
docker rm -f freeway-smoke
exit 0
fi
sleep 1
done
echo "gateway did not become healthy in 30s" >&2
docker logs freeway-smoke >&2
docker rm -f freeway-smoke
exit 1
- name: Summary
if: github.event_name != 'pull_request'
run: |
{
echo "### Image published"
echo
echo '```'
echo '${{ steps.meta.outputs.tags }}'
echo '```'
echo
echo "digest: \`${{ steps.build.outputs.digest }}\`"
} >> "$GITHUB_STEP_SUMMARY"