nandi/frqpublic Fork 0
bd10e816d10d09c3dbe1256c1b7c661d8e190ee6
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

app.py · 63 lines · 2.7 KBPython Blame HistoryRaw
The web container is plain Modal bd10e81 nandi 14h ago1"""The web bundle CI built, served at a URL.
2
3 FRQ_WEB_IMAGE=registry.rickub.com/nandi/frq-web:<sha> \
4 modal deploy .modal/web/app.py
5
6Plain Modal, and no `container.toml` behind it. The `dev` container is
7described by a spec because it is a sandbox with a volume, a toolchain and a
8command that changes; this one is four constants and a `Popen`, and a spec
9file for that was a second thing to read before the first one made sense.
10
11Nothing is built here. rickub builds the image — `Dockerfile` beside this
12file, two stages, the second one just the bundle and a python — and pushes it
13to `registry.rickub.com`; this deploys that exact tag. So the thing served is
14the thing that was built and tested, and a deploy is a pull rather than a
15compile. The workflow is `.rickub/workflows/web.yml`.
16"""
17
18import os
19import subprocess
20
21import modal
22
23# The tag CI just built and pushed. No default: unset, this fails here rather
24# than deploying whatever was current the last time somebody ran it.
25IMAGE = os.environ.get("FRQ_WEB_IMAGE", "")
26if not IMAGE:
27 raise SystemExit(
28 "FRQ_WEB_IMAGE is unset. It is the image to serve, e.g.\n"
29 " FRQ_WEB_IMAGE=registry.rickub.com/nandi/frq-web:<sha> \\\n"
30 " modal deploy .modal/web/app.py"
31 )
32
33# No `registry_secret`, which is a decision rather than an omission. Modal
34# pulls on every cold start — not once at deploy time — so a private image
35# would need a long-lived rickub deploy token kept as a Modal Secret, where
36# the workflow's own token is short-lived by design. The image is public
37# instead: it holds `build/web` and a python to serve it, and that bundle is
38# what the URL hands to anyone who opens it, so a credential here would be
39# guarding a copy of the public site.
40#
41# To go the other way, set the image private on rickub and pass
42# `secret=modal.Secret.from_name("rickub-registry")` below, naming a Secret
43# with REGISTRY_USERNAME / REGISTRY_PASSWORD for a pull-only token.
44image = modal.Image.from_registry(IMAGE)
45
46# Named, and the name is what makes a second deploy replace the running one
47# rather than stand another beside it.
48app = modal.App("frq-web", image=image)
49
50PORT = 8000
51SERVE = f"python3 -m http.server {PORT} --directory /srv/web"
52
53
54@app.function(cpu=1, memory=1024, timeout=3600, min_containers=1)
55# One container answering many requests: a static bundle costs nothing per
56# request, so scaling out on concurrency would buy cold starts and nothing
57# else.
58@modal.concurrent(max_inputs=100)
59# `web_server` waits for the port to accept a connection and then proxies to
60# it, so the command has to keep running — `Popen` and return, not `run`.
61@modal.web_server(port=PORT, startup_timeout=60)
62def serve():
63 subprocess.Popen(SERVE, shell=True)