mirror of
https://github.com/michaelshimeles/terminal.git
synced 2026-11-08 01:07:07 -10:00
Compare commits
2
Commits
master
...
fix/web-cache
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e53585b50 | ||
|
|
b5776f2abf |
No files matched your search
@@ -22,7 +22,7 @@ jobs:
|
||||
arch: x86_64
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy, rustfmt
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
grep -q "smoke:" live.log
|
||||
- name: Bundle .app
|
||||
run: ./scripts/bundle-macos.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v7
|
||||
if: always()
|
||||
with:
|
||||
name: macos-${{ matrix.arch }}
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy, rustfmt
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
name: Build the browser app
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
# Pull requests build the branch head, like Vercel's Git previews did, not the merge commit.
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
tar xzf "$RUNNER_TEMP/binaryen.tar.gz" -C "$RUNNER_TEMP"
|
||||
echo "$RUNNER_TEMP/binaryen-$BINARYEN/bin" >> "$GITHUB_PATH"
|
||||
- run: scripts/build-web.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: web-app
|
||||
path: site/static/app
|
||||
@@ -84,14 +84,14 @@ jobs:
|
||||
fi
|
||||
done
|
||||
exit $missing
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: web-app
|
||||
path: site/static/app
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
|
||||
@@ -207,13 +207,13 @@ rustup target add wasm32-unknown-unknown
|
||||
cargo install wasm-bindgen-cli --version 0.2.129 --locked # must match Cargo.lock
|
||||
brew install binaryen # wasm-opt
|
||||
scripts/build-web.sh # writes site/static/app
|
||||
cd site && npm run dev # then open http://localhost:5173/app
|
||||
cd site && npm run dev # then open http://localhost:5173/app/index.html
|
||||
```
|
||||
|
||||
`scripts/build-web.sh <dir>` writes somewhere else instead; any static file server works. Add
|
||||
`?sample` to the URL to run offline on sample data, or `?fps` for the frame-time readout.
|
||||
Every push to `master` builds it and deploys it with the site to
|
||||
[terminal.rasmic.xyz/app](https://terminal.rasmic.xyz/app). Pull requests get a preview;
|
||||
[terminal.rasmic.xyz/app](https://terminal.rasmic.xyz/app/). Pull requests get a preview;
|
||||
[site/README.md](site/README.md#deploys) covers the workflow and the secrets it needs.
|
||||
|
||||

|
||||
|
||||
+7
-4
@@ -10,8 +10,11 @@
|
||||
// The WebGPU build when the browser has WebGPU, else the larger WebGL2 one (no charts).
|
||||
// Start downloading now; the module below cancels this if there is no WebGPU adapter.
|
||||
window.terminalLoad = { build: 'gpu' in navigator ? 'terminal' : 'terminal-webgl', abort: new AbortController() };
|
||||
terminalLoad.wasm = fetch(`./${terminalLoad.build}_bg.wasm`, { signal: terminalLoad.abort.signal });
|
||||
document.head.append(Object.assign(document.createElement('link'), { rel: 'modulepreload', href: `./${terminalLoad.build}.js` }));
|
||||
// Each build's JS and wasm sit in a folder named after a hash of its files, so they can be
|
||||
// cached for good; scripts/build-web.sh fills in these paths.
|
||||
terminalLoad.paths = { terminal: './{terminal}', 'terminal-webgl': './{terminal-webgl}' };
|
||||
terminalLoad.wasm = fetch(`${terminalLoad.paths[terminalLoad.build]}_bg.wasm`, { signal: terminalLoad.abort.signal });
|
||||
document.head.append(Object.assign(document.createElement('link'), { rel: 'modulepreload', href: `${terminalLoad.paths[terminalLoad.build]}.js` }));
|
||||
</script>
|
||||
<style>
|
||||
html,
|
||||
@@ -56,11 +59,11 @@
|
||||
load.wasm.catch(() => {}); // the abort below rejects it
|
||||
load.abort.abort();
|
||||
load.build = 'terminal-webgl';
|
||||
load.wasm = fetch(`./${load.build}_bg.wasm`);
|
||||
load.wasm = fetch(`${load.paths[load.build]}_bg.wasm`);
|
||||
}
|
||||
window.terminalBuild = load.build;
|
||||
try {
|
||||
const { default: init } = await import(`./${load.build}.js`);
|
||||
const { default: init } = await import(`${load.paths[load.build]}.js`);
|
||||
await init({ module_or_path: load.wasm }); // runs the app, which removes #status after its first frame
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
+25
-8
@@ -7,6 +7,11 @@
|
||||
# WebGL2 fallback, which is larger and draws no charts). The page loads the second only when
|
||||
# the browser has no WebGPU adapter.
|
||||
#
|
||||
# Each build's JS glue and wasm go in immutable/<hash>/, a folder named after a hash of its
|
||||
# files, and index.html points at those folders. A new build gets new URLs, so the site can
|
||||
# cache everything under immutable/ for good (site/vercel.json), and a page can't pair one
|
||||
# build's JS with another build's wasm.
|
||||
#
|
||||
# Needs the wasm32-unknown-unknown target (rustup target add wasm32-unknown-unknown),
|
||||
# wasm-bindgen-cli at the version pinned in Cargo.lock, and wasm-opt from binaryen.
|
||||
set -euo pipefail
|
||||
@@ -30,19 +35,32 @@ features=(--enable-bulk-memory --enable-bulk-memory-opt --enable-call-indirect-o
|
||||
--enable-mutable-globals --enable-nontrapping-float-to-int --enable-reference-types --enable-sign-ext)
|
||||
|
||||
mkdir -p "$out"
|
||||
rm -rf "$out"/terminal* "$out/fonts"
|
||||
rm -rf "$out"/terminal* "$out/immutable" "$out/fonts"
|
||||
|
||||
# sed expressions that replace each {name} in index.html with that build's path
|
||||
paths=()
|
||||
|
||||
# SHA-256 of stdin. Older macOS has shasum but no sha256sum.
|
||||
sha256() { if command -v sha256sum >/dev/null; then sha256sum; else shasum -a 256; fi; }
|
||||
|
||||
# build <name> [cargo args...]
|
||||
build() {
|
||||
local name=$1
|
||||
local name=$1 tmp="$out/immutable/.$1" hash
|
||||
shift
|
||||
cargo build --profile web --target "$target" -p terminal-rs "$@"
|
||||
wasm-bindgen --target web --no-typescript --out-dir "$out" --out-name "$name" "$wasm"
|
||||
wasm-opt -Os "${features[@]}" --strip-debug --strip-producers "$out/${name}_bg.wasm" -o "$out/${name}_bg.wasm"
|
||||
wasm-bindgen --target web --no-typescript --out-dir "$tmp" --out-name "$name" "$wasm"
|
||||
wasm-opt -Os "${features[@]}" --strip-debug --strip-producers "$tmp/${name}_bg.wasm" -o "$tmp/${name}_bg.wasm"
|
||||
hash=$(cd "$tmp" && find . -type f | LC_ALL=C sort | xargs cat | sha256 | cut -c 1-16)
|
||||
mv "$tmp" "$out/immutable/$hash"
|
||||
paths+=(-e "s#{$name}#immutable/$hash/$name#g")
|
||||
}
|
||||
build terminal
|
||||
build terminal-webgl --features webgl
|
||||
cp app/web/index.html "$out/index.html"
|
||||
sed "${paths[@]}" app/web/index.html >"$out/index.html"
|
||||
if grep -n '{terminal[a-z-]*}' "$out/index.html" >&2; then
|
||||
echo "index.html names a build that this script doesn't make" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# egui's emoji fonts, which the page fetches after start-up (see app/src/web.rs), with their licenses.
|
||||
fonts=$(cargo metadata --format-version 1 --locked --filter-platform "$target" | python3 -c '
|
||||
@@ -54,9 +72,8 @@ cp "$fonts"/{NotoEmoji-Regular.ttf,OFL.txt,emoji-icon-font.ttf,emoji-icon-font-m
|
||||
|
||||
size() { wc -c <"$1" | tr -d ' '; }
|
||||
echo "wrote $out"
|
||||
for name in terminal terminal-webgl; do
|
||||
f="$out/${name}_bg.wasm"
|
||||
for f in "$out"/immutable/*/*_bg.wasm; do
|
||||
gz=$(gzip -9c "$f" | wc -c | tr -d ' ')
|
||||
br=$(command -v brotli >/dev/null && brotli -c -q 11 "$f" | wc -c | tr -d ' ' || echo "n/a")
|
||||
echo "${name}_bg.wasm: $(size "$f") bytes raw, $gz gzip -9, $br brotli -q 11; ${name}.js: $(size "$out/$name.js") bytes"
|
||||
echo "${f#"$out"/}: $(size "$f") bytes raw, $gz gzip -9, $br brotli -q 11; $(basename "${f%_bg.wasm}").js: $(size "${f%_bg.wasm}.js") bytes"
|
||||
done
|
||||
@@ -63,6 +63,18 @@ The workflow reads three repository secrets:
|
||||
To redeploy without a new commit, run the workflow from the Actions tab. Run on `master`, it
|
||||
deploys to production.
|
||||
|
||||
`vercel.json` also routes the browser app. `/app` redirects once to `/app/`, which serves
|
||||
`static/app/index.html` without a redirect, so the page's relative URLs resolve under `/app/`.
|
||||
`scripts/build-web.sh` puts each build's JS and wasm in `static/app/immutable/<hash>/`, named
|
||||
after a hash of those files, and the browser caches everything under `/app/immutable/` for a
|
||||
year. A new build gets new URLs, so a page never loads one build's JS with another build's wasm.
|
||||
`index.html` and the fonts keep Vercel's default `max-age=0, must-revalidate`, so the browser
|
||||
checks them on every visit. A request under `/app/immutable/` for a file that doesn't exist gets a
|
||||
404 with `no-store`, so that year-long header never sticks to a 404. `vercel.json` uses `routes`
|
||||
rather than `headers` and `rewrites` because only `routes` can add that rule after the filesystem
|
||||
check, the same way the SvelteKit adapter guards `/_app/immutable/`. `npm run dev` doesn't apply
|
||||
`vercel.json`; open `/app/index.html` there.
|
||||
|
||||
## Emails
|
||||
|
||||
`POST /download` adds each email to the `emails` sorted set in Upstash Redis, scored by when the
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
/**
|
||||
* The browser build lives in `static/app` (written by `scripts/build-web.sh`, not committed). Static
|
||||
* folders have no index page here, so send `/app` to the page itself.
|
||||
*/
|
||||
export const GET: RequestHandler = ({ url }) => {
|
||||
// Keep the query (`?sample`, `?fps`).
|
||||
redirect(308, `/app/index.html${url.search}`);
|
||||
};
|
||||
+17
-1
@@ -2,5 +2,21 @@
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"git": {
|
||||
"deploymentEnabled": false
|
||||
}
|
||||
},
|
||||
"routes": [
|
||||
{ "src": "^/app$", "status": 308, "headers": { "Location": "/app/" } },
|
||||
{
|
||||
"src": "^/app/immutable/.+",
|
||||
"headers": { "Cache-Control": "public, max-age=31536000, immutable" },
|
||||
"continue": true
|
||||
},
|
||||
{ "handle": "filesystem" },
|
||||
{ "src": "^/app/$", "dest": "/app/index.html", "check": true },
|
||||
{
|
||||
"src": "^/app/immutable/.+",
|
||||
"status": 404,
|
||||
"headers": { "Cache-Control": "no-store" },
|
||||
"continue": false
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user