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.

The web container is plain Modal bd10e81 · on bd10e816d10d09c3dbe1256c1b7c661d8e190ee6 · nandi · 13h ago
app.py · 63 lines · 2.7 KBPython Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
"""The web bundle CI built, served at a URL.

    FRQ_WEB_IMAGE=registry.rickub.com/nandi/frq-web:<sha> \
        modal deploy .modal/web/app.py

Plain Modal, and no `container.toml` behind it. The `dev` container is
described by a spec because it is a sandbox with a volume, a toolchain and a
command that changes; this one is four constants and a `Popen`, and a spec
file for that was a second thing to read before the first one made sense.

Nothing is built here. rickub builds the image — `Dockerfile` beside this
file, two stages, the second one just the bundle and a python — and pushes it
to `registry.rickub.com`; this deploys that exact tag. So the thing served is
the thing that was built and tested, and a deploy is a pull rather than a
compile. The workflow is `.rickub/workflows/web.yml`.
"""

import os
import subprocess

import modal

# The tag CI just built and pushed. No default: unset, this fails here rather
# than deploying whatever was current the last time somebody ran it.
IMAGE = os.environ.get("FRQ_WEB_IMAGE", "")
if not IMAGE:
    raise SystemExit(
        "FRQ_WEB_IMAGE is unset. It is the image to serve, e.g.\n"
        "  FRQ_WEB_IMAGE=registry.rickub.com/nandi/frq-web:<sha> \\\n"
        "      modal deploy .modal/web/app.py"
    )

# No `registry_secret`, which is a decision rather than an omission. Modal
# pulls on every cold start — not once at deploy time — so a private image
# would need a long-lived rickub deploy token kept as a Modal Secret, where
# the workflow's own token is short-lived by design. The image is public
# instead: it holds `build/web` and a python to serve it, and that bundle is
# what the URL hands to anyone who opens it, so a credential here would be
# guarding a copy of the public site.
#
# To go the other way, set the image private on rickub and pass
# `secret=modal.Secret.from_name("rickub-registry")` below, naming a Secret
# with REGISTRY_USERNAME / REGISTRY_PASSWORD for a pull-only token.
image = modal.Image.from_registry(IMAGE)

# Named, and the name is what makes a second deploy replace the running one
# rather than stand another beside it.
app = modal.App("frq-web", image=image)

PORT = 8000
SERVE = f"python3 -m http.server {PORT} --directory /srv/web"


@app.function(cpu=1, memory=1024, timeout=3600, min_containers=1)
# One container answering many requests: a static bundle costs nothing per
# request, so scaling out on concurrency would buy cold starts and nothing
# else.
@modal.concurrent(max_inputs=100)
# `web_server` waits for the port to accept a connection and then proxies to
# it, so the command has to keep running — `Popen` and return, not `run`.
@modal.web_server(port=PORT, startup_timeout=60)
def serve():
    subprocess.Popen(SERVE, shell=True)