Skip to content

core: repoint two remote.hpp comments at handleImpl (#356) #394

core: repoint two remote.hpp comments at handleImpl (#356)

core: repoint two remote.hpp comments at handleImpl (#356) #394

Workflow file for this run

name: WASM ladder gate
# Compile gate for the application ladder's WebAssembly clients — the one
# examples/TESTING.md's CI tiering promises ("the WASM compile gate for the
# affected rungs") and the only thing in this repository that can actually
# verify them: no Emscripten toolchain was available where rung 0's WASM-remote
# spike (examples/common/wasm_spike) or rung 1's WASM client
# (examples/pastebin/gui_wasm) were authored, so both shipped structurally
# complete and never compiled. Until this job runs green, treat every WASM
# target here as unverified.
#
# Deliberately separate from wasm-demo.yml (bank's WASM GUI): different sources,
# different path filter, and nothing here is deployed anywhere — this builds and
# stops. Single-threaded Qt-for-WASM, same as that workflow.
# The two `paths:` lists below are the one place in this repository where the
# rung list still has to be written out by hand. GitHub evaluates `on.*.paths`
# to decide whether to start the workflow at all, which happens before any step
# of it can run -- so nothing here can read examples/rungs.txt, the list every
# other consumer derives from. That is exactly how they fell a rung behind:
# both stopped at kanban, so ledger and lims matched nothing (morph#179).
#
# They are therefore checked against examples/rungs.txt from the outside, by
# scripts/check_rung_filters.sh in .github/workflows/drift-guard.yml, which
# fails the build naming any rung missing from either list. Adding a rung means
# adding two lines here; forgetting is caught on the same PR.
on:
push:
branches:
- master
paths:
- 'examples/common/**'
- 'examples/pastebin/**'
- 'examples/bookmarks/**'
- 'examples/polls/**'
- 'examples/kanban/**'
- 'examples/ledger/**'
- 'examples/lims/**'
- 'examples/crm/**'
- 'examples/CMakeLists.txt'
- 'examples/rungs.txt'
- 'cmake/**'
- 'include/morph/**'
- 'src/qt/**'
- 'CMakeLists.txt'
- '.github/workflows/wasm-ladder.yml'
pull_request:
branches:
- master
paths:
- 'examples/common/**'
- 'examples/pastebin/**'
- 'examples/bookmarks/**'
- 'examples/polls/**'
- 'examples/kanban/**'
- 'examples/ledger/**'
- 'examples/lims/**'
- 'examples/crm/**'
- 'examples/CMakeLists.txt'
- 'examples/rungs.txt'
- 'cmake/**'
- 'include/morph/**'
- 'src/qt/**'
- 'CMakeLists.txt'
- '.github/workflows/wasm-ladder.yml'
concurrency:
group: wasm-ladder-${{ github.ref }}
cancel-in-progress: true
env:
QT_VERSION: 6.8.3
EMSDK_VERSION: 3.1.56 # the emscripten Qt 6.8 was built against
jobs:
build-ladder-wasm:
name: Build the ladder's WASM clients
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install build tools
run: |
sudo apt-get update -q
sudo apt-get install -y ninja-build
# aqtinstall gives a matched host + wasm Qt pair (same cmake glue), so no
# host/target version skew. qtwebsockets on *both*: morph::qt links
# Qt6::WebSockets, and a ladder WASM client is a remote client by rule
# (examples/IMPLEMENTATION.md rule 4's WASM clause), so the transport is
# not optional here the way it is for bank's local-only demo.
- name: Install Qt (host desktop)
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: linux
target: desktop
arch: linux_gcc_64
modules: qtwebsockets
dir: ${{ runner.temp }}/qt
- name: Install Qt (wasm, single-threaded)
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
host: all_os
target: wasm
arch: wasm_singlethread
modules: qtwebsockets
dir: ${{ runner.temp }}/qt
- name: Set up emsdk
uses: mymindstorm/setup-emsdk@v14
with:
version: ${{ env.EMSDK_VERSION }}
actions-cache-folder: emsdk-ladder-cache
# MORPH_CLIENT_ONLY is mandatory, not a tuning knob: a rung's presenters
# are BridgeHandler<Model> templates, so the client names its model type
# even though it never hosts one — and without this option morph still
# emits the registrars that closure over that model's ODBC-backed
# execute() bodies, which cannot link in a browser
# (docs/spec/core/registry.md). morph_add_rung() fails the configure with
# that explanation if it is missing.
#
# MORPH_BUILD_TESTS=OFF: Catch2 binaries are not browser artifacts, and
# examples/common/CMakeLists.txt returns before its Catch2/Lightweight
# section under Emscripten for exactly that reason.
- name: Configure
run: |
export EM_CACHE="$PWD/.emcache"
mkdir -p "$EM_CACHE"
HOST=${{ runner.temp }}/qt/Qt/${{ env.QT_VERSION }}/gcc_64
WASM=${{ runner.temp }}/qt/Qt/${{ env.QT_VERSION }}/wasm_singlethread
# The all_os/wasm package extracts its scripts without the exec bit.
chmod +x "$WASM"/bin/* || true
"$WASM/bin/qt-cmake" -S . -B build-wasm-ladder -G Ninja \
-DQT_HOST_PATH="$HOST" \
-DMORPH_BUILD_QT=ON \
-DMORPH_BUILD_FORMS_QML=ON \
-DMORPH_BUILD_LADDER=ON \
-DMORPH_LADDER_RUNGS=all \
-DMORPH_CLIENT_ONLY=ON \
-DMORPH_BUILD_TESTS=OFF \
-DMORPH_BUILD_EXAMPLES=OFF
# The rung-0 spike and every rung that has a gui_wasm/ directory, built
# by name so a target that silently stops being generated
# (morph_add_rung() skips a rung's gui_wasm when its prerequisites are
# missing, announcing why) fails this job instead of passing it
# vacuously. The plain build that follows covers everything else.
#
# The rungs are read from examples/rungs.txt rather than listed here --
# this loop used to name `pastebin bookmarks polls` literally, a third
# hand-copy of the rung list in this repository (morph#179).
#
# The gate is now the presence of `examples/<rung>/gui_wasm/`, not of
# `examples/<rung>/`. Two cases it has to tell apart, and the directory
# is what distinguishes them:
#
# - The rung is not in this checkout at all. Rungs land as their own
# PRs against the shared foundation this workflow lives in (see
# LADDER.md), so a foundation-only checkout has none of them yet --
# the "no rung exists" case morph_add_rung.cmake's header already
# documents as an expected, silent no-target outcome.
# - The rung exists but has no WASM client yet. True of kanban (rung
# 4), ledger (rung 5) and lims (rung 6) today: they ship gui/ but no
# gui_wasm/, so there is no ladder_<rung>_gui_wasm target to build
# and naming one would fail the job for a target that was never
# supposed to exist.
#
# Where gui_wasm/ *is* present the tripwire is in full force, and now
# covers every such rung automatically: morph_add_rung() skipping the
# target for any other reason (missing gui_lib, missing QML module,
# MORPH_BUILD_FORMS_QML off) still fails this job, unchanged.
- name: Build the WASM-remote spike and every rung's WASM client
run: |
set -euo pipefail
export EM_CACHE="$PWD/.emcache"
cmake --build build-wasm-ladder --target morph_ladder_wasm_spike
# Substituted before the loop so a failing reader aborts the step
# rather than yielding an empty list to iterate over.
rungs="$(bash scripts/ladder_rungs.sh list)"
built=0
while IFS= read -r rung; do
[ -n "$rung" ] || continue
if [ -d "examples/$rung/gui_wasm" ]; then
cmake --build build-wasm-ladder --target "ladder_${rung}_gui_wasm"
built=$((built + 1))
else
echo "::notice::examples/$rung/gui_wasm not present in this checkout -- skipping ladder_${rung}_gui_wasm (see wasm-ladder.yml's comment)"
fi
done <<< "$rungs"
# A loop that named no target at all would pass this step while
# building nothing, which is the same shape of defect as the filter
# that started morph#179. At least one rung has a WASM client.
if [ "$built" -eq 0 ]; then
echo "::error::no ladder_<rung>_gui_wasm target was built -- the named-target tripwire matched nothing"
exit 1
fi
# Catches any further rung's WASM client too, without editing this
# file again -- closing the gap rung 1's own final review flagged.
cmake --build build-wasm-ladder
# Informational: the build steps above are the gate. Listed rather than
# asserted by path, since where Qt drops a wasm bundle is Qt's business.
- name: Show the produced artifacts
run: find build-wasm-ladder -name '*.wasm' -o -name '*.html' | sort