nandi/frqpublic Fork 0
3760746302d1370dfe997aca01cf671e5d36a553
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.

One frontend where there were three, and a core that is not Clojure 438b247 · on 3760746302d1370dfe997aca01cf671e5d36a553 · nandi · 21h ago
container.toml · 136 lines · 6.5 KBTOML 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
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
[container]
name        = "frq-flutter-web"
description = "the Flutter web build, incremental, from a pinned toolchain"
# `debian:13-slim` and not `arch-nix`: there is no nix in this container any
# more. The build is `tools/build-web.sh`, which fetches its own Flutter, JDK
# and Clojure CLI by pinned sha256, so what the image owes it is curl, git,
# tar and a C runtime — and the smallest image that has them is the right one.
registry    = "debian:13-slim"
# A Sandbox, not a Function: it runs on a real VM, the command is the
# sandbox's own process so it dies when the command does, and only a Sandbox
# can hold open a tunnel -- which is the whole of `serve`.
runtime     = "sandbox"

[build]
# The container lives inside the repo it builds, so the copy is rooted two
# levels up and `.` is the whole tree.
context = "../.."
include = ["."]
# The whole image build, and it is one apt line. What used to be here -- a
# nix store to populate, a devShell to print, a closure to warm before the
# source arrived so an edit would not invalidate it -- is gone with the nix
# it was for. There is no `warm` list any more either: this step reads
# nothing out of the tree, so nothing in the tree can invalidate it.
#
# git, because Flutter shells out to it against its own SDK checkout and
# refuses to run without one; unzip and xz-utils, because that is what the
# SDK and the toolchain tarballs arrive as; rsync for the sync below;
# ca-certificates so curl can verify what it fetches.
setup = [
  "apt-get update && apt-get install -y --no-install-recommends ca-certificates curl git rsync tar unzip xz-utils && rm -rf /var/lib/apt/lists/*",
]
# The build state a local checkout carries, wanted by nothing out here: this
# container builds into a volume of its own, and the clojure caches
# are the laptop's.
ignore  = [
    "flutter/build", "flutter/.home", "flutter/.dart_tool",
    "flutter/.clojuredart", "flutter/.cpcache",
    # The ClojureDart compiler's output, gitignored and -- until now --
    # uploaded anyway, because this list is explicit and does not read
    # .gitignore. It is megabytes of generated Dart per run, and worse than
    # the upload is what happened on arrival: the rsync below overwrote the
    # volume's copy, the one the last container compiled, with a laptop's.
    # Every file whose content differed then looked new to the compiler and
    # to Flutter, which is the incremental build undone by the thing that was
    # meant to feed it. It belongs to the volume, like flutter/build.
    "flutter/lib/cljd-out",
    # The toolchain, which is a gigabyte of Flutter SDK and lives on the
    # volume out here.
    ".toolchain",
    ".cpcache", "result", "build", ".git",
    # An editor's linter rewrites this while the upload is reading it, and
    # Modal fails the whole run with "was modified during build process".
    # Nothing out here reads it.
    ".clj-kondo",
]

# One volume now, where there were two: the nix binary cache went with nix.
# `devshell` is the working state of an incremental loop, shared by every
# container that has one -- each gets its own directory under it, named for
# what it belongs to, so `flutter-web` and `flutter-desktop` never write the
# same tree. Modal Volumes have no locking, so those directory names are the
# only thing keeping them apart, and two runs of the *same* container must
# not overlap.
[volumes]
devshell = "/devshell"

[resources]
cpu     = 8
memory  = 16384
timeout = 3600

# The port `tools/build-web.sh serve` listens on, tunnelled out. Nothing is
# served unless the command asks for it -- a plain build exits and the tunnel
# closes with the sandbox -- but the port has to be declared at create time,
# so it is declared once here and `modal run --command` decides whether
# anything ever binds it.
[network]
ports = [8080]

[run]
workdir = "/app"
# Source in, toolchain out of the volume, build in place. Three steps, and
# none of them evaluates anything: the old command spent its first minutes
# entering a devShell, printing an environment, caching that environment
# against flake.lock and copying a nix closure back afterwards, all to arrive
# at a PATH. A PATH is what `tools/toolchain.sh env` prints, out of a
# directory that is already on the volume.
command = """
set -e
SHELL_DIR=/devshell/frq-flutter-web

# Beside the working tree and NOT inside it: the rsync below runs with
# --delete, so anything under $SHELL_DIR that is not in /app is removed on
# every run. A cache kept in there would be deleted moments before it was
# consulted -- which is what happened to the last one that tried.
export FRQ_TOOLCHAIN=/devshell/frq-flutter-web.toolchain
mkdir -p "$SHELL_DIR" "$FRQ_TOOLCHAIN"

echo "sync: /app -> $SHELL_DIR"
# rsync and not cp, with --checksum and not mtimes: Modal copies the source in
# with fresh timestamps every run, so a plain copy looks entirely new to
# Flutter and rebuilds the lot. --checksum compares content and leaves the
# unchanged files' timestamps alone, which is the whole basis of the
# incremental build.
#
# The excludes are the state we are here to keep -- overwriting them from /app
# would defeat the volume. `flutter/web/` is NOT on the list: it is committed,
# because the OAuth client keeps a script there, so it has to arrive from
# /app like any other source.
rsync -a --checksum --delete \
    --exclude 'flutter/.home/' \
    --exclude 'flutter/.clojuredart/' \
    --exclude 'flutter/build/' \
    --exclude 'flutter/lib/cljd-out/' \
    --exclude 'flutter/.dart_tool/' \
    --exclude '.toolchain/' \
    --exclude '.git' \
    /app/ "$SHELL_DIR/"

cd "$SHELL_DIR"
# What survived from the last run, by presence and not by size: `du` here
# walked the pub cache, the toolchain and every object of the last build over
# a network volume, for numbers nobody acts on.
for d in "$FRQ_TOOLCHAIN" flutter/.clojuredart flutter/lib/cljd-out flutter/build; do
    [ -d "$d" ] && echo "  carried over: $d"
done

# The same script `just flutter-web` runs, with the same single build mode:
# what is served and what a laptop compiles are the same bundle.
tools/build-web.sh build
"""
# Flutter keeps its settings -- `--enable-web` among them -- under
# XDG_CONFIG_HOME, and its own caches under XDG_CACHE_HOME. Both point into
# the volume so a second run finds what the first one decided. Set here
# rather than in the command so a shell into this container gets them too.
env     = { XDG_CACHE_HOME = "/devshell/frq-flutter-web.toolchain/.cache", XDG_CONFIG_HOME = "/devshell/frq-flutter-web.toolchain/.config", FRQ_TOOLCHAIN = "/devshell/frq-flutter-web.toolchain" }