diff --git a/CHANGELOG.md b/CHANGELOG.md index 022e6b1..ea4c585 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - [#18](https://github.com/nrepl/proof/pull/18): The `version` snippet, so reports and the compatibility matrix show which version of a server proof checked. - [#18](https://github.com/nrepl/proof/pull/18): `proof matrix -html`, which builds the compatibility matrix as a web page, and the daily matrix of the bundled profiles on [GitHub Pages](https://nrepl.github.io/proof/). - [#19](https://github.com/nrepl/proof/pull/19): Profiles for nbb and let-go, and scenarios for `proof serve` that act like them. +- [#20](https://github.com/nrepl/proof/pull/20): Client profiles for clojure-cli.repl and `bb repl --connect`. ## 0.1.0 (2026-10-26) diff --git a/PLAN.md b/PLAN.md index 11f9842..90192dc 100644 --- a/PLAN.md +++ b/PLAN.md @@ -162,10 +162,13 @@ clients. ### More Clients -- [clojure-cli.repl](https://github.com/clojure/clojure-cli.repl), the - new client of the Clojure team, which doesn't handle `need-input` as - of 0.1.1. -- `bb repl --connect`. +- Done: [clojure-cli.repl](https://github.com/clojure/clojure-cli.repl), + the new client of the Clojure team. It's meant for the server that + comes with it, but it works with others too. As of 0.2.3 it still + doesn't answer `need-input`, so code reading input hangs, and it never + closes its session. +- Done: `bb repl --connect`, which every server in the matrix gets + right. ## After 0.2.0 diff --git a/README.md b/README.md index 93a7d9f..afc832f 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,8 @@ get a warning instead. Some of the checks send what a particular client sends, the way it sends it, e.g. `cider.connect` sends the requests CIDER sends while connecting. That way the report tells you directly whether CIDER, Calva, Conjure, -vim-fireplace, REPLy and neorepl will work with your server. +vim-fireplace, REPLy, neorepl, clojure-cli.repl and `bb repl --connect` +will work with your server. ## Checking Clients @@ -134,10 +135,11 @@ the replies). `-like jank` turns on all of jank's scenarios at once, and proof is still in its early days. Right now it covers the core of the protocol (`describe`, unknown ops, `eval`, sessions, `stdin`, `interrupt`, `load-file`, completions, lookups and the wire format), -requests that go wrong, what CIDER, Calva, Conjure, vim-fireplace, REPLy -and neorepl send while connecting, evaluating code and loading files, -the requests clients send and the server differences clients have to -deal with, and `proof list` will show you all the checks. +requests that go wrong, what CIDER, Calva, Conjure, vim-fireplace, REPLy, +neorepl, clojure-cli.repl and `bb repl --connect` send while connecting, +evaluating code and loading files, the requests clients send and the +server differences clients have to deal with, and `proof list` will show +you all the checks. Check out [PLAN.md](PLAN.md) for what's next. diff --git a/doc/design.md b/doc/design.md index 8c6435d..96942e5 100644 --- a/doc/design.md +++ b/doc/design.md @@ -132,8 +132,9 @@ it. Each situation becomes a check that sends those requests, the way the client sends them, and fails at the first reply the client couldn't use. Replies that only bother the client (e.g. an error the client just shows to the user) get a note. Right now there are profiles for CIDER, -Calva, Conjure, vim-fireplace, REPLy and neorepl, built from what they -send with their default settings. +Calva, Conjure, vim-fireplace, REPLy, neorepl, clojure-cli.repl and +`bb repl --connect`, built from what they send with their default +settings. A new profile starts with a recording, as `proof proxy -record` saves what a client sent as a profile with a check for each connection. What diff --git a/internal/checks/checks_test.go b/internal/checks/checks_test.go index 22ef51b..1a68347 100644 --- a/internal/checks/checks_test.go +++ b/internal/checks/checks_test.go @@ -73,9 +73,9 @@ func TestWellBehavedServerPassesEverything(t *testing.T) { // other check and rule must still pass. func TestChecksCatchMisbehaviour(t *testing.T) { F, W, S := check.Failed, check.Warned, check.Skipped - // What every client but REPLy goes through first. + // What every client but REPLy and clojure-cli.repl goes through first. connects := map[string]check.Verdict{"cider.connect": F, "calva.connect": F, "conjure.connect": F, "fireplace.connect": F, - "neorepl.connect": F} + "neorepl.connect": F, "bb-repl.connect": F} // The checks that can't tell whether the server has their op. noLoadFile := skipped(loadFileChecks()) // The interrupt checks, and the ones that interrupt the sleep snippet @@ -143,8 +143,8 @@ func TestChecksCatchMisbehaviour(t *testing.T) { {"integer value", quirks{intValue: true}, checktest.Merged(clientsLoad, map[string]check.Verdict{ "wire.field-types": F, "eval.value": F, "eval.survives-error": F, "eval.multiple-forms": F, "session.ephemeral": F, "session.persistent": F, "cider.eval": F, "cider.repl": F, "calva.eval": F, "conjure.eval": F, "fireplace.eval": F, - "neorepl.eval": F, "interrupt.survives": F, "interrupt.queued": F, "load-file.value": F, "robust.wrong-types": W, - "robust.disconnect": F, "robust.sessions-at-once": F, "robust.bad-frames": F})}, + "neorepl.eval": F, "clojure-cli.eval": F, "interrupt.survives": F, "interrupt.queued": F, "load-file.value": F, + "robust.wrong-types": W, "robust.disconnect": F, "robust.sessions-at-once": F, "robust.bad-frames": F})}, // Like cider-nrepl's pprint. {"printed values end in a newline", quirks{printNewline: true}, nil}, {"unsorted keys", quirks{unsortedKeys: true}, map[string]check.Verdict{"wire.canonical": W}}, diff --git a/internal/checks/clients/bb-repl.toml b/internal/checks/clients/bb-repl.toml new file mode 100644 index 0000000..f7a2b31 --- /dev/null +++ b/internal/checks/clients/bb-repl.toml @@ -0,0 +1,24 @@ +# What `bb repl --connect` (Babashka 1.13.229) sends while it connects. +# Its client is babashka.nrepl.impl.client, which is built on nREPL's own +# client library. It evaluates the user's code with nothing but the code +# and its session, like eval.value. The links point at the code that +# needs what the steps check. +name = "bb repl --connect" +code = "https://github.com/babashka/babashka/blob/3f6cf8c81a1c99fddb849bd4f953306a3f0f2013/" + +[[checks]] +id = "bb-repl.connect" +title = "bb repl --connect can connect" +why = "bb repl --connect clones a session and asks for describe before it shows a prompt, and it reads the ops and the versions in the reply as dicts, so a server that gets any of it wrong can't be used from it." + +[[checks.steps]] +send = { op = "clone" } +new-session = "repl" +why = "bb repl --connect can't connect" +refs = ["resources/src/babashka/babashka/nrepl/impl/client.clj#L16-L22"] + +[[checks.steps]] +send = { op = "describe" } +dicts = [["ops"], ["versions"]] +why = "bb repl --connect fails with an exception before it shows a prompt" +refs = ["resources/src/babashka/babashka/nrepl/impl/client.clj#L6-L9", "src/babashka/impl/repl.clj#L740-L750"] diff --git a/internal/checks/clients/clojure-cli.toml b/internal/checks/clients/clojure-cli.toml new file mode 100644 index 0000000..f31773d --- /dev/null +++ b/internal/checks/clients/clojure-cli.toml @@ -0,0 +1,30 @@ +# What the client of clojure-cli.repl 0.2.3 (clojure -M:attach) sends +# while it connects and when it evaluates code, without a config. It's +# meant for the server that comes with it, but it works with others, +# where its own ops (e.g. watch, on a connection of its own) get +# unknown-op. The links point at the code that needs what the steps +# check. +name = "clojure-cli.repl" +code = "https://github.com/clojure/clojure-cli.repl/blob/45f830e9b2eb6e3407a37e8d7d3075239817900d/client/src/clojure_cli/repl/" + +[[checks]] +id = "clojure-cli.eval" +title = "clojure-cli.repl can evaluate code" +why = "clojure-cli.repl clones a session when it connects and sends what the user types with the namespace of its prompt, so a server that gets either of them wrong leaves its users without a REPL." +needs = ["clojure"] + +[[checks.steps]] +send = { op = "clone" } +new-session = "repl" +why = "clojure-cli.repl can't connect" +refs = ["nrepl.clj#L29-L35", "client.clj#L429"] + +[[checks.steps]] +snippet = "value" +why = "users don't see the result of their code" +refs = ["nrepl.clj#L12-L17", "client.clj#L454-L458"] + +[checks.steps.send] +op = "eval" +session = "$repl" +ns = "user" diff --git a/internal/report/matrix.html b/internal/report/matrix.html index 0d218cb..2cac63f 100644 --- a/internal/report/matrix.html +++ b/internal/report/matrix.html @@ -93,7 +93,7 @@

nREPL Compatibility Matrix

Here's how the servers below fare against the checks of proof, which are mostly about what the clients people use (CIDER, Calva, Conjure, vim-fireplace, REPLy and -neorepl) need from a server.

+so on) need from a server.

pass
The server does what the clients need.
fail
Some client won't work properly with the server, or users lose data.
diff --git a/internal/serve/matrix_test.go b/internal/serve/matrix_test.go index 8c54f8c..d6dcc00 100644 --- a/internal/serve/matrix_test.go +++ b/internal/serve/matrix_test.go @@ -145,7 +145,7 @@ func TestPresetsGetTheColumnsOfTheirServers(t *testing.T) { F, W, S := check.Failed, check.Warned, check.Skipped noJava := map[string]check.Verdict{"fireplace.connect": S, "fireplace.eval": S} noLanguage := map[string]check.Verdict{"eval.ns": S, "eval.unknown-ns": S, "cider.eval": S, "calva.eval": S, - "conjure.eval": S} + "conjure.eval": S, "clojure-cli.eval": S} nreplInterrupts := map[string]check.Verdict{"interrupt.queued": F, "interrupt.other-connection": F, "wire.id": W, "wire.after-done": W} columns := map[string]map[string]check.Verdict{