This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-05-06
Channels
- # babashka (130)
- # beginners (97)
- # biff (36)
- # calva (6)
- # cherry (23)
- # clojure (29)
- # clojure-dev (1)
- # clojure-europe (9)
- # clojurescript (5)
- # datomic (24)
- # emacs (13)
- # fulcro (5)
- # hyperfiddle (33)
- # interop (2)
- # jobs (18)
- # kaocha (1)
- # london-clojurians (1)
- # lsp (20)
- # nrepl (1)
- # off-topic (60)
- # pathom (4)
- # reitit (7)
- # releases (1)
- # remote-jobs (4)
- # scittle (3)
- # specter (1)
- # tools-deps (7)
- # xtdb (16)
Are there any shell helpers, for example to check if a program exists in PATH? I want my build script to detect if podman or docker is in the PATH and use the right one.
(defn container-runtime-cmd []
(if (not (str/blank? (exec "which podman")))
"podman"
"docker"))
I'm not sure what exec
is here, but if this is from babashka.process
then this won't have the desired effect
I recommend the api docs for babashka.fs cover to cover, lots of goodies in there
I found a pattern I was quite happy with for supporting --dry-run
with babashka.cli
. It gives you behavior like this:
$ bb mycli.clj dostuff
total 12K
drwxr-xr-x 3 teodorlu teodorlu 4.0K 2023-05-06 13:16 .
drwxr-xr-x 163 teodorlu teodorlu 4.0K 2023-05-06 11:28 ..
drwxr-xr-x 2 teodorlu teodorlu 4.0K 2023-05-06 13:17 bbdispatch
$ bb mycli.clj dostuff --dry-run
(babashka.process/shell "ls -lah ..")
The trick is to dispatch the side effects, and let dispatch respect --dry-run
.
Lisp + shell + FP = β€οΈ!Right, agreed. Didn't think of that, thanks! Eval "smells like it's too wide" to me.
I guess this could be done in with-redefs as well?
Related to set -x maybe
I'd say with-redefs
should be limited to testing. It's not thread-safe and kind of meh...
Yeah, with-redefs
could do the trick. But then, one would have to remember to override every side-effecting function? I'd be a little cautious about overriding functions like spit
. I also kind of like that I have to call dispatch
explicitly for side effects. Perhaps it should be called run-effect!
instead.
Here's the real code I wrote: https://github.com/iterate/olorm/blob/2f30149a976859567ebade62bd471be865a17e91/cli/src/olorm/cli.clj#L51-L92
It's an internal (but public) tech microblog I'm running with a couple of coworkers, in Norwegian. Babashka made it so nice to use. $ olorm create
, type in $EDITOR, :wq
(or equivalent), and the new thing is published. I tried to make it minimally intrusive in an otherwise busy day.
Thanks for sharing! The context I'm thinking of is unit testing shell scripts. Maybe a wrapper similar to your dispatch could help
Oooh, right. I don't feel like I have a great solution for that yet. I tend to just write a dry-run option, and try it out.
Is this a proper way to "shell out" to another binary?
(shell
(apply str ["pdftk ",
(.getFileName pdf-file-path),
" dump_data | grep NumberOfPages"]
)
)
where pdf-file-path
is a java Path object like this:
pdf-file-path
=>
#object[sun.nio.fs.UnixPath
0xe7824bd
"/path/to/dir/week01.pdf"]
I have to serialize the Path to a string first, right?Also you can supply separate arguments to shell, no need to concatenate them as a single string
Hmm not sure what my error is:
(shell
(apply str ["pdftk ",
(str pdf-file-path),
" dump_data | grep NumberOfPages"]
)
)
Error: expecting "output" keyword. Instead, I got:
|
Errors encountered. No output created.
Done. Input errors, so no output created.
clojure.lang.ExceptionInfo: bookmark_files_combiner.core REPL:1:1
Seems like a similar issue to this: https://gitlab.com/pdftk-java/pdftk/-/issues/44
though I don't think I'm using the Java port of this program? I thought I was just using my system's pdftk (C or C++)
Also to re-iterate: https://clojurians.slack.com/archives/CLX41ASCS/p1683375531982329?thread_ts=1683375407.989049&channel=CLX41ASCS&message_ts=1683375531.982329
Canβt write any code in here on the phone but perhaps someone like @U06F82LES or @U3X7174KS can help you write it out
(shell {:out :str}
(apply str ["pdftk ",
(str pdf-file-path),
" dump_data"]
)
)
Like this?Also again you donβt need to do the apply str thing, just provide those directly :-)
This gives me an error...
(shell {:out :string}
["pdftk ",
(str pdf-file-path),
" dump_data"]
)
But more importantly, I'm not sure how to pipe this stuff to grep
, this is what I tried...
(shell "grep "
(shell {:out :string}
(apply str ["pdftk ",
(str pdf-file-path),
" dump_data"]
)
)
)
Iβll wait with another reply until Iβm home because typing code in the phone is frustrating me
You can however pass the string to grep if you need to using the :in argument, but itβs probably not hard to just process this manually in Clojure
Yay filtering worked π
(filter
(fn [s] (str/starts-with? s "PageMediaNumber:"))
(str/split
(:out (shell {:out :string}
"pdftk",
(str pdf-file-path),
"dump_data"
)
)
#"\n"
)
)
If my BB script creates some files in the process of its runtime, which aren't part of the script's goal, should I create these files as temporary files? with babashka.fs/create-temp-file
maybe? and if so, does this function return the path of the newly created temp file?
Yes, this returns the path of the temp file, but the temp file won't be automatically clean up, unless you reboot your computer (which cleans up the temp folder). If you want those temp files to be cleaned up on bb exit, you can do:
(let [temp-file (doto (fs/create-temp-file)
(fs/delete-on-exit))]
temp-file)
How can I receive arguments to my BB script from the user? say a relative path or an absolute path?
Hmm, that part of the book should actually point to: https://github.com/babashka/cli
Where should I look? suppose I want to receive arguments like this:
$ bb my-script.clj ./path/to/dir ./path/to/another/dir
btw, I've updated the docs here now: https://book.babashka.org/#_parsing_command_line_arguments
To parse two positional arguments, you don't need any CLI parsing library. You can simply do:
(let [[file1 file2] *command-line-args*]
...)
but if you need to parse additional options you can do this:
user=> (require '[babashka.cli :as cli])
nil
user=> (cli/parse-args ["file1" "file2" "--action" "delete"])
{:args ["file1" "file2"], :opts {:action "delete"}}
@borkdude from your last code snippet: Is this something that you would pass as the argument? is this an EDN notation?
{:args ["file1" "file2"], :opts {:action "delete"}}
I'm just gonna use the positional arguments option you showed earlier with command-line-args
I have another question on how to deal with the relative path string:
(let [[pdfs-dir-path-string
bookmarks-yaml-files-dir-path-strings
output-pdf-dir-path-string
] *command-line-args*]
(pprint pdfs-dir-path-string)
(pprint bookmarks-yaml-files-dir-path-strings)
(pprint output-pdf-dir-path-string)
)
All of these are just strings like so:
$ bb ./src/bookmark_files_combiner/core.clj resources/pdf-files/ resources/bookmark-files/ resources/output-boomark-file/
"resources/pdf-files/"
"resources/bookmark-files/"
"resources/output-boomark-file/"
My question is, how can I tell that this is a relative path and not an absolute path?Is it just about checking if the first character is /
and treating it as absolute path, otherwise treat it as relative path?
So if it's a relative path, I just convert it to an absolute path and go on about my business assuming it's all absolute paths after that?
Hey guys, so I just finished the script! Started working on it yesterday... Never used BB before, and only dabbled with Clojure a few times prior to this I had so much fun!! :star-struck: Shoutout to everyone in this amazing community that just keep providing their faithful assistance, without you this wouldn't have been possible! (I would've gotten stuck and quit) π Special shoutout to @borkdude π for being extremely quick, attentive and just so awesome to a total stranger like me on the internet π I uploaded my code to GitHub because I want Clojure to succeed and I can share stuff that I did with friends π https://github.com/amitnovick/bookmark-files-combiner
Nice!
If you want to be able to install a system script (so that you can run bookmark-files-combiner
from any folder), bbin might be able to help you do that!
More info on #babashka-bbin / https://github.com/babashka/bbin
And I think it's quite amazing that you're able to get this done without much prior clojure experience! π―
I tried to install babashka/bbin
on my Ubuntu 22.04 LTS with the Homebrew method but I got an error. It's probably something wrong with my disk space, though I'm not sure how to fix it.
$ brew install babashka/brew/bbin
==> Tapping babashka/brew
Cloning into '/home/linuxbrew/.linuxbrew/Homebrew/Library/Taps/babashka/homebrew-brew'...
remote: Enumerating objects: 399, done.
remote: Counting objects: 100% (143/143), done.
remote: Compressing objects: 100% (78/78), done.
remote: Total 399 (delta 97), reused 103 (delta 65), pack-reused 256
Receiving objects: 100% (399/399), 59.46 KiB | 801.00 KiB/s, done.
Resolving deltas: 100% (209/209), done.
Tapped 64 formulae (79 files, 172.6KB).
==> Tapping borkdude/brew
Cloning into '/home/linuxbrew/.linuxbrew/Homebrew/Library/Taps/borkdude/homebrew-brew'...
remote: Enumerating objects: 1114, done.
remote: Counting objects: 100% (231/231), done.
remote: Compressing objects: 100% (94/94), done.
remote: Total 1114 (delta 157), reused 210 (delta 137), pack-reused 883
Receiving objects: 100% (1114/1114), 138.50 KiB | 984.00 KiB/s, done.
Resolving deltas: 100% (737/737), done.
Tapped 9 formulae (39 files, 251.4KB).
==> Fetching dependencies for babashka/brew/bbin: borkdude/brew/babashka
==> Fetching borkdude/brew/babashka
==> Downloading
==> Downloading from
############################################################ 100.0%
==> Fetching babashka/brew/bbin
==> Downloading
==> Downloading from
#-#O#- #
==> Installing bbin from babashka/brew
==> Installing dependencies for babashka/brew/bbin: borkdude/brew/babashka
==> Installing babashka/brew/bbin dependency: borkdude/brew/ba
Error: An exception occurred within a child process:
Errno::ENOSPC: No space left on device - sendfile
In general my machine has free 40GB of disk space available, but maybe for some partition I need to allocate more, dunno
It's also possible to install bbin manually. It's just a babashka script! That's what I do on my Linux system, I don't usually install things with homebrew.
Here's the manual installation instructions: https://github.com/babashka/bbin/blob/main/docs/installation.md#manual-linux-and-macos
I got my script on a file path bookmark-files-combiner/core.clj
Wonder how I can pass that on to bbin
so that the global available name for it is bookmark-files-combiner` and not
core.clj
It is a bit worrying that anovick got that obscure error message though when installing bbin via brew
@UDHL22ZFE you can use bbin install <script> --as bookmark-files-combiner
I just run
$ bookmark-files-combiner pdf-files/ bookmark-files/ output-boomark-file/
and it just works! no need to call bb
no need to navigate to the script... it's so much easier β€οΈ@UDHL22ZFE If you want feedback on your clojure coding, you could ask in #C053PTJE6 too. E.g. I noticed you're using:
(defn foo [x]
(def y 1) ;; inline def
(+ x y))
which we usually write as:
(defn foo [x]
(let [y 1]
(+ x y))
Notice def
vs let
def
always introduces a global variable, even if you create it in the middle of the function, vs let
always creates local (immutable) bindingsIf you set up a linter like #CHY97NXE2 it will also tell you this. The linter can be integrated with your editor
Seems like a very useful tool to have... indeed I do not write idiomatic Clojure and that could help me out...
But then I'd have to install some Cursive plugin and install the clj-kondo
tool and make sure that it works... and it's just a bit of a headache for me... I hate spending time on environment setup
If I were to really be doing Clojure all day I would've considered that but right now I'm a student and don't have time to spend on this unfortunately... maybe in the future π
but there's no warnings that weren't there before by just the Cursive static analysis
that's what it gave me:
Error trying to annotate file
java.lang.Throwable: Control-flow exceptions (like ProcessCanceledException) should never be logged: ignore for explicitly started processes or rethrow to handle on the outer process level
at com.intellij.openapi.diagnostic.Logger.ensureNotControlFlow(Logger.java:264)
at com.intellij.idea.IdeaLogger.doLogError(IdeaLogger.java:151)
at com.intellij.idea.IdeaLogger.error(IdeaLogger.java:142)
at com.intellij.openapi.diagnostic.Logger.error(Logger.java:206)
at com.github.brcosta.cljstuffplugin.extensions.CljKondoAnnotator.lintWithBuiltinLinter(CljKondoAnnotator.kt:116)
at com.github.brcosta.cljstuffplugin.extensions.CljKondoAnnotator.doAnnotate(CljKondoAnnotator.kt:61)
at com.github.brcosta.cljstuffplugin.extensions.CljKondoAnnotator.doAnnotate(CljKondoAnnotator.kt:31)
at com.intellij.codeInsight.daemon.impl.ExternalToolPass.doAnnotate(ExternalToolPass.java:220)
at com.intellij.codeInsight.daemon.impl.ExternalToolPass.doAnnotate(ExternalToolPass.java:214)
at com.intellij.codeInsight.daemon.impl.ExternalToolPass$1.lambda$run$0(ExternalToolPass.java:192)
at com.intellij.codeInsight.daemon.impl.ExternalToolPass.runChangeAware(ExternalToolPass.java:289)
at com.intellij.codeInsight.daemon.impl.ExternalToolPass$1.lambda$run$2(ExternalToolPass.java:192)
at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$runProcess$2(CoreProgressManager.java:188)
at com.intellij.openapi.progress.impl.CoreProgressManager.lambda$executeProcessUnderProgress$12(CoreProgressManager.java:608)
at com.intellij.openapi.progress.impl.CoreProgressManager.registerIndicatorAndRun(CoreProgressManager.java:683)
at com.intellij.openapi.progress.impl.CoreProgressManager.computeUnderProgress(CoreProgressManager.java:639)
at com.intellij.openapi.progress.impl.CoreProgressManager.executeProcessUnderProgress(CoreProgressManager.java:607)
at com.intellij.openapi.progress.impl.ProgressManagerImpl.executeProcessUnderProgress(ProgressManagerImpl.java:60)
at com.intellij.openapi.progress.impl.CoreProgressManager.runProcess(CoreProgressManager.java:175)
at com.intellij.openapi.progress.util.BackgroundTaskUtil.runUnderDisposeAwareIndicator(BackgroundTaskUtil.java:365)
at com.intellij.openapi.progress.util.BackgroundTaskUtil.runUnderDisposeAwareIndicator(BackgroundTaskUtil.java:343)
at com.intellij.codeInsight.daemon.impl.ExternalToolPass$1.run(ExternalToolPass.java:191)
at com.intellij.util.ui.update.MergingUpdateQueue.execute(MergingUpdateQueue.java:332)
at com.intellij.util.ui.update.MergingUpdateQueue.execute(MergingUpdateQueue.java:322)
at com.intellij.util.ui.update.MergingUpdateQueue.lambda$flush$1(MergingUpdateQueue.java:271)
at com.intellij.util.ui.update.MergingUpdateQueue.flush(MergingUpdateQueue.java:285)
at com.intellij.util.ui.update.MergingUpdateQueue.run(MergingUpdateQueue.java:240)
at com.intellij.util.concurrency.QueueProcessor.runSafely(QueueProcessor.java:241)
at com.intellij.util.Alarm$Request.runSafely(Alarm.java:388)
at com.intellij.util.Alarm$Request.run(Alarm.java:377)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at com.intellij.util.concurrency.SchedulingWrapper$MyScheduledFutureTask.run(SchedulingWrapper.java:223)
at com.intellij.util.concurrency.BoundedTaskExecutor.doRun(BoundedTaskExecutor.java:241)
at com.intellij.util.concurrency.BoundedTaskExecutor.access$200(BoundedTaskExecutor.java:31)
at com.intellij.util.concurrency.BoundedTaskExecutor$1.execute(BoundedTaskExecutor.java:214)
at com.intellij.util.ConcurrencyUtil.runUnderThreadName(ConcurrencyUtil.java:212)
at com.intellij.util.concurrency.BoundedTaskExecutor$1.run(BoundedTaskExecutor.java:203)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:702)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1$1.run(Executors.java:699)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.util.concurrent.Executors$PrivilegedThreadFactory$1.run(Executors.java:699)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: com.intellij.openapi.progress.ProcessCanceledException
at com.intellij.openapi.application.impl.ReadMostlyRWLock.startRead(ReadMostlyRWLock.java:108)
at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:939)
at com.intellij.openapi.application.ReadAction.compute(ReadAction.java:68)
at com.intellij.openapi.editor.impl.DocumentImpl.getText(DocumentImpl.java:934)
at com.github.brcosta.cljstuffplugin.extensions.CljKondoAnnotator.getPsiFileContent(CljKondoAnnotator.kt:125)
at com.github.brcosta.cljstuffplugin.extensions.CljKondoAnnotator.getTempLintFile(CljKondoAnnotator.kt:131)
at com.github.brcosta.cljstuffplugin.extensions.CljKondoAnnotator.lintWithBuiltinLinter(CljKondoAnnotator.kt:102)
... 40 more