Bass

Bass is a scripting language for running commands and caching the shit out of them.

Bass's goal is to make shipping software predictable, repeatable, and fun. The plan is to support sophisticated CI/CD flows while sticking to familiar ideas. CI/CD boils down to running commands. Bass leverages that instead of trying to replace it.

If you'd like to try it out, grab the latest release and skim the guide!

demo thunks & thunk paths
𝄢

Commands are represented as a data value called a thunk. Thunks are rendered as space invaders.

(from (linux/alpine)
  ($ echo "Hello, world!"))
echo "Hello, world!"
{
:image
{
:image
{
:repository "alpine"
:platform
{
:architecture "amd64"
:os "linux"
}
:tag "latest"
:digest "sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659"
}
}
:args
(
  1. "echo"
  2. "Hello, world!"
)
}
𝄢

You can run a thunk, read its output, or check if it succeeds?.

(def thunk
  (from (linux/alpine)
    ($ echo "Hello, world!")))

[(run thunk) (next (read thunk :raw)) (succeeds? thunk)]
stderr: 16 lines
─╮
run {{thunk SOOINRF4LGA54: echo "Hello, world!"}}
[0.00s] resolve image config for docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.00s] docker-image://docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.00s] resolve docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
─╮ docker-image://docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
◀┤ [0.15s] echo "Hello, world!"
Hello, world!
─╮
read {{thunk SOOINRF4LGA54: echo "Hello, world!"}}
╰▶ [0.12s] echo "Hello, world!"
Hello, world!
[0.13s] echo "Hello, world!"
Hello, world!
(
  1. null
  2. "Hello, world!\n"
  3. true
)
𝄢

Files created by a thunk can be referenced as thunk paths.

(def create-file
  (from (linux/alpine)
    ($ sh -c "echo hello >> file")))

create-file/file
sh -c "echo hello >> file"
{
:image
{
:image
{
:repository "alpine"
:platform
{
:architecture "amd64"
:os "linux"
}
:tag "latest"
:digest "sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659"
}
}
:args
(
  1. "sh"
  2. "-c"
  3. "echo hello >> file"
)
}
./file
𝄢

Thunk paths can be passed to other thunks.

(from (linux/alpine)
  ($ cat create-file/file))
cat {{thunk 31657P3G9AQBE: sh -c "echo hello >> file"}}/file
{
:image
{
:image
{
:repository "alpine"
:platform
{
:architecture "amd64"
:os "linux"
}
:tag "latest"
:digest "sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659"
}
}
:args
(
  1. "cat"
  2. sh -c "echo hello >> file"
    {
    :image
    {
    :image
    {
    :repository "alpine"
    :platform
    {
    :architecture "amd64"
    :os "linux"
    }
    :tag "latest"
    :digest "sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659"
    }
    }
    :args
    (
    1. "sh"
    2. "-c"
    3. "echo hello >> file"
    )
    }
    ./file
)
}
𝄢

Like thunks, thunk paths are just data values. The underlying thunk only runs when another thunk that needs it runs, or when you read the path itself.

(-> (from (linux/alpine)
      ($ cat create-file/file))
    (read :raw)
    next)
stderr: 9 lines
─╮
read {{thunk FE7KMSFVGM5A4: cat {{thunk 31657P3G9AQBE: sh -c "echo hello >> file"}}/file}}
[0.00s] resolve image config for docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.01s] docker-image://docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.01s] resolve docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.12s] sh -c "echo hello >> file"
[0.12s] cat {{thunk 31657P3G9AQBE: sh -c "echo hello >> file"}}/file
hello
"hello\n"
demo fetching git repos & other inputs
𝄢

To fetch source code from a git repo you should probably use the .git module.

(use (.git (linux/alpine/git)))

(let [url "https://github.com/vito/bass"
      ref "main"]
  (git:checkout url (git:ls-remote url ref)))
stderr: 8 lines
─╮
read {{thunk B98MA4D49SOVQ: git ls-remote https://github.com/vito/bass main}}
[0.01s] resolve image config for docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] resolve docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.30s] git ls-remote https://github.com/vito/bass main
d32b936ceed8a32b5ef455ec70263d293ff6f1bf refs/heads/main
git submodule update --init --recursive
{
:image
git checkout d32b936ceed8a32b5ef455ec70263d293ff6f1bf
{
:image
git fetch origin d32b936ceed8a32b5ef455ec70263d293ff6f1bf
{
:image
git clone https://github.com/vito/bass ./
{
:image
{
:image
{
:repository "alpine/git"
:platform
{
:architecture "amd64"
:os "linux"
}
:tag "latest"
:digest "sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3"
}
}
:args
(
  1. "git"
  2. "clone"
  3. "https://github.com/vito/bass"
  4. ./
)
}
:args
(
  1. "git"
  2. "fetch"
  3. "origin"
  4. "d32b936ceed8a32b5ef455ec70263d293ff6f1bf"
)
}
:args
(
  1. "git"
  2. "checkout"
  3. "d32b936ceed8a32b5ef455ec70263d293ff6f1bf"
)
}
:args
(
  1. "git"
  2. "submodule"
  3. "update"
  4. "--init"
  5. "--recursive"
)
}
./
𝄢

Using ls-remote to resolve main to a commit ensures the checkout call is hermetic.

A non-hermetic thunk looks like this:

; BAD
(from (linux/alpine/git)
  ($ git clone "https://github.com/vito/bass" ./))
git clone https://github.com/vito/bass ./
{
:image
{
:image
{
:repository "alpine/git"
:platform
{
:architecture "amd64"
:os "linux"
}
:tag "latest"
:digest "sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3"
}
}
:args
(
  1. "git"
  2. "clone"
  3. "https://github.com/vito/bass"
  4. ./
)
}
𝄢

If you run this thunk somewhere else it might return something different. It'll also be cached forever, so you'll never get new commits.

Each input should specify an exact version to fetch. If you don't know it yet you can run another thunk to figure it out. You can keep that thunk from being cached forever by labeling it with the current time. That's how ls-remote works under the hood.

(defn ls-remote [repo ref & timestamp]
  (-> ($ git ls-remote $repo $ref)
      (with-image *git-image*)
      (with-env {:MINUTE (now 60)}) ; rerun every minute
      (read :unix-table) ; line and space separated table output
      next    ; first row   : <ref> <sha>
      first)) ; first column: <ref>
ls-remote
demo running tests
𝄢

To run tests, just run whatever command you would usually use to run tests.

(use (.git (linux/alpine/git)))

(defn go-test [src & args]
  (from (linux/golang)
    (cd src
      ($ go test & $args))))

(let [src git:github/vito/booklit/ref/master/]
  (succeeds? (go-test src ./tests/)))
stderr: 70 lines
─╮
read {{thunk CSUG1GJD7EN1I: git ls-remote https://github.com/vito/booklit master}}
[0.00s] resolve image config for docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.02s] docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.02s] resolve docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
─╮ docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
◀┤ [0.29s] git ls-remote https://github.com/vito/booklit master
2372cdeb7c66fe38fe4a7a833e941a1672d51194 refs/heads/master
─╮
run {{thunk 0A44IAH6A77RS: go test ./tests/}}
[0.41s] resolve image config for docker.io/library/golang@sha256:c7e98cc0fd4dfb71ee7465fee6c9a5f079163307e4bf141b336bb9dae00159a5
[0.27s] docker-image://docker.io/library/golang@sha256:c7e98cc0fd4dfb71ee7465fee6c9a5f079163307e4bf141b336bb9dae00159a5
[0.02s] resolve docker.io/library/golang@sha256:c7e98cc0fd4dfb71ee7465fee6c9a5f079163307e4bf141b336bb9dae00159a5
[0.08s] sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1
[0.08s] sha256:0300fff257029f01f778ded3bcfda47aa93ac410807fd2bd1c948a796af2b3ff
[2.14s] sha256:6fbff1d4eb7eece408734c05c8c63a49bb181871bc1280cff3f0e28d25a4ea28
[2.93s] sha256:2c117e525b33f7deb18e30423eb787ad9809038bb9eec33593f3ddff2c49a8de
[2.16s] sha256:9da421ddeb655bdfb3960e490b39373b0d1351e3eaba61d01978107920638392
[1.31s] sha256:ed881fbf1b07b42dd470cd5b56a8feb684d60879c6f8028a9e7a8715e0e72361
[1.25s] sha256:866771c43bf5eb77362eeeb163c0c825e194c2806d0b697028434e3b9c02f59d
[0.49s] extracting sha256:866771c43bf5eb77362eeeb163c0c825e194c2806d0b697028434e3b9c02f59d
[0.25s] extracting sha256:ed881fbf1b07b42dd470cd5b56a8feb684d60879c6f8028a9e7a8715e0e72361
[0.59s] extracting sha256:9da421ddeb655bdfb3960e490b39373b0d1351e3eaba61d01978107920638392
[0.75s] extracting sha256:2c117e525b33f7deb18e30423eb787ad9809038bb9eec33593f3ddff2c49a8de
[1.13s] extracting sha256:6fbff1d4eb7eece408734c05c8c63a49bb181871bc1280cff3f0e28d25a4ea28
[0.17s] extracting sha256:0300fff257029f01f778ded3bcfda47aa93ac410807fd2bd1c948a796af2b3ff
[0.27s] extracting sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1
◀╯ [6.65s] git clone https://github.com/vito/booklit ./
Cloning into '.'...
[0.47s] git fetch origin 2372cdeb7c66fe38fe4a7a833e941a1672d51194
From https://github.com/vito/booklit
* branch 2372cdeb7c66fe38fe4a7a833e941a1672d51194 -> FETCH_HEAD
[0.21s] git checkout 2372cdeb7c66fe38fe4a7a833e941a1672d51194
Note: switching to '2372cdeb7c66fe38fe4a7a833e941a1672d51194'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 2372cde disable jekyll
[0.18s] git submodule update --init --recursive
[6.36s] go test ./tests/
go: downloading github.com/onsi/gomega v1.27.4
go: downloading github.com/onsi/ginkgo/v2 v2.9.1
go: downloading github.com/sirupsen/logrus v1.9.3
go: downloading github.com/alecthomas/chroma/v2 v2.23.1
go: downloading golang.org/x/text v0.14.0
go: downloading github.com/agext/levenshtein v1.2.3
go: downloading github.com/yuin/goldmark v1.7.8
go: downloading github.com/segmentio/textio v1.2.0
go: downloading github.com/dlclark/regexp2 v1.11.5
go: downloading golang.org/x/sys v0.18.0
go: downloading github.com/google/go-cmp v0.5.9
go: downloading golang.org/x/net v0.23.0
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading github.com/go-logr/logr v1.2.4
ok github.com/vito/booklit/tests 0.093s
true
𝄢

Don't use Go? Use a different image and run a different command:

(defn cargo-test [src & args]
  (from (linux/rust)
    (cd src
      ($ cargo test & $args))))

(let [src git:github/alacritty/alacritty/ref/master/]
  (succeeds? (cargo-test src ./alacritty_terminal/)))
stderr: 395 lines
─╮
read {{thunk 3L6C908A012M8: git ls-remote https://github.com/alacritty/alacritty master}}
[0.00s] resolve image config for docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] resolve docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
─╮ docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
◀┤ [0.39s] git ls-remote https://github.com/alacritty/alacritty master
96f488dd3fb08e636cb6a01818891ae2044184be refs/heads/master
─╮
resolve {{thunk LL4CGDK5R2CHM: }}
[0.43s] resolve image config for docker.io/library/rust:latest
─╮
run {{thunk RE8HHNCOL70ME: cargo test ./alacritty_terminal/}}
[0.00s] resolve image config for docker.io/library/rust@sha256:0e6da0c8f06f25e9591f21c0f741cd4ff1086e271c3330f29f6e4e95869c7843
[1.49s] docker-image://docker.io/library/rust@sha256:0e6da0c8f06f25e9591f21c0f741cd4ff1086e271c3330f29f6e4e95869c7843
[0.01s] resolve docker.io/library/rust@sha256:0e6da0c8f06f25e9591f21c0f741cd4ff1086e271c3330f29f6e4e95869c7843
[3.96s] sha256:18cc029224c82ccaea9575c04af1fb20fbbdc49f24783076b8dec5a3b890a5de
[4.27s] sha256:a793e3c6bce826c77a6bfec52e3e42691937f0e5701d8efa06d32850314d3f30
[3.32s] extracting sha256:a793e3c6bce826c77a6bfec52e3e42691937f0e5701d8efa06d32850314d3f30
[1.49s] extracting sha256:18cc029224c82ccaea9575c04af1fb20fbbdc49f24783076b8dec5a3b890a5de
◀╯ [4.53s] git clone https://github.com/alacritty/alacritty ./
Cloning into '.'...
[0.61s] git fetch origin 96f488dd3fb08e636cb6a01818891ae2044184be
From https://github.com/alacritty/alacritty
* branch 96f488dd3fb08e636cb6a01818891ae2044184be -> FETCH_HEAD
[2.54s] git checkout 96f488dd3fb08e636cb6a01818891ae2044184be
Note: switching to '96f488dd3fb08e636cb6a01818891ae2044184be'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 96f488dd Fix resource cleanup for signal shutdown
[0.17s] git submodule update --init --recursive
[20.94s] cargo test ./alacritty_terminal/
Updating crates.io index
Downloading crates ...
Downloaded smallvec v1.15.1
Downloaded smol_str v0.2.2
Downloaded shlex v1.3.0
Downloaded signal-hook v0.4.3
Downloaded slab v0.4.11
Downloaded arrayvec v0.7.6
Downloaded smithay-client-toolkit v0.19.2
Downloaded parking_lot v0.12.5
Downloaded clap_builder v4.5.48
Downloaded tracing-core v0.1.34
Downloaded tiny-skia-path v0.11.4
Downloaded toml v0.9.11+spec-1.1.0
Downloaded strsim v0.11.1
Downloaded wayland-csd-frame v0.3.0
Downloaded strict-num v0.1.1
Downloaded cfg-if v1.0.3
Downloaded aho-corasick v1.1.3
Downloaded wayland-scanner v0.31.7
Downloaded walkdir v2.5.0
Downloaded unicode-ident v1.0.19
Downloaded thiserror-impl v1.0.69
Downloaded tempfile v3.23.0
Downloaded errno v0.3.14
Downloaded foreign-types-macros v0.2.3
Downloaded gethostname v1.0.2
Downloaded heck v0.5.0
Downloaded fdeflate v0.3.7
Downloaded glutin_glx_sys v0.6.1
Downloaded inotify-sys v0.1.5
Downloaded dpi v0.1.2
Downloaded libloading v0.8.9
Downloaded is_terminal_polyfill v1.70.1
Downloaded percent-encoding v2.3.2
Downloaded equivalent v1.0.2
Downloaded find-msvc-tools v0.1.3
Downloaded same-file v1.0.6
Downloaded pin-project-lite v0.2.16
Downloaded serde_spanned v1.0.4
Downloaded downcast-rs v1.2.1
Downloaded copypasta v0.10.2
Downloaded glutin v0.32.3
Downloaded sctk-adwaita v0.10.1
Downloaded scopeguard v1.2.0
Downloaded log v0.4.28
Downloaded serde_yaml v0.9.34+deprecated
Downloaded inotify v0.11.0
Downloaded bytemuck v1.24.0
Downloaded ryu v1.0.20
Downloaded crossfont v0.8.1
Downloaded memmap2 v0.9.8
Downloaded clap_derive v4.5.47
Downloaded proc-macro2 v1.0.101
Downloaded serde_derive v1.0.228
Downloaded x11-dl v2.21.0
Downloaded freetype-rs v0.36.0
Downloaded xkbcommon-dl v0.4.2
Downloaded x11-clipboard v0.9.3
Downloaded hashbrown v0.16.0
Downloaded xcursor v0.3.10
Downloaded xdg v3.0.0
Downloaded yeslogic-fontconfig-sys v5.0.0
Downloaded tracing v0.1.41
Downloaded wayland-protocols-wlr v0.3.9
Downloaded toml_writer v1.0.6+spec-1.1.0
Downloaded toml_datetime v0.7.5+spec-1.1.0
Downloaded wayland-sys v0.31.7
Downloaded serde_json v1.0.145
Downloaded xml-rs v0.8.27
Downloaded wayland-protocols v0.32.9
Downloaded wayland-protocols-plasma v0.3.9
Downloaded toml_edit v0.24.0+spec-1.1.0
Downloaded xkeysym v0.2.1
Downloaded wayland-backend v0.3.11
Downloaded unsafe-libyaml v0.2.11
Downloaded tiny-skia v0.11.4
Downloaded winnow v0.7.13
Downloaded toml_parser v1.0.6+spec-1.1.0
Downloaded x11rb v0.13.2
Downloaded unicode-width v0.2.2
Downloaded rustix v0.38.44
Downloaded zerocopy v0.8.27
Downloaded syn v2.0.106
Downloaded rustix v1.1.2
Downloaded regex-syntax v0.8.6
Downloaded vte v0.15.0
Downloaded quick-xml v0.37.5
Downloaded wayland-cursor v0.31.11
Downloaded serde v1.0.228
Downloaded notify v8.2.0
Downloaded khronos_api v3.1.0
Downloaded indexmap v2.11.4
Downloaded x11rb-protocol v0.13.2
Downloaded regex-automata v0.4.11
Downloaded getrandom v0.3.3
Downloaded cc v1.2.40
Downloaded png v0.17.16
Downloaded mio v1.0.4
Downloaded winit v0.30.13
Downloaded bitflags v2.9.4
Downloaded memchr v2.7.6
Downloaded clap v4.5.48
Downloaded polling v3.11.0
Downloaded parking_lot_core v0.9.12
Downloaded libc v0.2.176
Downloaded crc32fast v1.5.0
Downloaded serde_core v1.0.228
Downloaded pkg-config v0.3.32
Downloaded as-raw-xcb-connection v1.0.1
Downloaded once_cell v1.21.3
Downloaded lock_api v0.4.14
Downloaded glutin_egl_sys v0.7.1
Downloaded foreign-types-shared v0.3.1
Downloaded colorchoice v1.0.4
Downloaded quote v1.0.41
Downloaded notify-types v2.0.0
Downloaded miniz_oxide v0.8.9
Downloaded home v0.5.11
Downloaded cursor-icon v1.2.0
Downloaded cstr v0.2.12
Downloaded cfg_aliases v0.2.1
Downloaded scoped-tls v1.0.1
Downloaded raw-window-handle v0.6.2
Downloaded itoa v1.0.15
Downloaded foreign-types v0.5.0
Downloaded dlib v0.5.2
Downloaded jobserver v0.1.34
Downloaded clap_lex v0.7.5
Downloaded gl_generator v0.14.0
Downloaded fastrand v2.3.0
Downloaded thiserror v1.0.69
Downloaded wayland-client v0.31.11
Downloaded version_check v0.9.5
Downloaded utf8parse v0.2.2
Downloaded calloop v0.13.0
Downloaded base64 v0.22.1
Downloaded flate2 v1.1.4
Downloaded clap_complete v4.5.58
Downloaded bitflags v1.3.2
Downloaded ahash v0.8.12
Downloaded adler2 v2.0.1
Downloaded anstyle-parse v0.2.7
Downloaded anstream v0.6.21
Downloaded anstyle v1.0.13
Downloaded rustix-openpty v0.2.0
Downloaded calloop-wayland-source v0.3.0
Downloaded smithay-clipboard v0.7.2
Downloaded simd-adler32 v0.3.7
Downloaded signal-hook-registry v1.4.6
Downloaded arrayref v0.3.9
Downloaded anstyle-query v1.1.4
Downloaded linux-raw-sys v0.11.0
Downloaded linux-raw-sys v0.4.15
Downloaded freetype-sys v0.20.1
Compiling proc-macro2 v1.0.101
Compiling unicode-ident v1.0.19
Compiling quote v1.0.41
Compiling serde_core v1.0.228
Compiling serde v1.0.228
Compiling cfg-if v1.0.3
Compiling pkg-config v0.3.32
Compiling libc v0.2.176
Compiling rustix v1.1.2
Compiling once_cell v1.21.3
Compiling linux-raw-sys v0.11.0
Compiling shlex v1.3.0
Compiling find-msvc-tools v0.1.3
Compiling smallvec v1.15.1
Compiling downcast-rs v1.2.1
Compiling scoped-tls v1.0.1
Compiling memchr v2.7.6
Compiling khronos_api v3.1.0
Compiling wayland-client v0.31.11
Compiling xml-rs v0.8.27
Compiling rustix v0.38.44
Compiling thiserror v1.0.69
Compiling linux-raw-sys v0.4.15
Compiling slab v0.4.11
Compiling arrayvec v0.7.6
Compiling cfg_aliases v0.2.1
Compiling xkeysym v0.2.1
Compiling winnow v0.7.13
Compiling parking_lot_core v0.9.12
Compiling libloading v0.8.9
Compiling smithay-client-toolkit v0.19.2
Compiling xcursor v0.3.10
Compiling getrandom v0.3.3
Compiling cc v1.2.40
Compiling signal-hook v0.4.3
Compiling dlib v0.5.2
Compiling scopeguard v1.2.0
Compiling utf8parse v0.2.2
Compiling toml_writer v1.0.6+spec-1.1.0
Compiling ryu v1.0.20
Compiling lock_api v0.4.14
Compiling simd-adler32 v0.3.7
Compiling zerocopy v0.8.27
Compiling arrayref v0.3.9
Compiling anstyle v1.0.13
Compiling itoa v1.0.15
Compiling anstyle-parse v0.2.7
Compiling regex-syntax v0.8.6
Compiling is_terminal_polyfill v1.70.1
Compiling as-raw-xcb-connection v1.0.1
Compiling version_check v0.9.5
Compiling colorchoice v1.0.4
Compiling x11rb-protocol v0.13.2
Compiling bytemuck v1.24.0
Compiling anstyle-query v1.1.4
Compiling crc32fast v1.5.0
Compiling strict-num v0.1.1
Compiling foreign-types-shared v0.3.1
Compiling serde_json v1.0.145
Compiling hashbrown v0.16.0
Compiling unicode-width v0.2.2
Compiling anstream v0.6.21
Compiling home v0.5.11
Compiling strsim v0.11.1
Compiling base64 v0.22.1
Compiling heck v0.5.0
Compiling adler2 v2.0.1
Compiling equivalent v1.0.2
Compiling clap_lex v0.7.5
Compiling glutin v0.32.3
Compiling winit v0.30.13
Compiling pin-project-lite v0.2.16
Compiling wayland-sys v0.31.7
Compiling yeslogic-fontconfig-sys v5.0.0
Compiling x11-dl v2.21.0
Compiling quick-xml v0.37.5
Compiling crossfont v0.8.1
Compiling ahash v0.8.12
Compiling aho-corasick v1.1.3
Compiling tiny-skia-path v0.11.4
Compiling miniz_oxide v0.8.9
Compiling clap_builder v4.5.48
Compiling same-file v1.0.6
Compiling tracing-core v0.1.34
Compiling raw-window-handle v0.6.2
Compiling walkdir v2.5.0
Compiling fdeflate v0.3.7
Compiling bitflags v1.3.2
Compiling notify-types v2.0.0
Compiling unsafe-libyaml v0.2.11
Compiling percent-encoding v2.3.2
Compiling fastrand v2.3.0
Compiling xdg v3.0.0
Compiling syn v2.0.106
Compiling indexmap v2.11.4
Compiling cstr v0.2.12
Compiling tracing v0.1.41
Compiling flate2 v1.1.4
Compiling wayland-backend v0.3.11
Compiling freetype-sys v0.20.1
Compiling wayland-scanner v0.31.7
Compiling memmap2 v0.9.8
Compiling signal-hook-registry v1.4.6
Compiling inotify-sys v0.1.5
Compiling parking_lot v0.12.5
Compiling png v0.17.16
Compiling toml_parser v1.0.6+spec-1.1.0
Compiling regex-automata v0.4.11
Compiling toml_datetime v0.7.5+spec-1.1.0
Compiling serde_spanned v1.0.4
Compiling toml v0.9.11+spec-1.1.0
Compiling toml_edit v0.24.0+spec-1.1.0
Compiling serde_derive v1.0.228
Compiling thiserror-impl v1.0.69
Compiling foreign-types-macros v0.2.3
Compiling clap_derive v4.5.47
Compiling alacritty_config_derive v0.2.6-dev (/bass/work/alacritty_config_derive)
Compiling foreign-types v0.5.0
Compiling clap v4.5.48
Compiling clap_complete v4.5.58
Compiling log v0.4.28
Compiling bitflags v2.9.4
Compiling cursor-icon v1.2.0
Compiling smol_str v0.2.2
Compiling dpi v0.1.2
Compiling serde_yaml v0.9.34+deprecated
Compiling gl_generator v0.14.0
Compiling alacritty_config v0.2.4-dev (/bass/work/alacritty_config)
Compiling freetype-rs v0.36.0
Compiling inotify v0.11.0
Compiling vte v0.15.0
Compiling tiny-skia v0.11.4
Compiling mio v1.0.4
Compiling xkbcommon-dl v0.4.2
Compiling notify v8.2.0
Compiling glutin_egl_sys v0.7.1
Compiling glutin_glx_sys v0.6.1
Compiling alacritty v0.17.0-dev (/bass/work/alacritty)
Compiling polling v3.11.0
Compiling gethostname v1.0.2
Compiling rustix-openpty v0.2.0
Compiling tempfile v3.23.0
Compiling calloop v0.13.0
Compiling alacritty_terminal v0.25.2-dev (/bass/work/alacritty_terminal)
Compiling wayland-csd-frame v0.3.0
Compiling x11rb v0.13.2
Compiling wayland-protocols v0.32.9
Compiling calloop-wayland-source v0.3.0
Compiling wayland-cursor v0.31.11
Compiling x11-clipboard v0.9.3
Compiling wayland-protocols-wlr v0.3.9
Compiling wayland-protocols-plasma v0.3.9
Compiling sctk-adwaita v0.10.1
Compiling smithay-clipboard v0.7.2
Compiling copypasta v0.10.2
Finished `test` profile [unoptimized + debuginfo] target(s) in 17.58s
Running unittests src/main.rs (target/debug/deps/alacritty-f57d05b85b87aaa8)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 79 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/alacritty_config-a0113ec6d12890c0)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/alacritty_config_derive-5b1b4e065bba4409)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/config.rs (target/debug/deps/config-5acd74e4d4dd09cb)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 7 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/alacritty_terminal-55f1349ecb031e54)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 132 filtered out; finished in 0.00s
Running tests/ref.rs (target/debug/deps/ref-ccbda13791f1bc7b)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 45 filtered out; finished in 0.00s
true
demo running services
𝄢

To run a service thunk, assign names to its ports using with-port. The provided ports will be healthchecked whenever the service runs.

(defn http-server [index]
  (from (linux/python)
    (-> ($ python -m http.server)
        (with-mount (mkfile ./index.html index) ./index.html)
        (with-port :http 8000))))

(http-server "Hello, world!")
python -m http.server
{
:image
{
:image
{
:repository "python"
:platform
{
:architecture "amd64"
:os "linux"
}
:tag "latest"
:digest "sha256:7aea6827c8787754f99339ffed8cfc41fb09421f9c7d0e77a198b08422a3455e"
}
}
:args
(
  1. "python"
  2. "-m"
  3. "http.server"
)
:mounts
(
  1. {
    :source <fs>/index.html
    :target ./index.html
    }
)
}
𝄢

You can use addr to construct a thunk addr. A thunk addr is like a thunk path except it references a named port provided by the thunk rather than a file created by it.

(defn echo [msg]
  (let [server (http-server msg)]
    (from (linux/alpine)
      ($ wget -O- (addr server :http "http://$host:$port")))))

(echo "Hello, world!")
wget -O- {{thunk 5TUAG03AHNS8S: python -m http.server}}:http
{
:image
{
:image
{
:repository "alpine"
:platform
{
:architecture "amd64"
:os "linux"
}
:tag "latest"
:digest "sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659"
}
}
:args
(
  1. "wget"
  2. "-O-"
  3. python -m http.server
    {
    :image
    {
    :image
    {
    :repository "python"
    :platform
    {
    :architecture "amd64"
    :os "linux"
    }
    :tag "latest"
    :digest "sha256:7aea6827c8787754f99339ffed8cfc41fb09421f9c7d0e77a198b08422a3455e"
    }
    }
    :args
    (
    1. "python"
    2. "-m"
    3. "http.server"
    )
    :mounts
    (
    1. {
      :source <fs>/index.html
      :target ./index.html
      }
    )
    }
    http
)
}
𝄢

Like thunks and thunk paths, thunk addrs are just data values. The underlying service thunk only runs when another thunk that needs it runs.

(run (echo "Hello, world!"))
stderr: 26 lines
─╮
run {{thunk A0DUJ7UAMP9TU: wget -O- {{thunk 5TUAG03AHNS8S: python -m http.server}}:http}}
[0.03s] resolve image config for docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
─╮
start {{thunk 5TUAG03AHNS8S: python -m http.server}}
[0.43s] resolve image config for docker.io/library/python@sha256:7aea6827c8787754f99339ffed8cfc41fb09421f9c7d0e77a198b08422a3455e
[0.02s] mkfile /index.html
[0.00s] docker-image://docker.io/library/python@sha256:7aea6827c8787754f99339ffed8cfc41fb09421f9c7d0e77a198b08422a3455e
[0.02s] resolve docker.io/library/python@sha256:7aea6827c8787754f99339ffed8cfc41fb09421f9c7d0e77a198b08422a3455e
[0.11s] sha256:6052218b0e44cfb90f3a976135fe864bfd4fd24ee26965579474ce6e7a070c8d
[0.44s] sha256:ab1b8c92e1206c8483c82e4baaf3b8ed1cbce42e820339311f491da902b4e905
[0.48s] sha256:36a6d998ca16b439abfebc56848087e395ee1ff6552bea59bc77a879998b506c
[0.10s] extracting sha256:36a6d998ca16b439abfebc56848087e395ee1ff6552bea59bc77a879998b506c
[0.36s] extracting sha256:ab1b8c92e1206c8483c82e4baaf3b8ed1cbce42e820339311f491da902b4e905
[0.00s] extracting sha256:6052218b0e44cfb90f3a976135fe864bfd4fd24ee26965579474ce6e7a070c8d
[0.95s] CANCELED python -m http.server
[0.01s] docker-image://docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.01s] resolve docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.13s] wget -O- {{thunk 5TUAG03AHNS8S: python -m http.server}}:http
Connecting to 5TUAG03AHNS8S:8000 (10.64.0.32:8000)
writing to stdout
- 100% |********************************| 13 0:00:00 ETA
written to stdout
Hello, world!
null
demo building & publishing artifacts
𝄢

To build from source just run whatever build command you already use.

(use (.git (linux/alpine/git)))

(defn go-build [src & args]
  (from (linux/golang)
    (cd src
      (-> ($ go build & $args)
          (with-env {:CGO_ENABLED "0"})))))

(let [src git:github/vito/booklit/ref/master/
      built (go-build src "./cmd/booklit")]
  (-> (from (linux/alpine)
        ($ built/booklit --version))
      (read :raw)
      next))
stderr: 50 lines
─╮
read {{thunk 8G41EUGJKMDJI: {{thunk 6H92RQ317NEF8: go build ./cmd/booklit}}/booklit --version}}
[0.01s] resolve image config for docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.00s] resolve image config for docker.io/library/golang@sha256:c7e98cc0fd4dfb71ee7465fee6c9a5f079163307e4bf141b336bb9dae00159a5
[0.00s] resolve image config for docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] docker-image://docker.io/library/golang@sha256:c7e98cc0fd4dfb71ee7465fee6c9a5f079163307e4bf141b336bb9dae00159a5
[0.01s] resolve docker.io/library/golang@sha256:c7e98cc0fd4dfb71ee7465fee6c9a5f079163307e4bf141b336bb9dae00159a5
[0.01s] docker-image://docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.01s] resolve docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.01s] docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] resolve docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.86s] git clone https://github.com/vito/booklit ./
Cloning into '.'...
[0.29s] git fetch origin 2372cdeb7c66fe38fe4a7a833e941a1672d51194
From https://github.com/vito/booklit
* branch 2372cdeb7c66fe38fe4a7a833e941a1672d51194 -> FETCH_HEAD
[0.16s] git checkout 2372cdeb7c66fe38fe4a7a833e941a1672d51194
Note: switching to '2372cdeb7c66fe38fe4a7a833e941a1672d51194'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 2372cde disable jekyll
[0.13s] git submodule update --init --recursive
[5.24s] go build ./cmd/booklit
go: downloading github.com/jessevdk/go-flags v1.5.0
go: downloading github.com/sirupsen/logrus v1.9.3
go: downloading github.com/alecthomas/chroma/v2 v2.23.1
go: downloading golang.org/x/text v0.14.0
go: downloading github.com/agext/levenshtein v1.2.3
go: downloading github.com/segmentio/textio v1.2.0
go: downloading github.com/yuin/goldmark v1.7.8
go: downloading golang.org/x/sys v0.18.0
go: downloading github.com/dlclark/regexp2 v1.11.5
[0.13s] {{thunk 6H92RQ317NEF8: go build ./cmd/booklit}}/booklit --version
time="2026-03-11T04:10:07Z" level=info msg="plugin registered" plugin=baselit
0.0.0-dev
"0.0.0-dev\n"
𝄢

Thunk paths can be serialized to JSON. If all thunks involved in its creation are hermetic the JSON structure represents a repeatable artifact.

(def built
  (go-build git:github/vito/booklit/ref/master/ "./cmd/booklit"))

(emit built *stdout*)
{
  "image": {
    "thunk": {
      "image": {
        "ref": {
          "platform": {
            "os": "linux",
            "arch": "amd64"
          },
          "repository": "golang",
          "tag": "latest",
          "digest": "sha256:c7e98cc0fd4dfb71ee7465fee6c9a5f079163307e4bf141b336bb9dae00159a5"
        }
      }
    }
  },
  "args": [
    {
      "string": {
        "value": "go"
      }
    },
    {
      "string": {
        "value": "build"
      }
    },
    {
      "string": {
        "value": "./cmd/booklit"
      }
    }
  ],
  "env": [
    {
      "symbol": "CGO_ENABLED",
      "value": {
        "string": {
          "value": "0"
        }
      }
    }
  ],
  "mounts": [
    {
      "source": {
        "thunk": {
          "thunk": {
            "image": {
              "thunk": {
                "image": {
                  "thunk": {
                    "image": {
                      "thunk": {
                        "image": {
                          "thunk": {
                            "image": {
                              "ref": {
                                "platform": {
                                  "os": "linux",
                                  "arch": "amd64"
                                },
                                "repository": "alpine/git",
                                "tag": "latest",
                                "digest": "sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3"
                              }
                            }
                          }
                        },
                        "args": [
                          {
                            "string": {
                              "value": "git"
                            }
                          },
                          {
                            "string": {
                              "value": "clone"
                            }
                          },
                          {
                            "string": {
                              "value": "https://github.com/vito/booklit"
                            }
                          },
                          {
                            "dirPath": {
                              "path": "."
                            }
                          }
                        ]
                      }
                    },
                    "args": [
                      {
                        "string": {
                          "value": "git"
                        }
                      },
                      {
                        "string": {
                          "value": "fetch"
                        }
                      },
                      {
                        "string": {
                          "value": "origin"
                        }
                      },
                      {
                        "string": {
                          "value": "2372cdeb7c66fe38fe4a7a833e941a1672d51194"
                        }
                      }
                    ]
                  }
                },
                "args": [
                  {
                    "string": {
                      "value": "git"
                    }
                  },
                  {
                    "string": {
                      "value": "checkout"
                    }
                  },
                  {
                    "string": {
                      "value": "2372cdeb7c66fe38fe4a7a833e941a1672d51194"
                    }
                  }
                ]
              }
            },
            "args": [
              {
                "string": {
                  "value": "git"
                }
              },
              {
                "string": {
                  "value": "submodule"
                }
              },
              {
                "string": {
                  "value": "update"
                }
              },
              {
                "string": {
                  "value": "--init"
                }
              },
              {
                "string": {
                  "value": "--recursive"
                }
              }
            ]
          },
          "path": {
            "dir": {
              "path": "."
            }
          }
        }
      },
      "target": {
        "dir": {
          "path": "."
        }
      }
    }
  ]
}
null
𝄢

The exact format is not finalized and probably needs versioning and deduping.

A thunk path's JSON form can be piped to bass --export to build the artifact and emit a tar stream.

cat thunk-path.json | bass --export | tar -xf -

You can publish thunk path JSON as part of your release as a form of provenance:

(let [repro (mkfile ./file.json (json built))]
  (from (linux/nixery.dev/gh)
    ($ gh release create v0.0.1 $repro)))
stderr: 4 lines
─╮
resolve {{thunk OUH3MRLTGBAHO: }}
[17.62s] resolve image config for nixery.dev/gh:latest
gh release create v0.0.1 <fs>/file.json
{
:image
{
:image
{
:repository "nixery.dev/gh"
:platform
{
:architecture "amd64"
:os "linux"
}
:tag "latest"
:digest "sha256:61a64314112c65df2b3bf13b00c5edacc876618379838fe0f0a3f009f618f8cd"
}
}
:args
(
  1. "gh"
  2. "release"
  3. "create"
  4. "v0.0.1"
  5. <fs>/file.json
)
}
demo pinning dependencies
𝄢

To pin dependencies, configure a path to a bass.lock file as the magic *memos* binding.

(def *memos* *dir*/bass.lock)
*memos*
𝄢

The linux path root resolves an image reference to a digest and memoizes its result into *memos* if defined.

(run (from (linux/alpine) ; resolves linux/alpine and writes to *memos*
       ($ echo hi)))

(run (from (linux/alpine) ; uses the digest from *memos*
       ($ cat $*memos*))) ; reveals the wizard behind the curtain
stderr: 88 lines
─╮
resolve {{thunk GH0TPL8SHMP36: }}
[0.12s] resolve image config for docker.io/library/alpine:latest
─╮
run {{thunk TNR8TNCFGNUNC: echo hi}}
[0.01s] resolve image config for docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.01s] docker-image://docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
[0.01s] resolve docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
─╮ docker-image://docker.io/library/alpine@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659
◀┤ [0.15s] echo hi
hi
─╮
run {{thunk 23VFMLSSM5HVG: cat <host: /tmp/bass-scope3501422696>/bass.lock}}
[0.01s] upload <host: /tmp/bass-scope3501422696>/bass.lock
[0.00s] transferring /tmp/bass-scope3501422696:
[0.00s] copy /bass.lock /bass.lock
◀╯ [0.14s] cat <host: /tmp/bass-scope3501422696>/bass.lock
memos: {
module: {
args: {
command_path: {
name: "run"
}
}
}
calls: {
binding: "resolve"
results: {
input: {
array: {
values: {
object: {
bindings: {
symbol: "platform"
value: {
object: {
bindings: {
symbol: "os"
value: {
string: {
value: "linux"
}
}
}
}
}
}
bindings: {
symbol: "repository"
value: {
string: {
value: "alpine"
}
}
}
bindings: {
symbol: "tag"
value: {
string: {
value: "latest"
}
}
}
}
}
}
}
output: {
thunk: {
image: {
ref: {
platform: {
os: "linux"
arch: "amd64"
}
repository: "alpine"
tag: "latest"
digest: "sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659"
}
}
}
}
}
}
}
null
𝄢

The github path root resolves a branch or tag reference to a commit and returns its checkout, memoizing the commit in *memos* if defined.

(use (.git (linux/alpine/git)))

git:github/vito/booklit/ref/master/
stderr: 12 lines
─╮
resolve {{thunk 27OTTG8R8CRVM: }}
[0.14s] resolve image config for docker.io/alpine/git:latest
─╮
read {{thunk 3C5PBVAIV19NG: git ls-remote https://github.com/vito/booklit master}}
[0.01s] resolve image config for docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] resolve docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.27s] git ls-remote https://github.com/vito/booklit master
2372cdeb7c66fe38fe4a7a833e941a1672d51194 refs/heads/master
git submodule update --init --recursive
{
:image
git checkout 2372cdeb7c66fe38fe4a7a833e941a1672d51194
{
:image
git fetch origin 2372cdeb7c66fe38fe4a7a833e941a1672d51194
{
:image
git clone https://github.com/vito/booklit ./
{
:image
{
:image
{
:repository "alpine/git"
:platform
{
:architecture "amd64"
:os "linux"
}
:tag "latest"
:digest "sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3"
}
}
:args
(
  1. "git"
  2. "clone"
  3. "https://github.com/vito/booklit"
  4. ./
)
}
:args
(
  1. "git"
  2. "fetch"
  3. "origin"
  4. "2372cdeb7c66fe38fe4a7a833e941a1672d51194"
)
}
:args
(
  1. "git"
  2. "checkout"
  3. "2372cdeb7c66fe38fe4a7a833e941a1672d51194"
)
}
:args
(
  1. "git"
  2. "submodule"
  3. "update"
  4. "--init"
  5. "--recursive"
)
}
./
𝄢

Paths like above are often used with use to load Bass modules from thunk paths. Bass doesn't have its own package system; it uses thunks for that too.

(let [src git:github/vito/booklit/ref/master/]
  (use (src/bass/booklit.bass))
  (when (succeeds? (booklit:tests src))
    (booklit:build src "dev" "linux" "amd64")))
stderr: 129 lines
─╮
export path {{thunk OAJ9RR01DK7P2: git submodule update --init --recursive}}/bass/booklit.bass
[0.01s] resolve image config for docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] resolve docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
─╮ docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
◀┤ [0.74s] git clone https://github.com/vito/booklit ./
Cloning into '.'...
[0.29s] git fetch origin 2372cdeb7c66fe38fe4a7a833e941a1672d51194
From https://github.com/vito/booklit
* branch 2372cdeb7c66fe38fe4a7a833e941a1672d51194 -> FETCH_HEAD
[0.17s] git checkout 2372cdeb7c66fe38fe4a7a833e941a1672d51194
Note: switching to '2372cdeb7c66fe38fe4a7a833e941a1672d51194'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 2372cde disable jekyll
[0.15s] git submodule update --init --recursive
─╮
export path {{thunk OAJ9RR01DK7P2: git submodule update --init --recursive}}/bass/bass.lock
◀┤ [0.76s] git clone https://github.com/vito/booklit ./
Cloning into '.'...
[0.28s] git fetch origin 2372cdeb7c66fe38fe4a7a833e941a1672d51194
From https://github.com/vito/booklit
* branch 2372cdeb7c66fe38fe4a7a833e941a1672d51194 -> FETCH_HEAD
[0.18s] git checkout 2372cdeb7c66fe38fe4a7a833e941a1672d51194
Note: switching to '2372cdeb7c66fe38fe4a7a833e941a1672d51194'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 2372cde disable jekyll
[0.13s] git submodule update --init --recursive
─╮
run {{thunk MK5ORO6R6KSD8: ./scripts/test -p}}
[0.38s] resolve image config for docker.io/library/golang@sha256:403f48633fb5ebd49f9a2b6ad6719f912df23dae44974a0c9445be331e72ff5e
[0.01s] docker-image://docker.io/library/golang@sha256:403f48633fb5ebd49f9a2b6ad6719f912df23dae44974a0c9445be331e72ff5e
[0.01s] resolve docker.io/library/golang@sha256:403f48633fb5ebd49f9a2b6ad6719f912df23dae44974a0c9445be331e72ff5e
[0.07s] sha256:94ffa0dd0d53ec79db46d23854e0d4826253b23ef3ab5f6c3cc020013e34d9da
[1.52s] sha256:b0248cf3e63c73d0e496a67807d056ca41d5e968b61087e8eca2cf4b9b4d7b99
[2.86s] sha256:28b9642b26541791df806ca798ac3d02bed9379c4945a1dfed80e2768bcead73
[2.66s] sha256:079116d1299c7a03ad97f33aca1b181392a259fb6bd3b4b22efee1e80c7a62d3
[0.44s] sha256:0336c50c9f6942b660e433b1086238eec37057c34b14c4e3b28bd7bf05bd84ba
[2.18s] sha256:1b89f3c7f7da8adf032a33a75d1b659cee33179ecb88ea0ba75e4fc58ebe63a6
[0.55s] extracting sha256:b0248cf3e63c73d0e496a67807d056ca41d5e968b61087e8eca2cf4b9b4d7b99
[0.28s] sha256:127e97b4daf784e08840a21765f0d4f251192ef2994d0e4a253490f81e63955b
[0.05s] extracting sha256:127e97b4daf784e08840a21765f0d4f251192ef2994d0e4a253490f81e63955b
[0.06s] extracting sha256:0336c50c9f6942b660e433b1086238eec37057c34b14c4e3b28bd7bf05bd84ba
[0.50s] extracting sha256:1b89f3c7f7da8adf032a33a75d1b659cee33179ecb88ea0ba75e4fc58ebe63a6
[0.65s] extracting sha256:079116d1299c7a03ad97f33aca1b181392a259fb6bd3b4b22efee1e80c7a62d3
[0.94s] extracting sha256:28b9642b26541791df806ca798ac3d02bed9379c4945a1dfed80e2768bcead73
[0.01s] extracting sha256:94ffa0dd0d53ec79db46d23854e0d4826253b23ef3ab5f6c3cc020013e34d9da
◀╯ [6.75s] git clone https://github.com/vito/booklit ./
Cloning into '.'...
[0.33s] git fetch origin 2372cdeb7c66fe38fe4a7a833e941a1672d51194
From https://github.com/vito/booklit
* branch 2372cdeb7c66fe38fe4a7a833e941a1672d51194 -> FETCH_HEAD
[0.17s] git checkout 2372cdeb7c66fe38fe4a7a833e941a1672d51194
Note: switching to '2372cdeb7c66fe38fe4a7a833e941a1672d51194'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 2372cde disable jekyll
[0.15s] git submodule update --init --recursive
[0.01s] copy / /
[4.97s] go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo
go: downloading github.com/onsi/ginkgo/v2 v2.9.1
go: downloading github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
go: downloading github.com/google/pprof v0.0.0-20230406165453-00490a63f317
go: downloading golang.org/x/tools v0.10.0
[1.09s] ERROR ./scripts/test -p
go: downloading golang.org/x/text v0.14.0
go: downloading github.com/sirupsen/logrus v1.9.3
go: downloading github.com/yuin/goldmark v1.7.8
go: downloading github.com/jessevdk/go-flags v1.5.0
go: downloading github.com/alecthomas/chroma/v2 v2.23.1
go: downloading github.com/onsi/gomega v1.27.4
go: downloading github.com/go-logr/logr v1.2.4
go: downloading github.com/agext/levenshtein v1.2.3
go: downloading github.com/segmentio/textio v1.2.0
go: downloading golang.org/x/net v0.23.0
go: downloading golang.org/x/sys v0.18.0
go: downloading github.com/google/go-cmp v0.5.9
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading github.com/dlclark/regexp2 v1.11.5
errhtml/embed.go:5:19: pattern *.css: no matching files found
ginkgo run failed
Found no test suites
null
𝄢

To re-evaluate and update all memoized results, run bass --bump:

bass --bump bass.lock

This command loads each module and re-evalutes each memoized call, updating the bass.lock file in-place.

demo webhook-driven CI/CD
𝄢

Bass Loop is a public service for calling Bass code in response to webhooks.

First, install the GitHub app and put a script like this in your repo at bass/github-hook:

; file for memoized dependency resolution
(def *memos* *dir*/bass.lock)

; load dependencies
(use (.git (linux/alpine/git))
     (git:github/vito/bass-loop/ref/main/bass/github.bass))

; run Go tests
(defn go-test [src & args]
  (from (linux/golang)
    (cd src
      ($ go test & $args))))

; standard suite of validations for the repo
(defn checks [src]
  {:test (go-test src "./...")})

; called by bass-loop
(defn main []
  (for [event *stdin*]
    (github:check-hook event git:checkout checks)))
stderr: 41 lines
─╮
resolve {{thunk 27OTTG8R8CRVM: }}
[0.08s] resolve image config for docker.io/alpine/git:latest
─╮
read {{thunk V14HU5TF4QPJG: git ls-remote https://github.com/vito/bass-loop main}}
[0.01s] resolve image config for docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.01s] docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
[0.00s] resolve docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
─╮ docker-image://docker.io/alpine/git@sha256:d453f54c83320412aa89c391b076930bd8569bc1012285e8c68ce2d4435826a3
◀┤ [0.32s] git ls-remote https://github.com/vito/bass-loop main
c14c6dabec09038ff41c53a98c140e6931d21d32 refs/heads/main
─╮
export path {{thunk RRJSI8Q2M6C00: git submodule update --init --recursive}}/bass/github.bass
◀╯ [0.75s] git clone https://github.com/vito/bass-loop ./
Cloning into '.'...
[0.27s] git fetch origin c14c6dabec09038ff41c53a98c140e6931d21d32
From https://github.com/vito/bass-loop
* branch c14c6dabec09038ff41c53a98c140e6931d21d32 -> FETCH_HEAD
[0.16s] git checkout c14c6dabec09038ff41c53a98c140e6931d21d32
Note: switching to 'c14c6dabec09038ff41c53a98c140e6931d21d32'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at c14c6da vendorsha
[0.13s] git submodule update --init --recursive
main
𝄢

Next start a Bass runner to let Bass Loop use your local runtimes:

bass --runner myuser@github.bass-lang.org

From here on anything that myuser does to the repo will route an event to the bass/github-hook script with myuser's runners available for running thunks.

The github:check-hook helper handles check-related events by running thunks as GitHub status checks. Other events may be interpreted however you like.