Stevedore is a DSL for generating shell scripts using clojure.
While there is nothing wrong with normal shell scripting, Stevedore allows the ability to easily interpolate clojure values. As a consequence, we gain the benefits of being able to flexibly parameterize scripts and tailor them to target specific operating systems and their versions.
Stevedore is distributed as a jar, and is available in the clojars repository.
Installation is with lein or your favourite maven repository aware build tool.
:dependencies [[com.palletops/stevedore "0.8.0-beta.7"]]
<dependencies>
<dependency>
<groupId>com.palletops</groupId>
<artifactId>stevedore</artifactId>
<version>0.8.0-beta.7</version>
</dependency>
<dependencies>
<repositories>
<repository>
<id>clojars</id>
<url>http://clojars.org/repo</url>
</repository>
</repositories>
Let's write a basic script; nothing fancy just a plain ole' thing you can easily write in your scripting language of choice. Very static.
(use 'pallet.stevedore)
(require 'pallet.stevedore.bash)
(print
(with-script-language :pallet.stevedore.bash/bash
(script
("ls" "/some/path")
(defvar x 1)
(println @x)
(defn foo [x] ("ls" @x))
("foo" 1)
(if (= a a)
(println "Reassuring")
(println "Ooops"))
(println "I am" @("whomai")))))
Outputs:
# form-init7685875326394262653.clj:2
ls /some/path
# form-init7685875326394262653.clj:3
x=1
# form-init7685875326394262653.clj:4
echo ${x}
foo() {
x=$1
# form-init7685875326394262653.clj:5
ls ${x}
}
# form-init7685875326394262653.clj:6
foo 1
# form-init7685875326394262653.clj:7
if [ "a" == "a" ]; then echo Reassuring;else echo Ooops;fi
# form-init7685875326394262653.clj:10
echo I am $(whomai)
Here is where we start seeing some power. Notice everything escaped with ~
gets evaluated as clojure code before becoming parts of arguments for script
.
(print
(with-script-language :pallet.stevedore.bash/bash
(let [path "/some/path"]
(script
("ls" ~path)
("ls" ~(.replace path "some" "other"))))))
Outputs:
# form-init7685875326394262653.clj:6
ls /some/path
# form-init7685875326394262653.clj:7
ls /other/path
That's cool, but here's one better. Let's do the same thing as the previous snippet but let's generate the script using a function that takes a path as an argument. This lets us generate a slightly different script with each different argument.
(defn list-path [path]
"Replaces the \"some\" portion of path argument with the \"other\" string."
(script
("ls" ~path)
("ls" ~(.replace path "some" "other"))))
(print (with-script-language :pallet.stevedore.bash/bash
(list-path "/some/path")))
Outputs:
# form-init7685875326394262653.clj:7
ls /some/path
# form-init7685875326394262653.clj:8
ls /other/path
(print (with-script-language :pallet.stevedore.bash/bash
(list-path "/some/different/path")))
Outputs:
# form-init7685875326394262653.clj:7
ls /some/different/path
# form-init7685875326394262653.clj:8
ls /other/different/path
Concatenate scripts together using do-script
.
(print (with-script-language :pallet.stevedore.bash/bash
(do-script
(list-path "/some/path")
(list-path "/some/different/path"))))
Outputs:
# form-init7685875326394262653.clj:5
ls /some/path
# form-init7685875326394262653.clj:6
ls /other/path
# form-init7685875326394262653.clj:5
ls /some/different/path
# form-init7685875326394262653.clj:6
ls /other/different/path
Chain scripts together with &&
using chained-script
.
(print (with-script-language :pallet.stevedore.bash/bash
(chained-script
(list-path "/some/path")
(list-path "/some/different/path"))))
Outputs:
# form-init7685875326394262653.clj:4
# form-init7685875326394262653.clj:5
ls /some/path
# form-init7685875326394262653.clj:6
ls /other/path && \
# form-init7685875326394262653.clj:5
# form-init7685875326394262653.clj:5
ls /some/different/path
# form-init7685875326394262653.clj:6
ls /other/different/path
Chain your scripts and exit if the chain fails using checked-script
.
(print (with-script-language :pallet.stevedore.bash/bash
(checked-script
(list-path "/some/path")
(list-path "/some/different/path"))))
Outputs:
echo ' # form-init7685875326394262653.clj:5
ls /some/path
# form-init7685875326394262653.clj:6
ls /other/path
...';
{
# form-init7685875326394262653.clj:5
# form-init7685875326394262653.clj:5
ls /some/different/path
# form-init7685875326394262653.clj:6
ls /other/different/path
} || { echo '#> # form-init7685875326394262653.clj:5
ls /some/path
# form-init7685875326394262653.clj:6
ls /other/path
: FAIL'; exit 1;} >&2
echo '#> # form-init7685875326394262653.clj:5
ls /some/path
# form-init7685875326394262653.clj:6
ls /other/path
: SUCCESS'
Licensed under EPL
Copyright 2010, 2011, 2012, 2013 Hugo Duncan.