• Stars
    star
    346
  • Rank 122,430 (Top 3 %)
  • Language
    Go
  • License
    MIT License
  • Created over 3 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

a low fidelity scripting language for project infrastructure

bass

Discord

Bass is a low-fidelity Lisp dialect for the glue code driving your project.

README.mp4

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. 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?)

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.

$ bass
=> (from (linux/alpine) ($ cat *dir*/README.md))


        β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
      β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
    β–ˆβ–ˆβ–ˆβ–ˆ  β–ˆβ–ˆ  β–ˆβ–ˆβ–ˆβ–ˆ
    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
    β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
  β–ˆβ–ˆβ–ˆβ–ˆ    β–ˆβ–ˆ    β–ˆβ–ˆβ–ˆβ–ˆ
    β–ˆβ–ˆβ–ˆβ–ˆ      β–ˆβ–ˆβ–ˆβ–ˆ

<thunk JU61UMJQ70FMI: (.cat)>
=> (thunk? (from (linux/alpine) ($ cat *dir*/README.md)))
true

To run a thunk and raise an error if the command fails, use (run). To get true or false instead, use (succeeds?).

=> (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. 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.

=> (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).

=> (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) or (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.

$ ./bass/build -i src=./ | bass --export | tar -xf -
$ ls bass.linux-amd64.tgz

This, and generally everything, works best when your thunks are 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.

example

Running a thunk:

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

(run thunk)

Passing thunk paths around:

; 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

what's it for?

Bass typically replaces CI .yml files, Dockerfiles, and Bash scripts.

Instead of writing .yml DSLs interpreted by some CI system, you write real code. Instead of writing ad-hoc Dockerfiles 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. 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 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.

how does it work?

To run a thunk, Bass's Buildkit runtime translates it to one big 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
$ git clone https://github.com/vito/bass
$ cd bass
$ make -j install

Bass runs thunks with Buildkit, 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.

$ ./hack/buildkit/start # if needed
$ bass ./demos/go-build-git.bass

macOS

macOS support works by just running Buildkit in a Linux VM.

Use the included lima/bass.yaml template to manage the VM with limactl.

$ brew install lima
$ limactl start ./lima/bass.yaml
$ bass ./demos/go-build-git.bass

Windows

Same as Linux, using WSL2. Windows containers should work once Buildkit supports it.

editor setup

Plug 'vito/bass.vim'

lua <<EOF
require'bass_lsp'.setup()
EOF

cleaning up

The Buildkit runtime leaves snapshots around for caching thunks, so if you start to run low on disk space you can run the following to clear them:

$ bass --prune

the name

Bass is named after the πŸ”Š, not the 🐟. Please do not think of the 🐟 every time. It will eventually destroy me.

rationale

motivation

After 6 years working on Concourse I felt pretty unsatisfied and burnt out. I wanted to solve CI/CD "once and for all" but ended up being overwhelmed with complicated problems that distracted from the core goal: database migrations, NP hard visualizations, scalability, resiliency, etc. etc. etc.

When it came to adding core features, it felt like building a language confined to a declarative YAML schema and driven by a complex state machine. So I wanted to try just building a damn language instead, since that's what I had fun with back in the day (Atomy, Atomo, Hummus, Cletus, Pumice).

why a new Lisp?

I think the pattern of YAML DSLs interpreted by DevOps services may be evidence of a gap in our toolchain that could be filled by something more expressive. I'm trying to discover a language that fills that gap while being small enough to coexist with all the other crap a DevOps engineer has to keep in their head.

After writing enterprise cloud software for so long, it feels good to return to the loving embrace of (((one thousand parentheses))). For me, Lisp is the most fun you can have with programming. Lisps are also known for doing a lot with a little - which is exactly what I need for this project.

Kernel's influence

Bass is a descendant of the Kernel programming language. Kernel is the tiniest Lisp dialect I know of - it has a primitive form beneath lambda called $vau (op in Bass) which it leverages to replace the macro system found in most other Lisp dialects.

Unfortunately this same mechanism makes Kernel difficult to optimize for production applications, but Bass targets a domain where its own performance won't be the bottleneck, so it seems like a good opportunity to share Kernel's ideas with the world.

Clojure's influence

Bass marries Kernel's semantics with Clojure's vocabulary and ergonomics, because you should never have to tell a coworker that the function to get the first element of a list is called πŸš—. A practical Lisp should be accessible to engineers from any background.

is it any good?

It's pretty close.

I'm using it for my projects and enjoying it so far, but there are still some limitations and rough edges.

project expectations

This project is built for fun and is developed in my free time. I'm just trying to build something that I would want to use for my own projects. I don't plan to bear the burden of large enterprises using it, but I'm interested in collaborating with and supporting hobbyists.

how can I help?

Try it out! I'd love to hear experience reports especially if things don't go well. This project is still young, and it only gets better the more it gets used.

Pull requests are very welcome, but this is still a personal hobby so I will probably push back on contributions that substantially increase the maintenance burden or technical debt (...unless they're wicked cool).

For more guidance, see the contributing docs.

thanks

  • John Shutt, creator of the Kernel programming language.
  • Rich Hickey, creator of the Clojure programming language.
  • The Buildkit project, which powers the default runtime.
  • MacStadium, who have graciously donated hardware for testing macOS support.

MacStadium logo

More Repositories

1

chyrp

The ultra-lightweight ultra-flexible blogging engine with a fetish for birds and misspellings.
PHP
231
star
2

go-repl

A Go REPL. Builds up a source .go file over time, compiles it for output.
Go
205
star
3

booklit

a pretty lit content authoring system
Go
173
star
4

atomy

a modular, macro-ular, totally tubular language for the Rubinius VM. #atomo @ freenode
Ruby
52
star
5

go-parse

A Parsec-like parsing library for Go.
Go
52
star
6

progrock

progress ui that rocks
Go
50
star
7

houdini

a no-op Garden backend
Go
45
star
8

cadet

a spatial approach to github issue networking and management
JavaScript
33
star
9

hummus

A dialect of the Kernel programming language.
Haskell
25
star
10

git-branch-heads-resource

(DEPRECATED) a Concourse resource for tracking changes across many branches
Shell
23
star
11

bass-loop

a continuous Bass service
JavaScript
20
star
12

go-play

Playing with the Go language.
Go
18
star
13

atomo

atomo programming language
Haskell
16
star
14

dagger-compose

Docker Compose... but in Dagger
Go
15
star
15

interact

Another interaction library in pure Ruby.
Ruby
14
star
16

twentythousandtonnesofcrudeoil

the front fell off
Go
13
star
17

chyrp-site

Chyrp's site. Powered by Chyrp.
PHP
13
star
18

atomo-old

A concurrent, object-oriented, functional programming language with a very strong type system. NOTE: Not to be confused with http://atomo-lang.org/ - same author, very different language; this one's dead.
Haskell
13
star
19

pumice

A dialect of the Kernel programming language, in RPython.
Python
10
star
20

vaultchuck

migrates a concourse pipeline from static credentials to vault
Go
9
star
21

language-racket

Racket and Scribble language support for Atom
9
star
22

elm-ansi

ANSI stream handling for Elm
Elm
8
star
23

boosh

boosh outer outer shell
Go
8
star
24

grafana-boshrelease

a BOSH release for the Grafana metrics dashboard
HTML
8
star
25

swirly

lets you get up close and personal with your dumps
JavaScript
7
star
26

tracksuit

github issue and tracker story syncer
Go
7
star
27

uploader

A tiny little uploader utility with password protection and an API.
JavaScript
7
star
28

gosub

go dependency submodule automator
Go
6
star
29

midterm

a pretty mid terminal emulator
Go
6
star
30

build-metadata-resource

caution: misuse may result in angry concourse developers
Shell
6
star
31

harry

automatically runs `make` when prerequisites change
Go
6
star
32

hello

HELLO !
Haskell
6
star
33

customs

dashboard showing progression from GitHub issues to Tracker stories
JavaScript
5
star
34

cloudformer

just one more layer of abstraction should do!
Go
5
star
35

sudoku.hs

A Sudoku puzzle solver written in Haskell.
Haskell
4
star
36

gocd-release

BOSH release for GoCD - http://go.cd
Shell
4
star
37

garden-systemd

a garden backend powered by systemd
Go
4
star
38

cloudfoundry-buildpack-bitcoin-miner

Shell
4
star
39

cletus

A dialect of the Kernel programming language. In Atomy!
Ruby
4
star
40

dotfiles

.
Shell
4
star
41

parby

A parser combinator library...for Ruby!
Ruby
4
star
42

dagql

an opinionated GraphQL server
Go
4
star
43

parsec

A parser combinator library in Scheme.
Scheme
3
star
44

go-interact

golang CLI interaction library
Go
3
star
45

dot-nvim

~/.config/nvim
Vim Script
3
star
46

go-sse

simple, tested Go interfaces for SSE per http://www.w3.org/TR/2012/CR-eventsource-20121211/
Go
3
star
47

mothership

big honkin' extensible command-line application library
Ruby
3
star
48

anatomy

Anatomy documentation system
CSS
3
star
49

daggerverse

a monorepo of all my Dagger modules
Clojure
3
star
50

git-branches-resource

tracks the state of branches in a git repository
Shell
3
star
51

clj-aliter

Clojure experimenting to see if it's a viable alternative to Erlang for our purpose.
Clojure
3
star
52

tabs

an unstable assortment of bass modules i need for my own projects
Clojure
3
star
53

garden-linux-docker-image

Builds a Docker image for running garden-linux.
Shell
2
star
54

executor-pool-spike

experimenting with self-organizing executor pools
Go
2
star
55

bass-loop-demo

A demo repo for Bass Loop.
2
star
56

slate-httpd

A small HTTP server in Slate.
2
star
57

cfv4

cf on concourse
2
star
58

gocart

Go Dependency Manager
Go
2
star
59

maestro

spikin' on junk
Go
2
star
60

doodle

rubinius documentation language and system
Ruby
2
star
61

lattice-concourse

scripts and stuff for running concourse on lattice
Shell
2
star
62

tree-sitter-bass

Scheme
1
star
63

dora

example app
Ruby
1
star
64

clouseau

Framework/runtime detection library.
Ruby
1
star
65

atomy-vim

Atomy bundle for Vim.
Vim Script
1
star
66

pidfile

manages a pidfile as an ifrit process
Go
1
star
67

markabee

A toy clone of _why's markaby. Not recommended for use by anyone, really. For educational purposes.
Ruby
1
star
68

atomy-tmbundle

Atomy TextMate bundles/etc.
1
star
69

i-fought-the-law-and-the-law-won

everything is terrible
Shell
1
star
70

ticketing

Elm
1
star
71

pipe2proj

converts a concourse pipeline into a concourse project
Go
1
star
72

room101

1
star
73

web-development

sigh
Shell
1
star
74

MusicPlayer

A little media player app for Haiku. Not the work of a professional.
C++
1
star
75

telegraf-agent-boshrelease

same as telegraf bosh release but with more properties
Shell
1
star
76

darcsden

an archive of my work on darcsden, which now runs hub.darcs.net
Haskell
1
star
77

slides

CSS
1
star
78

drone-vagrant

Ruby
1
star
79

node-tree-sitter-golang

node and golang sitting in a tree, p-a-r-s-i-n-g
C
1
star
80

atom-autocomplete-gocode

gocode integration for autocomplete+
CoffeeScript
1
star
81

drone-release

janky drone release
Shell
1
star
82

dotfiles-very-old

My configuration dotfiles.
Vim Script
1
star
83

atomo-anatomy

atomo documentation tool
Haskell
1
star
84

warden-linux

warden-linux, but with a winston .yml
Go
1
star
85

consul-release

BOSH release for Consul - http://consul.io
Shell
1
star
86

invaders

a Go library for generating unique space invader aliens
Go
1
star
87

broomhlda

a syntax highlighting lib in Atomy
Python
1
star