Fork me on GitHub
#babashka
<
2022-08-12
>
teodorlu12:08:23

Hi :) I want: 1. To write a CLI tool with babashka 2. To have it on PATH so that I can run it from any folder on my system 3. To declare dependencies only in bb.edn, and avoid needing a "generate script" step (like in Neil) 4. To be able to split my script into multiple files / namespaces, like a normal Clojure project Currently, the only option I see is to use a shell wrapper. Something like:

#!/usr/bin/env bash

SRC_PATH="/home/teodorlu/dev/ORG/MYTOOL/SRC"
exec bb --classpath $SRC_PATH -m MYTOOL "$@"
It seems to run my script. But I'm linking straight into MYTOOL/SRC, so I don't think I'm picking up dependencies. Is there an endorsed way to do this?

teodorlu12:08:34

Hmm, I'm wondering whether just using babashka itself for the wrapper would be a solution. Something like:

(require '[babashka.deps :as deps])
(deps/add-deps '{:deps {mytool {:local/root "/home/teodorlu/dev/ORG/MYTOOL/SRC"}}})

(require 'mytool)
(apply mytool/main *command-line-args*)

borkdude12:08:05

The endorsed way to pick up deps from bb.edn is to use --config

👀 1
teodorlu12:08:00

Right! That looks like exactly what I need. Not sure why I missed that. Trying --config. Thanks!

borkdude12:08:05

so your shell wrapper would look like this:

SRC_PATH="/home/teodorlu/dev/ORG/MYTOOL"
exec bb --config $SRC_PATH -m MYTOOL "$@"

💯 1
1
borkdude12:08:19

This is also what I'm considering for bb install, that it would generate a shell wrapper like that

💯 2
borkdude12:08:29

for both bash and windows .bat

👍 2
teodorlu12:08:06

That would be great 🙂 Feels like I'm writing platform specific boilerplate right now.

borkdude12:08:14

you could make a bb script/tool which generates that boilerplate, e.g. a bb install prototype in userspace

borkdude12:08:30

would be good to experiment with that to see if it works and to inform what bb install will look like

teodorlu12:08:59

Good point. Where bb-install-prototype.clj itself can be a standalone BB script.

teodorlu12:08:12

I tested --config - was exactly what I was looking for :thumbsup:

borkdude12:08:56

nice, so maybe bb install should just clone a repository

borkdude12:08:58

and then use this

teodorlu13:08:15

That cold work!

teodorlu13:08:52

bb install prototype, local only, --dry-run only:

$ bb -m bb-install bb.edn --main ucad --install-dir ~/.local/bin --dry-run
#!/usr/bin/env bash

# This script was generated by `bb install`.
# TODO link to `bb install` docs

exec bb --config /home/teodorlu/dev/iterate/squirreltime/ucad/bb.edn -m ucad "$@"
Source (WIP):
(ns bb-install
  (:require [babashka.cli :as cli]
            [babashka.fs :as fs]
            [clojure.string :as str]))

;; Example usage:
;;
;;     bb-install BB_EDN_PATH --main MAIN --wrapper-script-type bash --dir ~/.local/bin

(defn- lines [& ls]
  (str/join "\n" (map str ls)))

(defn shell-escape-string [s]
  ;; TODO, not sure how
  ;; Otherwise, we'll fail on spaces in file names
  s)

(defn run [opts]
  (let [opts (merge {:wrapper-script-type :bash                    ; :wrapper-script-type should default to batch on Windows
                     :install-dir (fs/expand-home "~/.local/bin")}
                    opts)
        {:keys [dry-run main wrapper-script-type install-dir bb-edn-path]} opts]
    (assert dry-run "Only dry-run is supported for now")
    (assert main)
    (assert wrapper-script-type)
    (assert (#{:bash} wrapper-script-type) "Only bash wrappers are supported for now")
    (assert install-dir)
    (assert bb-edn-path)

    ;; without dry-run, should write to a file.
    (println (lines
              "#!/usr/bin/env bash"
              ""
              "# This script was generated by `bb install`."
              "# TODO link to `bb install` docs"
              ""
              (str "exec bb --config " (shell-escape-string (fs/absolutize bb-edn-path)) " -m " main " \"$@\"")))))

(defn -main [& args]
  (let [opts (cli/parse-opts args
                             {:alias {:m :main}
                              :coerce {:main :string
                                       :wrapper-script-type :keyword
                                       :dry-run :boolean
                                       :install-dir :string}
                              :args->opts [:bb-edn-path]})]
    (run opts)))

teodorlu13:08:34

please disregard the squirrels in the path 😄

borkdude13:08:52

maybe it's better if you pushed this to a repository :)

teodorlu13:08:41

what? git? Not code via slack? 😄

borkdude13:08:43

Nice. And now maybe add a shebang do you can download it to your path ;)

borkdude13:08:52

and then your can run $ bb-install :)

😄 1
teodorlu13:08:25

It's so nice to be able to use Clojure for this kind of work

teodorlu13:08:25

Did some more polish, added install instructions: https://play.teod.eu/bb-install/ No helptext, and few guardrails. But works for installing a local bb script.

borkdude13:08:09

So one of the ideas in bb install is that you can install from a url, not from a local project

borkdude13:08:19

e.g. you could install neil that way

teodorlu13:08:02

Yup, I haven't given that any thought. Requires a caching / versioning strategy.

borkdude13:08:20

the caching can be taken care of by the clojure CLI

1
borkdude13:08:28

just use add-deps with the git repo

👍 1
teodorlu13:08:30

Did you have a place on github where you wanted to collect ideas for bb install?

borkdude13:08:44

yes, you already contributed to that discussion?

😅 1
teodorlu14:08:01

I had completely compartmentalized "install locally" vs "install remotely". Thanks for the link!

borkdude14:08:46

Yeah I think both should be supported. We could support a local install via :local/root and the remote one via :git/url right

👍 1