Skip to content

Commit 153ae71

Browse files
committed
release: v1.10.1 — the last release; demos retired, zwasm dep repointed
Smell-audited: 1: version-only releases are where "no behaviour change" gets used to skip verification, so this one still takes the full gate before the tag. The two workflow hardenings are not opportunistic scope — they are the steps that MUST work exactly once more, and one of them (view-then-create) is a race this repo has documented as real and unfixed since v1.10.0 cleared it by 47 s. v1.10.0 was meant to be the final release. Two things about the project's surroundings changed within days of it, and a tag that documents a world which no longer exists is worse than one more patch: - zwasm left the `clojurewasm` org, so the final tag would have pointed a dependency URL at a redirect rather than at the repository. - cw-playground and cw-serverless-demo are being shut down and archived, so the README's "Try it live" section would have promised two URLs that answer nothing. `cljw` itself is unchanged — same runtime, same behaviour. Docs: the demos are described in the past tense and point at their (archived) source, which still builds and remains the most complete worked example of an end-to-end cljw deployment. The technical write-up in docs/works/demos.md is kept deliberately: it is the record of what was built, and losing it would cost more than the dead links do. Release-path hardening, both in workflows that now get exactly one more consequential run: - release.yml's `gh release view || gh release create` is TOCTOU across the two matrix legs — both can see "no release", both create, and the loser dies on "already exists" under `set -e`, failing a leg whose artifact was fine. A failed create is now accepted IF the release exists afterwards; a create that failed for any other reason still fails the step, because the trailing view finds nothing. All four paths verified against real exit codes. - Both workflows `tar -x` the Zig toolchain into a directory they do not clear first. On a fresh hosted runner that is fine, which is exactly why it would only ever fail on a re-run — tar reports "Cannot open: File exists" and delays it into a non-zero exit. ci.yml additionally `mv`s into a path that, if it existed, would nest the toolchain one level deeper and silently leave `zig` off PATH. Both now clear the targets first.
1 parent d096811 commit 153ae71

6 files changed

Lines changed: 99 additions & 23 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ jobs:
8484
source .github/versions.lock
8585
mkdir -p ~/.local
8686
curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/${{ matrix.zig-dir }}.tar.xz" -o /tmp/zig.tar.xz
87+
# Clear both paths first so a re-run is idempotent: tar refuses to
88+
# overwrite existing entries ("Cannot open: File exists"), and `mv` into
89+
# an EXISTING directory nests instead of replacing, which would put the
90+
# toolchain at zig-0.16.0/<zig-dir>/ and leave `zig` off PATH.
91+
rm -rf "$HOME/.local/${{ matrix.zig-dir }}" "$HOME/.local/zig-0.16.0"
8792
tar -xJf /tmp/zig.tar.xz -C ~/.local
8893
mv "$HOME/.local/${{ matrix.zig-dir }}" "$HOME/.local/zig-0.16.0"
8994

‎.github/workflows/release.yml‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ jobs:
5353
source .github/versions.lock
5454
mkdir -p ~/.local
5555
curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/${{ matrix.zig-dir }}.tar.xz" -o /tmp/zig.tar.xz
56+
# Clear the target first so a re-run is idempotent: tar refuses to
57+
# overwrite existing entries ("Cannot open: File exists") and, under
58+
# `set -e`, delays that into a non-zero exit at the end of extraction.
59+
rm -rf "$HOME/.local/${{ matrix.zig-dir }}"
5660
tar -xJf /tmp/zig.tar.xz -C ~/.local
5761
echo "$HOME/.local/${{ matrix.zig-dir }}" >> "$GITHUB_PATH"
5862
@@ -74,7 +78,16 @@ jobs:
7478
shell: bash
7579
run: |
7680
set -euo pipefail
77-
# Create the release once (idempotent — later matrix legs just upload).
78-
gh release view "$TAG_NAME" >/dev/null 2>&1 || \
79-
gh release create "$TAG_NAME" --title "$TAG_NAME" --generate-notes
81+
# Create the release once; later matrix legs just upload.
82+
#
83+
# view-then-create is a TOCTOU race across the matrix: both legs can
84+
# see "no release" and both call create, and the loser exits non-zero
85+
# on "already exists", failing an otherwise-good leg under `set -e`.
86+
# (v1.10.0's two legs reached this 47 s apart — it was luck, not
87+
# design.) So treat a failed create as fine IF the release exists
88+
# afterwards; a create that failed for any other reason still fails
89+
# the step, because the trailing view then finds nothing.
90+
gh release view "$TAG_NAME" >/dev/null 2>&1 \
91+
|| gh release create "$TAG_NAME" --title "$TAG_NAME" --generate-notes \
92+
|| gh release view "$TAG_NAME" >/dev/null 2>&1
8093
gh release upload "$TAG_NAME" dist/* --clobber

‎CHANGELOG.md‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,50 @@ first stable `1.0.0` tag; pre-1.0 `alpha` / `rc` tags may still change surfaces.
77

88
## [Unreleased]
99

10+
## [1.10.1] - 2026-08-12
11+
12+
**The last release.** v1.10.0 was meant to be it; this one exists because two
13+
things about the project's surroundings changed in the days after, and leaving
14+
them only on `main` would mean the final *tag* documented a world that no longer
15+
exists. No runtime behaviour changes — `cljw` itself is byte-for-byte the same
16+
program.
17+
18+
### Changed
19+
20+
- **The embedded Wasm engine is fetched from `zwasm/zwasm`.** zwasm moved out of
21+
the `clojurewasm` org into its own, where it continues under separate
22+
maintainership. `build.zig.zon` now names that location directly instead of
23+
relying on GitHub's transfer redirect, which is only an alias until something
24+
else claims the vacated name. The pin itself is unchanged — same tag (v2.5.0),
25+
same commit, same content hash, verified by fetching both URLs.
26+
- **The two hosted demos are shut down and their repositories archived.**
27+
`cw-playground` and `cw-serverless-demo` ran on Fly until August 2026. The
28+
README and `docs/works/demos.md` now describe them in the past tense and point
29+
at the source rather than at URLs that no longer answer. The source still
30+
builds and remains the most complete worked example of an end-to-end `cljw`
31+
deployment.
32+
- **CI runs one configuration everywhere.** Pull requests, pushes and manual
33+
dispatches previously ran three different suites, all called "the gate", so
34+
"green" meant three different things depending on where it was said. They now
35+
all run the same `test/run_all.sh --serial-e2e` that a local
36+
`scripts/run_gate.sh` runs, and `check_gate_parity.sh` fails if a tier comes
37+
back. The nightly schedule is retired with it (ADR-0185).
38+
39+
### Fixed
40+
41+
- **The nREPL end-to-end tests leaked a server process each.** `( cd DIR && cmd ) &`
42+
cannot be exec-optimised by bash, so `$!` was the subshell rather than the
43+
runtime, and `kill $!` reaped the shell while orphaning its `cljw` child. Each
44+
survivor held its memory for the rest of the run, and the accumulated pressure
45+
was enough for macOS to SIGKILL an unrelated, freshly-built binary — a leak
46+
wearing the costume of a runtime crash. All four nREPL suites now reap by a
47+
port-scoped pattern and end with zero surviving processes.
48+
- **The entry-point parity test aborted instead of reporting.** Its whole job is
49+
to notice when one entry point behaves differently from the others, but under
50+
`set -e` a killed child's exit status propagated through command substitution
51+
and took the step down with no diagnosis. It now captures status and output per
52+
entry point, so a dying entry point is reported as a named divergence.
53+
1054
## [1.10.0] - 2026-08-12
1155

1256
**The final release.** ClojureWasm is no longer maintained — a from-scratch

‎README.md‎

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,23 @@ JVM Clojure calls Java and ClojureScript calls JavaScript; here the host is a
137137
language-neutral `.wasm`, so a function from Rust, Go, or C is indistinguishable
138138
from a Clojure one.
139139

140-
## Try it live
141-
142-
- **Playground** — <https://cw-playground.fly.dev> — run Clojure in your browser,
143-
evaluated in-process by `cljw`; call sandboxed Rust / Go WebAssembly modules
144-
over the FFI. ([source](https://github.com/clojurewasm/cw-playground))
145-
- **Bookshelf demo** — <https://cw-serverless-demo.fly.dev> — a small multi-user
146-
bookshelf served end-to-end by `cljw`'s own HTTP server, with SQLite and
147-
book-cover colours running in-process through the WebAssembly FFI, no JVM.
148-
([source](https://github.com/clojurewasm/cw-serverless-demo))
140+
## The demos (source only — the hosted instances are gone)
141+
142+
Two demos ran on Fly until August 2026. They are shut down with the project and
143+
their repositories are archived, but the source is the most complete example of
144+
what `cljw` did end to end, and each builds `cljw` from source in its
145+
`Dockerfile` — so they double as a working deployment recipe.
146+
147+
- **Playground** ([source](https://github.com/clojurewasm/cw-playground)) — a
148+
browser Clojure playground. Submissions were evaluated **in-process** on the
149+
server's `cljw` under a per-submission budget (`cljw.eval/with-budget` —
150+
steps / deadline / heap), and could call sandboxed Rust and Go WebAssembly
151+
modules over the FFI.
152+
- **Bookshelf** ([source](https://github.com/clojurewasm/cw-serverless-demo)) —
153+
a small multi-user bookshelf served end-to-end by `cljw`'s own HTTP server,
154+
no JVM: sessions, CRUD and persistence, with SQLite running as `sqlite3.wasm`
155+
and book-cover colours coming from a Rust module — both through the
156+
WebAssembly FFI.
149157

150158
## Documentation
151159

‎build.zig.zon‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.{
22
.name = .cljw,
3-
.version = "1.10.0",
3+
.version = "1.10.1",
44
.fingerprint = 0x1869d207073beffa,
55
.minimum_zig_version = "0.16.0",
66
.dependencies = .{

‎docs/works/demos.md‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,14 @@ still ahead.)
3737

3838
## 3. A real app — the Bookshelf (the polyglot story inside a real web app)
3939

40+
> The hosted instance ran on Fly until August 2026 and is shut down with the
41+
> project; the repository is archived. The description below is a record of what
42+
> it did, and the source still builds.
43+
4044
[`clojurewasm/cw-serverless-demo`](https://github.com/clojurewasm/cw-serverless-demo)
41-
(<https://cw-serverless-demo.fly.dev>) — a multi-user bookshelf served end-to-end
42-
by cljw's own HTTP server, no JVM. Sign in with Google, build your shelf, browse
43-
others — sessions, CRUD, persistence, server-rendered SPA.
45+
— a multi-user bookshelf served end-to-end by cljw's own HTTP server, no JVM.
46+
Sign in with Google, build your shelf, browse others — sessions, CRUD,
47+
persistence, server-rendered SPA.
4448

4549
The polyglot story runs *inside* the app, both over the Wasm FFI:
4650

@@ -49,14 +53,16 @@ The polyglot story runs *inside* the app, both over the Wasm FFI:
4953
persistence, no file-VFS.
5054
- **Book-cover colours** come from a hand-written Rust module via `(wasm/call …)`.
5155

52-
It deploys self-contained: the `Dockerfile` builds `cljw` from source (`-Dwasm`
53-
ReleaseSafe; zwasm via a pinned tag), and the SQLite store persists on a Fly
56+
It deployed self-contained: the `Dockerfile` builds `cljw` from source (`-Dwasm`
57+
ReleaseSafe; zwasm via a pinned tag), and the SQLite store persisted on a Fly
5458
volume. Config is environment-driven (`GOOGLE_CLIENT_ID` via `fly secrets`).
5559

5660
## 4. A Playground — eval Clojure on cljw in the browser
5761

62+
> Hosted instance shut down with the project (August 2026); repository archived.
63+
5864
[`clojurewasm/cw-playground`](https://github.com/clojurewasm/cw-playground)
59-
(<https://cw-playground.fly.dev>) — type Clojure, run it on cljw (not JVM Clojure),
65+
— type Clojure, run it on cljw (not JVM Clojure),
6066
see the printed output and result. Submissions are evaluated **in-process** on the
6167
server's cljw under a per-submission budget (`cljw.eval/with-budget` — steps /
6268
deadline / heap; a runaway is recovered as a value so the server survives), and can
@@ -68,9 +74,9 @@ the numeric tower, ratios, lazy seqs, STM, and the Wasm modules.
6874
- Verified on macOS arm64 + Ubuntu x86_64 only.
6975
- Throughput/GC have headroom; the strengths are startup (~4–5 ms), size (<4 MB),
7076
and a small footprint.
71-
- The deployed demos run a Debian-slim (glibc) image with the full `-Dwasm` build,
72-
so the polyglot Wasm FFI is live in production. (A static-musl build still omits
73-
`-Dwasm` — zwasm's GC uses a glibc-only `pthread_getattr_np` — but the Fly demos
74-
do not use musl.)
77+
- The demos ran a Debian-slim (glibc) image with the full `-Dwasm` build, so the
78+
polyglot Wasm FFI was live in production, not just in tests. (A static-musl
79+
build still omits `-Dwasm` — zwasm's GC uses a glibc-only
80+
`pthread_getattr_np` — but the Fly demos did not use musl.)
7581
- Deep Java interop (gen-class / deep proxy / deep reflection) is out of scope by
7682
design — this targets the "call Wasm from Clojure" corner, not a JVM replacement.

0 commit comments

Comments
 (0)