ci(pages): build the wasm bundle and publish web/ to GitHub Pages
improvise-d31's finished artifact depends on improvise-9x6 (the worker-hosted server) through improvise-djm. The *pipeline* does not, and building it now is what stops the delivery half from being a mystery when the worker lands: the swap from websocket transport to worker transport then changes what is bundled, not how it is built and shipped. Two jobs in ci.yml rather than a second workflow. `pages-build` runs on every push and pull request, so a change that breaks `web/build.sh` or the bundle fails on the PR that made it. `pages-deploy` runs only on a push to main and `needs: [test, wasm, pages-build]`, so nothing that failed `cargo test` or the existing wasm32 gate can reach the published site — the deploy depends on those checks instead of repeating them. Deliberately not path-filtered. The bundle is improvise-wasm-client plus improvise-protocol, improvise-core and improvise-formula, so a filter meaning "skip when the bundle cannot have changed" would have to name most of the workspace, and getting it wrong leaves a stale site with nothing saying so. wasm-pack is a pinned release tarball, v0.15.0 — the version `nix develop` now supplies — so the local run that produced 346 KB of wasm (120 KB gzipped) is evidence about the CI run. The devShell was missing wasm-pack entirely: the `wasm` job only runs `cargo check`, which does not need it, so nothing had ever required the one tool that turns a passing check into a loadable page. What this cannot do yet, said on the page itself rather than routed around: web/app.js is a thin client that holds no model, so a copy served from a static host paints an empty grid until it is pointed at an improvise-ws-server with ?ws=. The notice is static HTML, not something app.js decides, because it is true on every load and a page that cannot reach a server cannot be trusted to explain why. README says the same and names the deployed URL. Verified: cargo test --workspace 1134 passed / 0 failed, clippy clean, fmt clean, actionlint clean, ./web/build.sh succeeds and the assemble step produces a 388 KB site whose every reference resolves inside it. NOT verified: the workflow has never run — GitHub Pages is not enabled on the repository (GET /repos/fiddlerwoaroof/improvise/pages is 404), which is improvise-3gy. The deploy job asks configure-pages to enable it; if the token may not, that step is where the run fails and the fix is the repository setting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EoW5BB87ZZdm96SvBTWawH
This commit is contained in:
@@ -68,3 +68,95 @@ jobs:
|
||||
run: |
|
||||
nix shell nixpkgs#trealla --command tpl prolog-sessions/kb.pl \
|
||||
-g "(kb_consistent -> halt(0) ; (kb_report, halt(1)))"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GitHub Pages delivery (improvise-d31).
|
||||
#
|
||||
# Two jobs on purpose. `pages-build` runs on every push and pull request, so
|
||||
# a change that breaks `web/build.sh` or the bundle fails on the PR that made
|
||||
# it — the build is a gate. `pages-deploy` runs only on a push to main and
|
||||
# `needs` the two check jobs above, so nothing that failed `cargo test` or the
|
||||
# wasm32 gate can reach the published site. The deploy depends on those checks
|
||||
# rather than repeating them (improvise-d31 AC 2).
|
||||
#
|
||||
# Not path-filtered, deliberately. The bundle is `improvise-wasm-client` plus
|
||||
# improvise-protocol plus improvise-core plus improvise-formula, so a filter
|
||||
# that meant "skip when the bundle cannot have changed" would have to name
|
||||
# most of the workspace, and the failure mode of getting it wrong is a stale
|
||||
# site with no signal that it is stale. A redundant build is the cheaper error.
|
||||
pages-build:
|
||||
name: Browser bundle + static site
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
# Pinned release binary rather than `cargo install` (a three-minute
|
||||
# source build) or a third-party action. wasm-pack runs its own bundled
|
||||
# `wasm-opt`, which is why improvise-wasm-client's Cargo.toml names
|
||||
# `--enable-bulk-memory`: without it the optimizer rejects the
|
||||
# `memory.copy` LLVM emits regardless.
|
||||
# v0.15.0 is what `nix develop` supplies, and the bundle was built and
|
||||
# measured against it locally (346 KB, 120 KB gzipped). Pinning the same
|
||||
# version is what makes that local run evidence about this one.
|
||||
- name: Install wasm-pack
|
||||
env:
|
||||
WASM_PACK_VERSION: v0.15.0
|
||||
run: |
|
||||
set -eu
|
||||
url="https://github.com/rustwasm/wasm-pack/releases/download/${WASM_PACK_VERSION}/wasm-pack-${WASM_PACK_VERSION}-x86_64-unknown-linux-musl.tar.gz"
|
||||
curl -sSfL "$url" | tar -xz --strip-components=1 -C /usr/local/bin \
|
||||
"wasm-pack-${WASM_PACK_VERSION}-x86_64-unknown-linux-musl/wasm-pack"
|
||||
wasm-pack --version
|
||||
|
||||
# The same script a developer runs locally, so there is one answer to
|
||||
# "how is the bundle built" rather than two that can drift.
|
||||
- name: Build the wasm bundle
|
||||
run: ./web/build.sh
|
||||
|
||||
# `web/` is the source directory: it also holds build.sh and .gitignore,
|
||||
# which have no business being served. Assemble what ships.
|
||||
- name: Assemble the site
|
||||
run: |
|
||||
set -eu
|
||||
mkdir -p site
|
||||
cp web/index.html web/app.js web/style.css site/
|
||||
cp -rf web/pkg site/pkg
|
||||
rm -f site/pkg/.gitignore site/pkg/package.json
|
||||
find site -type f | sort
|
||||
du -sh site
|
||||
|
||||
- uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: site
|
||||
|
||||
pages-deploy:
|
||||
name: Deploy to GitHub Pages
|
||||
needs: [test, wasm, pages-build]
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
# A superseded deploy is allowed to finish rather than being cancelled:
|
||||
# `deploy-pages` cancelled midway can leave the Pages deployment in a
|
||||
# half-applied state, and the next push republishes anyway.
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
# `enablement: true` asks the API to turn Pages on with source =
|
||||
# GitHub Actions. It succeeds only if the repository allows the workflow
|
||||
# token to do so; if it does not, this step is where the run fails and
|
||||
# the fix is the repository setting, not this file (improvise-3gy).
|
||||
- uses: actions/configure-pages@v5
|
||||
with:
|
||||
enablement: true
|
||||
- id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
@@ -176,6 +176,35 @@ improvise cmd 'add-cat Region' 'add-item Region East' -f model.improv
|
||||
improvise script setup.txt -f model.improv
|
||||
```
|
||||
|
||||
## In the browser
|
||||
|
||||
The browser page is at **<https://fiddlerwoaroof.github.io/improvise/>**, built
|
||||
and published from `main` by the `pages-build` / `pages-deploy` jobs in
|
||||
`.github/workflows/ci.yml`. A deploy only happens after `cargo test` and the
|
||||
wasm32 check have passed.
|
||||
|
||||
**It needs a server, and a static host is not one.** The page is a thin client:
|
||||
it holds no model, and every question about a cell — its text, whether it may
|
||||
be typed into — is answered by a server and arrives already decided. So the
|
||||
published page paints an empty grid until it is pointed at one:
|
||||
|
||||
```sh
|
||||
cargo run -p improvise-ws-server -- examples/demo.improv --port 8787
|
||||
```
|
||||
|
||||
then open the page with `?ws=ws://127.0.0.1:8787`. Over https a `ws://`
|
||||
address is blocked as mixed content, so for now a local server is most easily
|
||||
paired with a local page:
|
||||
|
||||
```sh
|
||||
./web/build.sh # wasm-pack -> web/pkg (gitignored)
|
||||
python3 -m http.server 8080 --directory web
|
||||
```
|
||||
|
||||
Running with no separate process is the in-page worker, improvise-9x6, which
|
||||
is not built yet. Until it lands, what is deployed is the delivery pipeline,
|
||||
not a working demo.
|
||||
|
||||
## What's interesting about the architecture
|
||||
|
||||
All user actions flow through a two-phase command/effect pipeline. Commands
|
||||
|
||||
@@ -50,6 +50,12 @@
|
||||
pkgs.universal-ctags
|
||||
beads.packages.${system}.default
|
||||
|
||||
# `web/build.sh` — and so the Pages job's local dry run — needs this.
|
||||
# The `wasm` CI job only runs `cargo check`, which does not, so the
|
||||
# shell was missing the one tool that turns a passing check into a
|
||||
# loadable page (improvise-d31).
|
||||
pkgs.wasm-pack
|
||||
|
||||
# Demo recording and release tooling
|
||||
pkgs.asciinema
|
||||
pkgs.vhs
|
||||
|
||||
@@ -34,6 +34,32 @@
|
||||
<span id="status" role="status"></span>
|
||||
</header>
|
||||
|
||||
<!--
|
||||
This page is the *client* half and nothing else. It holds no model: what
|
||||
a cell is, what it says and whether it may be typed into is decided by a
|
||||
server and arrives already decided. So a copy served from a static host
|
||||
— GitHub Pages included — paints an empty grid and says "disconnected"
|
||||
until it is pointed at one.
|
||||
|
||||
Two servers are possible. `improvise-ws-server`, which exists, is a
|
||||
native process you run yourself; pass its address as ?ws=ws://host:port.
|
||||
The in-page worker (improvise-9x6), which does not exist yet, is what
|
||||
makes this page standalone. Until it lands the honest description of a
|
||||
Pages deployment is "the delivery pipeline, carrying a client with
|
||||
nowhere to connect" — which is why this notice is static HTML rather
|
||||
than something app.js decides: it is true on every load, and a page that
|
||||
cannot reach a server cannot be trusted to explain why.
|
||||
-->
|
||||
<p id="backend-notice">
|
||||
improvise in the browser is a thin client: the document lives in a
|
||||
server. Run <code>improvise-ws-server model.improv --port 8787</code>
|
||||
and reload with <code>?ws=ws://127.0.0.1:8787</code>. Served over
|
||||
https, a <code>ws://</code> address is blocked as mixed content — use
|
||||
<code>wss://</code> or a local http server. Running with no separate
|
||||
process at all is the in-page worker (improvise-9x6), and it is not
|
||||
built yet.
|
||||
</p>
|
||||
|
||||
<main>
|
||||
<aside id="categories" aria-label="Categories"></aside>
|
||||
<div id="grid" tabindex="0" aria-label="Grid"></div>
|
||||
|
||||
@@ -139,3 +139,17 @@ td input {
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* The page cannot reach a server from a static host, and says so. */
|
||||
#backend-notice {
|
||||
margin: 0;
|
||||
padding: 0.5rem 1rem;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
#backend-notice code {
|
||||
background: color-mix(in srgb, var(--line) 45%, transparent);
|
||||
padding: 0 0.25rem;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user