Fork me on GitHub

Finally decided to start using bb for shell stuff (I know -- slow on the uptake here!) and switched next.jdbc's to

🚀 6

Hints and tips on improving my bb usage/idioms welcome since I'm new to it! 🙂

Bob B02:10:34

Given the context, a feature that you might like is "babashka tasks" (<>). In this case, a lot of the 'post-processing' of the clojure invocation can be handled by tasks 'clojure' function... bb.edn lets you define tasks:

; bb.edn excerpt with a mocked-up task, could be replaced with -X... call
{:tasks {demo-fail
         {:doc  "print something and then fail with exit code 23"
          :task (let [env {"FOO" (first *command-line-args*)}] 
                  (clojure {:extra-env env} 
                           "-M -e '(println (System/getenv \"FOO\"))
                                   (System/exit 23)'"))}}}
and then from the directory where bb.edn lives:
$ bb tasks
The following tasks are available:

demo-fail print something and then fail with exit code 23
$ bb demo-fail maria
Error while executing task: demo-fail
$ echo $?
the 'clojure' function uses deps.clj to invoke the JVM (which uses babashka.process), so the options are more or less the same, and it handles the exit code processing (if you like that kind of thing)


@U013JFLRFS8 Yeah, unlikely I'll use that because my scripting outside of work is pretty simple stuff -- and at work we use build.clj and have the Clojure CLI vendored into our repo so that every tier only has to have a JDK and then they all use the same version of the CLI without us needing to so any ops at all to install stuff.


(and my OSS projects also tend that way for anything I might run in CI -- what I showed above was just a convenience script I run locally)


I use tasks more as a way to abstract myself from the CI intricacies too. More often than not i have to hop CIs and have a mix of CIs, hence its nice there but maybe it could me a "me problem" 😅


i do have a fairly complex flow with parallel things, error recovery, cleanup etc in tasks. feels nicer than the YAML for me


@U7ERLH6JX How do you setup bb for CI? (and which CI are you using)


mostly circle. its pretty much uses the linux install script:


Ah, OK, so it's installing bb every time for Circle (or, say, BitBucket).


if you have a container based executor, you can use the bb docker image we publish too


Good to know re: GH Actions. Thanks.


Also good to know re: Docker image -- does that also have a JDK etc? Or is it barebones with just bb?


we even implement bb's ci logic in bb itself , just saying if you wanna go down that rabbit hole : 😛


> Docker image -- does that also have a JDK etc? Or is it barebones with just bb Its just bb and the only dependency it optionally needs: curl


happy to answer any of bb and bb based ci/orchestration/build questions! most of it i was a part of building 😄


OK, so without Java, that means you can't run clojure in that Docker image:

seanc@Sean-win-11-laptop:~/clojure$ docker run --rm babashka/babashka bb clojure --version
Exception in thread "main" java.lang.Exception: Couldn't find 'java'. Please set JAVA_HOME.
        at borkdude.deps$_main.invokeStatic(deps.clj:439)
        at borkdude.deps$_main.doInvoke(deps.clj:428)
        at clojure.lang.RestFn.applyTo(
        at clojure.core$apply.invokeStatic(core.clj:667)
        at babashka.deps$clojure.invokeStatic(deps.clj:43)
        at babashka.deps$clojure.invoke(deps.clj:7)
        at babashka.deps$clojure.invokeStatic(deps.clj:31)
        at babashka.main$exec.invokeStatic(main.clj:954)
I wasn't sure how bb actually ran its clojure command 🙂


So if you want to use bb with Clojure stuff in a Docker image, you need to start with the clojure images and add bb at startup...


yeah that uses which would use the JVM clojure to fetch deps and build classpath. the idiom we generally do is in docker we do a multistage build: • get the deps in a java+bb image • use the next stage with a minimal bb image


once the stuff is there on the classpath bb wont need the JVM anymore


Ah, so you generate the classpath from the JDK image and store it (in a file?) and then use that in the bb image in the next stage?


(but you're still limited to whatever bb can actually run with, so you can't run arbitrary Clojure code)


you could copy over the ~/.m2 in the next stage. when loading the deps its the same maven like logic


Right... yeah, I'm used to BitBucket which caches ~/.m2 but only refreshes its caches once every two weeks unless you do it manually!


essentially the way we build bb, after the compilation, we dont have the JVM anymore, specially the JIT infra. so anything that produces bytecode and/or needs compilation to bytecode wont run in bb.


it is an interpreter after all.


Yeah, it's going to take me a bit to wrap my head around what I would actually use bb for... which is why it's taken me so long to actually install it...


At work, we have some shell scripts that may or may not make sense to switch to bb but the tradeoff is that we'd need bb on every server -- or vendored into our repo, like we do for the Clojure CLI -- and once we have the latter, it's somewhat hard to justify the former, just for "shell" scripts, esp. when startup time isn't really an issue for pretty much anything we run...


(and we already run a bunch of stuff in dev/test/CI via build.clj and the full`tools.deps.alpha` stack)


in general, if youre already using mostly jvm clojure and arent too sad about the startup time, keep using that. if theres an explosion of "glue" shell stuff and its too hard to understand/you wish you had clojure there, you know where to look 😄


My intention here is to try to "train" myself to turn to bb first and bash second, where I wouldn't instantly just stick something into my build.clj file 🙂

👍 1

yeah the "batteries included" nature of bb could be another motivation for you too:


I see there (which I maintain). I just cut a new minor release of that -- what version does bb use and how could I figure that out? How quickly does bb pick up new versions of those libraries?


picking up libs are fairly quick. can attribute to borkdude's inhuman efficiency 🙏:skin-tone-3:


Cool. So it's just a matter of getting all those updated before the next bb build/release? Oh... :rolling_on_the_floor_laughing:


yeah bb is produced like any other clojure project. has the jar and everything. just that things change after GraalVM eats that jar 😛


we could do that i guess! its mostly PR driven now i think


@U04V70XH6 Built-in libs like tools.cli can be overriden, if you add the lib to bb's classpath and use (:require [...] :reload)


$ bb -Sdeps '{:deps {org.clojure/tools.cli {:mvn/version "1.0.214"}}}' -e "(require '[ :as cli] :reload) (cli/summarize (cli/compile-option-specs [[\"-b\" \"--boolean\" \"Bla\" :default true]]))"
"  -b, --boolean  true  Bla"


Ah, cool. Good to know @U04V15CAJ Thanks!


We also have this page with supported projects:


With p/shell it is fine, but should not be necessary to do this: Since shell (unlike the other functions in process) already automatically checks its exit code and throws if it's non-zero


Awesome first script :)


Since you are using VScode, this might be interesting too:

❤️ 1

Welcome to the wonderful world of cross-platform scripting in Clojure @U04V70XH6! I knew you'd come around eventually!


Did anybody mention the It can also setup bb.


might i also add cross-platform and truly multithreaded scripting 😄

🚀 2

> Did anybody mention the setup-clojure GitHub Action yet? I mentioned the setup bb one, but yes this is more general and nicer!


My personal pref is to use bb tasks to abstract the tasks for a project. It's nice to go to one of my projects and type bb tasks . And then be able to quickly run a task with very few keystrokes. But I did start with bb scripts only first.


You can start out with just script/foo.clj and then hook up the script in bb.edn using load-file later


Yeah my scripts were easy to hook up to tasks.


@U04V15CAJ Ah, yes, I had been trying various combinations of things, including ProcessBuilder directly, before I ended up there so it's sort of leftover from earlier code. I started with, then went to p/process, then ProcessBuilder, then p/shell because it wasn't entirely clear from the book which would better for my use case (specifically: adding environment variables). Also, it took me a while to realize that :env replaces all the environment variables, which I'm pretty sure c.j.s/sh does not?


It does. Compare:

user=> (:out (sh "bash" "-c" "echo $PATH"))
user=> (:out (sh "bash" "-c" "echo $PATH" :env {"FOO" "bar"}))


Ah, interesting. I've never tripped over that before (but tripped over it straight away running tests) so I guess I've just been lucky in the past. Thank you!

👍 1