The deploy fetched the compiler, verified it, compiled every package and ran all
fifty-two tests, and then stopped:
The program `escript` was not found. Is it installed?
`lustre/dev build` is the obvious way to build a Lustre app and it cannot be the
way this one is built anywhere but here. The app targets JavaScript, but
lustre_dev_tools is an Erlang program — it carries mist and glisten and wisp so
it can run a dev server — so building with it needs an Erlang runtime that the
app itself never uses and the deploy image does not have. Pulling an OTP release
down to run a tool that shells out to esbuild is the wrong trade when esbuild is
a Node package and Node is already installed.
So build.sh does the four things that tool was doing: compile to JavaScript,
bundle with a pinned esbuild, take the shell from assets/index.html, copy the
assets in. It is now the local build as well as the deploy's, so what ships is
what was tested rather than something assembled a second way, and it uses a
compiler that is already installed rather than always fetching one — which is
what made it worth having it work on both platforms.
index.html is checked in rather than generated from a table in gleam.toml. It is
four links and a script tag, and a build step that assembles that from
configuration is indirection over something anyone should be able to open and
read. The table stays for the dev server, which knows no other way to serve a
shell, and each of them says so.
Checked by running the bundle: boot, atlas, a board, a solve and the win screen,
no console errors.- commit
- 02af01d6be57a4f79795f82536d5b4c98b4872d5
- parent
- 4caad897bcb5b300459eeefae484cd04533ca2f6
- author
- Dami <dami.etoile@gmail.com>
| M | README.md | 16 | +++++-- |
| A | assets/ | 34 | +++++++++++++ |
| M | build.sh | 77 | +++++++++++++++++++++++------- |
@@ -22,7 +22,7 @@ gleam check --target javascript
2222 gleam test --target javascript2323 2424 # Offline-capable production bundle in dist/25-gleam run -m lustre/dev build25+./build.sh2626 ```2727 2828 ### What is Gleam, and what is not@@ -101,9 +101,17 @@ game's own input path, which is how a screenshot of a real run gets taken.
101101 102102 ## Offline PWA103103 104-`gleam run -m lustre/dev build` generates `dist/index.html` and `dist/klotski.js`, then copies the105-manifest, service worker, solver worker, fonts, icons, and stylesheet. Serve `dist/` from an HTTPS106-origin (or localhost) to install it. The service worker uses a versioned cache, removes superseded104+`./build.sh` compiles to JavaScript, bundles with esbuild, and copies `assets/` — the shell, the105+manifest, the service worker, the solver worker, the fonts, the icons and the stylesheet — into106+`dist/`. It is the deploy's build command as well as the local one, so what ships is what was107+tested. It uses a Gleam that is already installed and fetches a pinned one when there is not.108+109+Not `lustre/dev build`, which is the obvious choice and does not work off this machine:110+lustre_dev_tools is itself an Erlang program, so building with it needs `escript` — a runtime this111+app never uses and the deploy image does not have. The dev server still uses it, and still wants112+Erlang; nothing else does.113+114+Serve `dist/` from an HTTPS origin (or localhost) to install it. The service worker uses a versioned cache, removes superseded107115 caches on activation, refreshes same-origin resources while online, and falls back to cached108116 resources or the app shell offline.109117 @@ -0,0 +1,34 @@
1+2+<!--3+ The application shell, and deliberately a file rather than something generated4+ from a table in gleam.toml. It is four links and a script tag; a build step5+ that assembles it from configuration is indirection over something anyone6+ should be able to open and read.7+8+ The dev server still writes its own from [tools.lustre.html], because that is9+ the only shell it knows how to serve. The two want to say the same thing: if10+ you add a link here, add it there.11+-->12+<html lang="en">13+ <head>14+ <meta charset="utf-8" />15+ <meta name="viewport" content="width=device-width, initial-scale=1" />16+ <title>klot.ski</title>17+ <meta18+ name="description"19+ content="Offline sliding-block puzzles, rendered like a tiny one-bit print shop."20+ />21+ <meta name="theme-color" content="#e8f4f2" />22+ <meta name="mobile-web-app-capable" content="yes" />23+ <meta name="apple-mobile-web-app-capable" content="yes" />24+ <link rel="manifest" href="/manifest.webmanifest" />25+ <link rel="icon" href="/icons/icon-192.png" />26+ <link rel="apple-touch-icon" href="/icons/icon-192.png" />27+ <link rel="stylesheet" href="/styles.css" />28+ <script src="/bootstrap.js"></script>29+ <script type="module" src="/klotski.js"></script>30+ </head>31+ <body>32+ <div id="app"></div>33+ </body>34+</html>@@ -5,36 +5,75 @@
55 # pinned to a version, because a toolchain that floats is a build that breaks on66 # a day nobody touched the repository.77 #8-# Nothing here needs Erlang. The project targets JavaScript, so `lustre/dev`9-# runs under Node and the compiler is the only extra thing on the machine.8+# It does not run `lustre/dev build`, which is the obvious thing to do and does9+# not work here. The app targets JavaScript, but lustre_dev_tools is itself an10+# Erlang program — it carries a whole HTTP server in mist and glisten so it can11+# run a dev server — so building with it needs `escript`, which is a runtime this12+# app never uses and which the image does not have either. Pulling an Erlang13+# release down to run a tool that shells out to esbuild is the wrong trade when14+# esbuild is a Node package and Node is already installed.15+#16+# So the four things `lustre/dev build` does are done directly: compile to17+# JavaScript, bundle, take the shell from assets/index.html, copy the assets in.1018 set -euo pipefail1119 1220 GLEAM_VERSION="1.18.1"13-ARCH="$(uname -m)"14-case "$ARCH" in15- x86_64) TARGET="x86_64-unknown-linux-musl" ;;16- aarch64 | arm64) TARGET="aarch64-unknown-linux-musl" ;;17- *) echo "no Gleam build for $ARCH" >&2; exit 1 ;;18-esac19-20-TARBALL="gleam-v${GLEAM_VERSION}-${TARGET}.tar.gz"21-URL="https://github.com/gleam-lang/gleam/releases/download/v${GLEAM_VERSION}/${TARBALL}"22-23-mkdir -p .bin24-if [ ! -x .bin/gleam ]; then25- echo "fetching Gleam ${GLEAM_VERSION} for ${TARGET}"21+ESBUILD_VERSION="0.28.1"22+23+# A compiler that is already here is the one to use — this script is the build,24+# so it has to run on the machine it was written on as well as on the one that25+# deploys it. Only a machine without Gleam fetches one.26+if command -v gleam >/dev/null 2>&1; then27+ echo "using $(gleam --version)"28+else29+ case "$(uname -s)" in30+ Linux) OS="unknown-linux-musl" ;;31+ Darwin) OS="apple-darwin" ;;32+ *) echo "no Gleam build for $(uname -s)" >&2; exit 1 ;;33+ esac34+ case "$(uname -m)" in35+ x86_64 | amd64) ARCH="x86_64" ;;36+ aarch64 | arm64) ARCH="aarch64" ;;37+ *) echo "no Gleam build for $(uname -m)" >&2; exit 1 ;;38+ esac39+40+ TARBALL="gleam-v${GLEAM_VERSION}-${ARCH}-${OS}.tar.gz"41+ URL="https://github.com/gleam-lang/gleam/releases/download/v${GLEAM_VERSION}/${TARBALL}"42+43+ echo "fetching Gleam ${GLEAM_VERSION} for ${ARCH}-${OS}"44+ mkdir -p .bin2645 WORK="$(mktemp -d)"2746 curl -fsSL "$URL" -o "${WORK}/${TARBALL}"2847 curl -fsSL "${URL}.sha256" -o "${WORK}/${TARBALL}.sha256"2948 # The published digest already names the file it belongs to, so the check runs3049 # from the directory that file is in. A download nobody verified is a download3150 # anybody can replace.32- ( cd "$WORK" && sha256sum -c "${TARBALL}.sha256" )51+ if command -v sha256sum >/dev/null 2>&1; then52+ ( cd "$WORK" && sha256sum -c "${TARBALL}.sha256" )53+ else54+ ( cd "$WORK" && shasum -a 256 -c "${TARBALL}.sha256" )55+ fi3356 tar xzf "${WORK}/${TARBALL}" -C .bin3457 rm -rf "$WORK"58+ export PATH="$PWD/.bin:$PATH"3559 fi3660 37-export PATH="$PWD/.bin:$PATH"38-gleam --version3961 gleam test40-gleam run -m lustre/dev build62+gleam build --target javascript63+64+# The bundle needs something to call. The compiler emits a module per Gleam65+# module and nothing that runs one, so this is the line `lustre/dev build`66+# appends for you.67+OUT="build/dev/javascript/klotski"68+printf 'import { main } from "./klotski.mjs";\nmain();\n' > "${OUT}/_entry.mjs"69+70+rm -rf dist71+mkdir -p dist72+npx --yes "esbuild@${ESBUILD_VERSION}" "${OUT}/_entry.mjs" \73+ --bundle --minify --format=esm --outfile=dist/klotski.js74+75+# index.html is checked in rather than generated, so what ships is a file76+# somebody can read. Everything else in assets/ is served as it stands.77+cp -R assets/. dist/78+echo "built dist/"79+ls -la dist