Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/vito/bass
a low fidelity scripting language for project infrastructure
https://github.com/vito/bass
build buildkit language lisp
Last synced: 3 days ago
JSON representation
a low fidelity scripting language for project infrastructure
- Host: GitHub
- URL: https://github.com/vito/bass
- Owner: vito
- License: mit
- Created: 2021-06-22T04:15:01.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2025-01-23T17:42:05.000Z (11 days ago)
- Last Synced: 2025-01-24T22:06:09.589Z (10 days ago)
- Topics: build, buildkit, language, lisp
- Language: Go
- Homepage: https://bass-lang.org
- Size: 28.5 MB
- Stars: 387
- Watchers: 9
- Forks: 13
- Open Issues: 22
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# bass
[![Discord](https://img.shields.io/discord/939941216215240774?color=7389D8&label=&logo=discord&logoColor=ffffff&labelColor=6A7EC2)](https://discord.gg/HFW85RyUtK)
Bass is a low-fidelity Lisp dialect for the glue code driving your project.
https://github.com/vito/bass/assets/1880/ab05445c-95f7-44b6-a67b-d9fc8eb02d41
## reasons you might be interested
* you're sick of YAML and want to write code instead of config and templates
* you'd like to have a uniform stack between dev and CI
* you'd like be able to audit or rebuild published artifacts
* you're nostalgic about Lisp## what the thunk?
Bass is a functional language for scripting commands, represented by
[thunks][t-thunk]. A thunk is a serializable recipe for a command to run,
including all of its inputs, and their inputs, and so on. ([Why are they called
thunks?][why-thunks])Thunks lazily run their command to produce a `stdout` stream, an output
directory, and an exit status. These results are cached indefinitely, but only
when the command succeeds.```clojure
$ bass
=> (from (linux/alpine) ($ cat *dir*/README.md))██████
██████████
██████████████
████ ██ ████
██████████████
██████ ██████
████ ██ ████
████ ████=> (thunk? (from (linux/alpine) ($ cat *dir*/README.md)))
true
```To run a thunk and raise an error if the command fails, use [`(run)`][b-run].
To get `true` or `false` instead, use [`(succeeds?)`][b-succeeds].```clojure
=> (def thunk (from (linux/alpine) ($ echo "Hello, world!")))
thunk
=> (run thunk)
; Hello, world!
null
=> (succeeds? thunk)
true
=> (succeeds? (from (linux/alpine) ($ sh -c "exit 1")))
false
```To access a thunk's output directory, use a [thunk path][t-thunk-path]. Thunk
paths can be passed to other thunks. Filesystem timestamps in thunk paths are
normalized to `1985-10-06 08:15 UTC` to support reproducible builds.```clojure
=> (def thunk (from (linux/alpine) ($ cp *dir*/README.md ./some-file)))
thunk
=> (run (from (linux/alpine) ($ head "-1" thunk/some-file)))
; # bass
null
```To parse values from a thunk's `stdout` or from a thunk path, use
[`(read)`][b-read].```clojure
=> (next (read (from (linux/alpine) ($ head "-1" thunk/some-file)) :raw))
"# bass\n"
=> (next (read thunk/some-file :lines))
"# bass"
=> (next (read thunk/some-file :unix-table))
("#" "bass")
```To serialize a thunk or thunk path to JSON, use [`(json)`][b-json] or
[`(emit)`][b-emit] it to `*stdout*`. Pipe a thunk path to `bass --export | tar
-xf -` to extract it, or pipe a thunk to `bass --export | docker load` to
export a thunk to Docker.```sh
$ ./bass/build -i src=./ | bass --export | tar -xf -
$ ls bass.linux-amd64.tgz
```This, and generally everything, works best when your thunks are
[hermetic][t-hermetic].#### tl;dr
It's a bit of a leap, but I like to think of Bass as a purely functional,
lazily evaluated Bash.Instead of running commands that mutate machine state, Bass has a read-only
view of the host machine and passes files around as values in ephemeral,
reproducible filesystems addressed by their creator thunk.[b-read]: https://bass-lang.org/stdlib.html#binding-read
[b-run]: https://bass-lang.org/stdlib.html#binding-run
[b-succeeds]: https://bass-lang.org/stdlib.html#binding-succeeds?
[b-json]: https://bass-lang.org/stdlib.html#binding-json
[b-emit]: https://bass-lang.org/stdlib.html#binding-emit## example
Running a [thunk][t-thunk]:
```clojure
(def thunk
(from (linux/ubuntu)
($ echo "Hello, world!")))(run thunk)
```Passing [thunk paths][t-thunk-path] around:
```clojure
; use git stdlib module
(use (.git (linux/alpine/git))); returns a thunk dir containing compiled binaries
(defn go-build [src pkg]
(subpath
(from (linux/golang)
(cd src
($ go build -o ./built/ $pkg)))
./built/))(defn main []
(let [src git:github/vito/booklit/ref/master/
bins (go-build src "./cmd/...")]
; kick the tires
(run (from (linux/ubuntu)
($ bins/booklit --version)))(emit bins *stdout*)))
```### irl examples
* [Bass](bass/)
* [Booklit](https://github.com/vito/booklit/tree/master/bass)## what's it for?
Bass typically replaces CI `.yml` files, `Dockerfile`s, and Bash scripts.
Instead of writing `.yml` DSLs interpreted by some CI system, you write real
code. Instead of writing ad-hoc `Dockerfile`s and pushing/pulling images, you
chain thunks and share them as code. Instead of writing Bash scripts, you write
Bass scripts.Bass scripts have limited access to the host machine, making them portable
between dev and CI environments. They can be used to codify your entire
toolchain into platform-agnostic scripts.In the end, the purpose of Bass is to run [thunks][t-thunk]. Thunks are
serializable command recipes that produce files or streams of values. Files
created by thunks can be easily passed to other thunks, forming one big
super-thunk that recursively embeds all of its dependencies.Bass is designed for [hermetic][t-hermetic] builds but it stops short of
enforcing them. Bass trades purism for pragmatism, sticking to familiar albeit
fallible CLIs rather than abstract declarative configuration. For your
artifacts to be reproducible your thunks must be hermetic, but if you simply
don't care yet, YOLO `apt-get` all day and fix it up later.For a quick run-through of these ideas, check out the [Bass homepage][bass].
[bass]: https://bass-lang.org
[llb]: https://github.com/moby/buildkit/blob/master/docs/solver.md
[t-thunk]: https://bass-lang.org/bassics.html#term-thunk
[t-thunk-path]: https://bass-lang.org/bassics.html#term-thunk%20path
[t-hermetic]: https://bass-lang.org/bassics.html#term-hermetic### how does it work?
To run a thunk, Bass's [Buildkit][buildkit] runtime translates it to one big
[LLB][llb] definition and solves it through the client API. The runtime handles
setting up mounts and converting thunk paths to string values passed to the
underlying command.The runtime architecture is modular, but Buildkit is the only implementation at
the moment.## start playing
* prerequisites: `git`, `go`, `upx`
```sh
$ git clone https://github.com/vito/bass
$ cd bass
$ make -j install
```Bass runs thunks with [Buildkit][buildkit-quickstart], so you'll need
`buildkitd` running somewhere, somehow.If `docker` is installed and running Bass will use it to start Buildkit
automatically and you can skip the rest of this section.### Linux
The included `./hack/buildkit/` scripts can be used if you don't have
`buildkitd` running already.```sh
$ ./hack/buildkit/start # if needed
$ bass ./demos/go-build-git.bass
```[buildkit-quickstart]: https://github.com/moby/buildkit#quick-start
### macOS
macOS support works by just running Buildkit in a Linux VM.
Use the included [`lima/bass.yaml`](lima/bass.yaml) template to manage the VM
with [`limactl`][lima].```sh
$ brew install lima
$ limactl start ./lima/bass.yaml
$ bass ./demos/go-build-git.bass
```[lima]: https://github.com/lima-vm/lima
### Windows
Same as Linux, using WSL2. Windows containers should work [once Buildkit
supports it][windows].[windows]: https://github.com/moby/buildkit/issues/616
## editor setup
* vim config: [bass.vim][bass.vim]
[bass.vim]: https://github.com/vito/bass.vim
```vim
Plug 'vito/bass.vim'lua <
[MacStadium]: https://www.macstadium.com/
[kernel]: https://web.cs.wpi.edu/~jshutt/kernel.html
[clojure]: https://clojure.org/[go]: https://golang.org
[concourse]: https://github.com/concourse/concourse
[oci]: https://github.com/opencontainers/image-spec
[atomo]: https://github.com/vito/atomo
[atomy]: https://github.com/vito/atomy
[pumice]: https://github.com/vito/pumice
[cletus]: https://github.com/vito/cletus
[hummus]: https://github.com/vito/hummus
[resources]: https://concourse-ci.org/resources.html
[tasks]: https://concourse-ci.org/tasks.html
[jq]: https://stedolan.github.io/jq/
[concourse-types]: https://resource-types.concourse-ci.org/
[json]: https://www.json.org/
[streams]: https://en.wikipedia.org/wiki/Standard_streams
[buildkit]: https://github.com/moby/buildkit
[booklit-test]: https://github.com/vito/booklit/blob/master/ci/test.yml
[booklit-build]: https://github.com/vito/booklit/blob/master/ci/build.yml
[why-thunks]: https://github.com/vito/bass-loop/discussions/4