Skip to content

Add container support: configurable web-UI bind address + Dockerfile#113

Merged
dmccoystephenson merged 2 commits into
mainfrom
feature/web-container-support
Jul 18, 2026
Merged

Add container support: configurable web-UI bind address + Dockerfile#113
dmccoystephenson merged 2 commits into
mainfrom
feature/web-container-support

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

Two changes needed to run FishE's web front-end in a Docker container behind a reverse proxy (Traefik), for deployment in Stephenson-Software/gateway:

  • WebUserInterface.__init__ already accepts host/port params, but they were never plumbed up through UserInterfaceFactory.create_user_interface's WEB branch, which called it with no override — always binding the hardcoded default 127.0.0.1:8000. Inside a container on a bridge network, a process bound to 127.0.0.1 is unreachable from a sibling container (e.g. a reverse proxy), so this makes the WEB branch read FISHE_WEB_HOST/FISHE_WEB_PORT env vars, defaulting to the existing 127.0.0.1:8000 so behavior is unchanged for anyone not setting them.
  • Adds a Dockerfile (python:3.12-slim, stdlib-only — no pygame, matching the web front-end's own "no extra dependencies" footprint) that runs examples/web_app.py with FISHE_WEB_HOST=0.0.0.0 baked in via ENV, and creates /app/data (the SaveFileManager default save directory, resolved relative to the process cwd) owned by a non-root user so a volume mounted over it stays writable.

examples/web_app.py's startup print now reflects the actual host/port instead of a hardcoded string.

Test plan

  • python3 -m compileall -q src tests
  • SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy python3 -m pytest --verbose -vv --cov=src --cov-report=term-missing --cov-report=xml:cov.xml — 327 passed, userInterfaceFactory.py at 100% coverage
  • black/autoflake run locally; only the files actually touched by this change are included in the diff (pre-existing formatting drift elsewhere in the repo was reverted, out of scope for this PR)
  • Manual smoke test: ran FishE(interfaceType=UIType.WEB) with FISHE_WEB_HOST=0.0.0.0 FISHE_WEB_PORT=8123 and confirmed the server bound 0.0.0.0 and GET /state returned 200 with the expected JSON

🤖 Generated with Claude Code

WebUserInterface hardcodes host="127.0.0.1" by default, which is
unreachable from outside its own network namespace (e.g. from a reverse
proxy in a sibling container). UserInterfaceFactory's WEB branch now reads
FISHE_WEB_HOST/FISHE_WEB_PORT (defaulting to the existing 127.0.0.1:8000,
so behavior is unchanged for anyone not setting them) and threads them
into WebUserInterface's existing host/port constructor params.

Also adds a Dockerfile (python:3.12-slim, stdlib only, non-root user) that
runs examples/web_app.py with FISHE_WEB_HOST=0.0.0.0 so the web front-end
is reachable from outside the container.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes the web UI runnable in containerized/reverse-proxied deployments by letting the WEB UI bind address/port be configured via environment variables, and adds a minimal Docker image to run the web front-end.

Changes:

  • Plumbs FISHE_WEB_HOST / FISHE_WEB_PORT through UserInterfaceFactory when creating WebUserInterface.
  • Updates examples/web_app.py to print the configured bind address/port and documents the env var mechanism.
  • Adds a Dockerfile that runs the web app as a non-root user and pre-creates /app/data for writable saves/volumes.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
tests/ui/test_userInterfaceFactory.py Adds WEB UI factory tests (default host + env override behavior).
src/ui/userInterfaceFactory.py Reads FISHE_WEB_HOST / FISHE_WEB_PORT and passes them into WebUserInterface.
examples/web_app.py Updates startup messaging and docs to reflect configurable bind settings.
Dockerfile Provides a container image for running the web UI with 0.0.0.0:8000 defaults and non-root execution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +43 to +47
host = os.environ.get("FISHE_WEB_HOST", "127.0.0.1")
port = int(os.environ.get("FISHE_WEB_PORT", "8000"))
return WebUserInterface(
currentPrompt, timeService, player, host=host, port=port
)
Comment thread tests/ui/test_userInterfaceFactory.py Outdated
Comment on lines +70 to +72
"""Test factory creates a web UI bound to 127.0.0.1:8000 by default,
matching WebUserInterface's own defaults, when no FISHE_WEB_HOST/
FISHE_WEB_PORT override is set."""
@dmccoystephenson

Copy link
Copy Markdown
Member Author

@copilot address comments

- Wrap int() conversion of FISHE_WEB_PORT in a try/except and re-raise
  with a descriptive message pointing to the env var name
- Add test_factory_create_web_ui_invalid_port to cover the error path
- Fix test_factory_create_web_ui_default_bind docstring: it tested the
  host default with an overridden ephemeral port (0), not 127.0.0.1:8000

Copilot AI commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

@copilot address comments

Both reviewer comments addressed in commit fix: validate FISHE_WEB_PORT and clarify test docstring:

  1. userInterfaceFactory.py: int() is now wrapped in a try/except ValueError that re-raises with "FISHE_WEB_PORT must be an integer, got: <value>", plus a new test_factory_create_web_ui_invalid_port test covers the error path.
  2. test_userInterfaceFactory.py: The test_factory_create_web_ui_default_bind docstring no longer claims the default port is 8000 — it now correctly explains that FISHE_WEB_PORT=0 is an ephemeral override to avoid port conflicts, and that only the host default is being asserted.

@dmccoystephenson
dmccoystephenson merged commit acfb9b0 into main Jul 18, 2026
@dmccoystephenson
dmccoystephenson deleted the feature/web-container-support branch July 18, 2026 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants