babashka

https://github.com/babashka/babashka. Also see #sci, #nbb and #babashka-circleci-builds .
bringe 2021-02-02T00:50:38.257500Z

If I want to use babashka for project automation scripting, in a cross-platform way, would using the jar be wise?

bringe 2021-02-02T00:51:19.257900Z

Currently getting this error, but maybe I need to use a newer version of Java?

[brandon@brandon-desktop calva]$ java -jar bb.jar -h
Exception in thread "main" java.lang.UnsupportedClassVersionError: sci/impl/Reflector has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0

bringe 2021-02-02T00:51:48.258200Z

[brandon@brandon-desktop calva]$ java -version
openjdk version "1.8.0_275"
OpenJDK Runtime Environment (build 1.8.0_275-b01)
OpenJDK 64-Bit Server VM (build 25.275-b01, mixed mode)

ericdallo 2021-02-02T00:54:32.258300Z

Yeah, in this case the jar was byte compiled with JDK 11

๐Ÿ‘ 1
bringe 2021-02-02T00:55:03.258600Z

Thanks

bringe 2021-02-02T00:56:33.258800Z

Is it only runnable with that specific version? I tried the above with jdk 15 and got IllegalArgumentException: Uknown signal: PIPE

ericdallo 2021-02-02T01:02:55.259Z

Hum, it should be runnable with JDK 15

bringe 2021-02-02T01:12:33.261300Z

Thinking about it, I'm not sure the cross-platform / include-the-tool-in-project really is a big concern. Devs already need other tools to work on a given project. ๐Ÿ˜„

ericdallo 2021-02-02T03:24:04.263600Z

I managed to make a GH action windows build compiling the native image ๐Ÿ˜„ https://github.com/clojure-lsp/clojure-lsp/blob/master/.github/workflows/release.yml#L235-L279

๐Ÿ‘ 1
2021-02-02T04:15:39.265Z

Worst case you could build a static binary with graal maybe? The babashka docker file and scripts are pretty manageable

bringe 2021-02-02T20:41:54.283900Z

Thanks. It seems for dev use it makes sense to just require the developer of Calva install the bb binary for their machine.

borkdude 2021-02-02T08:35:54.265900Z

@brandon.ringe I'm not sure if the .jar adds anything beyond a normal clojure project. babashka is basically a clojure interpreter + a set of libraries. So using it from the JVM kind of defeats the purpose of good startup time.

borkdude 2021-02-02T08:36:41.266Z

Maybe you could explain your use case more. If this is just used as an alternative to clojure inside of Calva I suggest using the deps.clj jar.

borkdude 2021-02-02T08:36:48.266200Z

@pez ^

pez 2021-02-02T12:04:49.266500Z

Latest Calva actually is using deps.clj for this (on Windows). Have I then introduced a requirement for some special version of Java?

pez 2021-02-02T12:06:34.266700Z

As for @brandon.ringeโ€™s use case it is for a script during Calva development. Weโ€™ll probably just require that people have babashska installed to use the script.

borkdude 2021-02-02T12:06:46.266900Z

I thought you are using bb.exe

pez 2021-02-02T12:07:48.267100Z

It added a bit too many megabytes to the extension, so I switched to deps.clj.

borkdude 2021-02-02T12:12:44.267300Z

Makes sense

borkdude 2021-02-02T12:13:14.267500Z

As for scripting for development. I think people should just install bb for Calva dev. It's available for every major OS

borkdude 2021-02-02T12:13:28.267700Z

Or you can make your scripts portable with clojure by adding a deps.edn

borkdude 2021-02-02T12:13:37.267900Z

so people can run the scripts using that if they don't have bb and don't want to use it.

borkdude 2021-02-02T12:15:29.268200Z

But maybe it's ok to be opinionated for dev mode

pez 2021-02-02T13:00:15.268500Z

> As for scripting for development. I think people should just install bb forย Calvaย dev. Itโ€™s available for every major OS Yes, thatโ€™s where we are going.

ericdallo 2021-02-02T16:45:38.269200Z

Is it possible to test a process that accepts stdin and produces to stdout via babashka?

ericdallo 2021-02-03T13:32:00.296700Z

@borkdude Is it possible to run a babashka script (with bb interpreter on header) from windows?

borkdude 2021-02-03T13:32:41.296900Z

windows doesn't support this, so you have to run it like bb thescript.clj

ericdallo 2021-02-03T13:32:49.297100Z

๐Ÿ‘

ericdallo 2021-02-02T16:46:00.269300Z

my idea is to create some kind of integration test for clojure-lsp, where I run the process, send some json and expect other json

ericdallo 2021-02-02T16:46:17.269500Z

Is it possible with https://github.com/babashka/process?

ericdallo 2021-02-02T17:12:28.269900Z

I'm trying something like that:

#!/usr/bin/env bb

(require '[babashka.process :refer [process $ check]])

(with-out-str (check (process '[clojure-lsp]
                                {:in "{}\r\n" :out *out*})))

ericdallo 2021-02-02T17:13:33.270200Z

but it seems to keep listen to the process

ericdallo 2021-02-02T17:19:42.270400Z

Oh, just found the cat demo, I'll check

borkdude 2021-02-02T17:32:13.270700Z

@ericdallo you, you can add :in and :out args to a process

ericdallo 2021-02-02T17:35:18.270900Z

I'm trying with something like this:

(def clojure-lsp-proc (process '[clojure-lsp]))

(when-not (.isAlive (:proc clojure-lsp-proc))
  (println "TODO error")
  (System/exit 1))

(def stdin (io/writer (:in clojure-lsp-proc)))

(binding [*out* stdin]
  (println "{}\r\n"))

(.close stdin)

(println (slurp (:out clojure-lsp-proc)))

ericdallo 2021-02-02T17:36:00.271100Z

I'd need something that I can print to stdin and keep open and sync listen for stdout after each stdin input

ericdallo 2021-02-02T17:44:02.271400Z

oh, the issue is with slurp, since it's waiting for process finish

ericdallo 2021-02-02T17:44:14.271600Z

but the process will keep going, I only need the output response

borkdude 2021-02-02T17:44:58.271800Z

is the output EDN?

ericdallo 2021-02-02T17:45:59.272Z

no, server should respond with something like:

ericdallo 2021-02-02T17:46:13.272200Z

Content-Type: 232

{"some":"json"}

borkdude 2021-02-02T17:49:27.272600Z

so you can read a couple of lines of text?

borkdude 2021-02-02T17:49:46.272800Z

or first some content type line and then a json value?

borkdude 2021-02-02T17:49:57.273Z

you can easily do this without waiting for the process to end

ericdallo 2021-02-02T17:50:54.273200Z

yeah, I need to read the first line, then an new line and then a json

ericdallo 2021-02-02T18:07:59.273400Z

the issue is: server can return a json on that pattern anytime, and I just want to parse it, check if is the json I want or ignore it

ericdallo 2021-02-02T18:08:25.273600Z

so the server receives an input in that pattern and can start respondinhg with multiple responses following that pattern

ericdallo 2021-02-02T18:18:25.273800Z

For some reason it keeps waiting for the stout stream:

(def clojure-lsp-proc (process '[./clojure-lsp]))

(when-not (.isAlive (:proc clojure-lsp-proc))
  (println "TODO error")
  (System/exit 1))

(def stdin (io/writer (:in clojure-lsp-proc)))
(def stout (io/reader (:out clojure-lsp-proc)))

(binding [*out* stdin]
  (println "Content-Length: 3")
  (println "")
  (println "{}"))

(println (with-open [rdr stout]
           (reduce conj [] (line-seq rdr))))

borkdude 2021-02-02T18:45:05.274Z

this is because line-seq will consume until EOF and if your process is still alive and waiting for more input, you won't get an EOF

ericdallo 2021-02-02T18:46:40.274200Z

hum got it

ericdallo 2021-02-02T18:47:06.274400Z

what's the best approach here so? It'd be great if I could say, read next 3 lines (Content Type, newline and the json content)

borkdude 2021-02-02T18:51:00.274700Z

yeah, so do (read-line) twice, while binding *in* to the stdout an then read one JSON value from the stream

ericdallo 2021-02-02T18:54:49.274900Z

(def clojure-lsp-proc (process '[./clojure-lsp]))
;; (def clojure-lsp-proc (process '[cat]))

(when-not (.isAlive (:proc clojure-lsp-proc))
  (println "TODO error")
  (System/exit 1))

(def stdin (io/writer (:in clojure-lsp-proc)))
(def stout (io/reader (:out clojure-lsp-proc)))

(binding [*out* stdin]
  (println "Content-Length: 3")
  (println "")
  (println "{}"))

(binding [*in* stout]
  (println (read-line))
  (println (read-line))
  (println (read-line)))

(println "Done!")
Right? Still freezes

borkdude 2021-02-02T18:59:40.275100Z

But does it print the three lines?

borkdude 2021-02-02T18:59:54.275300Z

Try 2 lines. Maybe the third line doesn't have a newline?

ericdallo 2021-02-02T19:08:43.275500Z

No, its frozen ๐Ÿ˜•

ericdallo 2021-02-02T19:09:24.275900Z

I changed to

(binding [*in* stout]
  (println "->" (read-line))
  (println "->" (read-line))
  (println "->" (read-line)))

ericdallo 2021-02-02T19:09:39.276100Z

and nothing is printed, so the read-line is not reading

ericdallo 2021-02-02T19:10:14.276300Z

Oh, also there is a time for the server start to respond (2-3s) Not sure if it's related

borkdude 2021-02-02T19:11:17.276500Z

@ericdallo try :out :inherit, this will print the output to stdout directly

๐Ÿ‘ 1
borkdude 2021-02-02T19:11:22.276700Z

for debugging

ericdallo 2021-02-02T19:14:18.277Z

Not sure I did something wrong:

(def clojure-lsp-proc (process '[./clojure-lsp]
                               {:out :inherit}))

(def stdin (io/writer (:in clojure-lsp-proc)))
(def stout (io/reader (:out clojure-lsp-proc)))

(binding [*out* stdin]
  (println "Content-Length: 3")
  (println "")
  (println "{}"))

(binding [*in* stout]
  (println "->" (read-line))
  (println "->" (read-line))
  (println "->" (read-line)))

(println "Done!")
:
-> nil
-> nil
-> nil
Done!

borkdude 2021-02-02T19:15:43.277300Z

in this case you should not read from stdout, since it's already redirected

borkdude 2021-02-02T19:16:20.277500Z

it basically does nothing

ericdallo 2021-02-02T19:27:44.277700Z

oh, alright

ericdallo 2021-02-02T19:28:09.277900Z

so commenting the binding, it starts, print the Done and finish

ericdallo 2021-02-02T19:28:32.278100Z

it seems is not waiting for the server start

borkdude 2021-02-02T19:29:03.278300Z

because your script isn't waiting for something

borkdude 2021-02-02T19:29:10.278500Z

it just terminates

borkdude 2021-02-02T19:29:28.278700Z

Try (Thread/sleep 10000) or something

ericdallo 2021-02-02T19:31:57.278900Z

it just wait for the sleep then end, prints nothing

ericdallo 2021-02-02T19:32:13.279100Z

(def clojure-lsp-proc (process '[./clojure-lsp]
                               {:out :inherit}))

(def stdin (io/writer (:in clojure-lsp-proc)))
;; (def stout (io/reader (:out clojure-lsp-proc)))

(binding [*out* stdin]
  (println "Content-Length: 3")
  (println "")
  (println "{}"))

;; (binding [*in* stout]
;;   (println "->" (read-line))
;;   (println "->" (read-line))
;;   (println "->" (read-line)))

(println "Done!")

(Thread/sleep 20000)

borkdude 2021-02-02T19:42:36.279300Z

if it doesn't print anything, then something is probably wrong. Add some debugging by printing to stderr from the process. Also use :err :inherit.

ericdallo 2021-02-02T19:43:17.279600Z

oh, yeah, the :err :inherit worked ๐Ÿ˜„

ericdallo 2021-02-02T19:43:44.279800Z

sorry, I forgot to mention that a {} should return an error, but I though it was considered output anyway

ericdallo 2021-02-02T19:43:55.280Z

I'll build a valid LSP spec json and test it

ericdallo 2021-02-02T19:47:31.280200Z

Ok, it worked:

ericdallo 2021-02-02T19:47:51.280600Z

Now I need to remove the debugging out inherit and handle that without sleeping somehow

ericdallo 2021-02-02T19:49:25.280800Z

Ok, with this:

(def clojure-lsp-proc (process '[./clojure-lsp]))

(def stdin (io/writer (:in clojure-lsp-proc)))
(def stout (io/reader (:out clojure-lsp-proc)))

(binding [*out* stdin]
  (println "Content-Length: 59")
  (println "")
  (println "{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"params\":{},\"id\":1}"))

(binding [*in* stout]
  (println "->" (read-line))
  (println "->" (read-line))
  (println "->" (read-line)))

(println "Done!")
I get:

ericdallo 2021-02-02T19:49:46.281200Z

For some reason the last read-line is not being printed

borkdude 2021-02-02T19:52:30.281400Z

Maybe because the json isn't ending in a newline?

borkdude 2021-02-02T19:53:33.281600Z

can you try (cheshire.json/parse-stream *in*) instead of the third read-line?

ericdallo 2021-02-02T19:54:53.281800Z

Hum, I imagine cheshire.json/ is not bundled with bb?

ericdallo 2021-02-02T19:54:59.282Z

it can't find the require

borkdude 2021-02-02T19:55:15.282200Z

eh cheshire.core, typo

๐Ÿ‘ 1
ericdallo 2021-02-02T19:56:22.282500Z

Yeah, it seems to work ๐ŸŽ‰

ericdallo 2021-02-02T19:56:41.282900Z

thanks for the help @borkdude!

borkdude 2021-02-02T19:59:41.283100Z

๐ŸŽ‰

ericdallo 2021-02-02T19:59:59.283300Z

bb is really a game changer

ericdallo 2021-02-02T20:00:32.283500Z

before trying bb, I searched a little bit on how to read process output dynamically and send input via bash

ericdallo 2021-02-02T20:00:51.283700Z

and I need to say that is a pretty mess hahaha