diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b77615..95c3207 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,3 +30,41 @@ jobs: - name: Test run: uv run pytest -m 'not integration and not slow' -v + + # Same Dockerfile deploy.yml pushes, built but not pushed — a PR that would + # break the prod image (Dockerfile, uv.lock, missing files) fails here instead. + build: + needs: test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - name: Validate compose file + run: touch .env && docker compose config -q # .env is gitignored + + - name: Build image + uses: docker/build-push-action@v6 + with: + context: . + push: false + cache-from: type=gha + cache-to: type=gha,mode=max + + # Renders the template with envsubst (the nginx image does it on start) and + # runs nginx -t, so a bad directive fails here, not as a crash-looping nginx + # on deploy. Throwaway cert + `app` host stand in for the real ones. + - name: Validate nginx config + run: | + d=$(mktemp -d); mkdir -p $d/live/ci.test + openssl req -x509 -newkey rsa:2048 -nodes -days 1 -subj /CN=ci.test \ + -keyout $d/live/ci.test/privkey.pem -out $d/live/ci.test/fullchain.pem + docker run --rm --add-host app:127.0.0.1 -e DOMAIN=ci.test \ + -v $PWD/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \ + -v $PWD/nginx/snippets:/etc/nginx/snippets:ro \ + -v $PWD/nginx/templates:/etc/nginx/templates:ro \ + -v /dev/null:/etc/nginx/conf.d/default.conf:ro \ + -v $d:/etc/letsencrypt:ro \ + nginx:alpine nginx -t diff --git a/nginx/snippets/proxy-pass.conf b/nginx/snippets/proxy-pass.conf index 06ffe1a..b84b86a 100644 --- a/nginx/snippets/proxy-pass.conf +++ b/nginx/snippets/proxy-pass.conf @@ -4,11 +4,8 @@ set $upstream_app app:8000; proxy_pass http://$upstream_app; -# No backend synthesis timeout exists today (ffmpeg.py only bounds the -# encode step at 30s; TTS inference itself is unbounded) — pick a ceiling -# safely above realistic worst-case rather than nginx's low defaults cutting -# off legitimate slow requests. -proxy_read_timeout 120s; +# proxy_read_timeout is set per location (nginx rejects a directive repeated in +# one block, so it can't live here and be overridden after the include). proxy_send_timeout 120s; proxy_connect_timeout 5s; diff --git a/nginx/templates/20-speech-server.conf.template b/nginx/templates/20-speech-server.conf.template index 7577de2..d8aa73f 100644 --- a/nginx/templates/20-speech-server.conf.template +++ b/nginx/templates/20-speech-server.conf.template @@ -74,9 +74,8 @@ server { include /etc/nginx/snippets/proxy-pass.conf; # TTS inference is CPU-bound and can be slow (24-layer models, long text, - # emulated arch) — give synthesis more than the shared 120s read timeout so - # a legitimately long generation isn't cut off with a 504. This override must - # come AFTER the include, whose 120s would otherwise win. + # emulated arch) — give synthesis more than the 120s default on other + # routes so a legitimately long generation isn't cut off with a 504. proxy_read_timeout 300s; } @@ -91,5 +90,9 @@ server { limit_conn perip 20; include /etc/nginx/snippets/proxy-pass.conf; + + # No backend timeout exists today (TTS inference is unbounded) — pick a + # ceiling safely above realistic worst-case rather than nginx's low default. + proxy_read_timeout 120s; } }