{"id":16918281,"url":"https://github.com/vito/bass","last_synced_at":"2025-05-15T08:05:19.589Z","repository":{"id":37087197,"uuid":"379138786","full_name":"vito/bass","owner":"vito","description":"a low fidelity scripting language for project infrastructure","archived":false,"fork":false,"pushed_at":"2025-03-14T03:38:18.000Z","size":29899,"stargazers_count":402,"open_issues_count":22,"forks_count":13,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-04-14T13:08:26.371Z","etag":null,"topics":["build","buildkit","language","lisp"],"latest_commit_sha":null,"homepage":"https://bass-lang.org","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vito.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-06-22T04:15:01.000Z","updated_at":"2025-04-13T15:58:00.000Z","dependencies_parsed_at":"2024-02-23T20:27:31.376Z","dependency_job_id":"ba6a35d6-0096-4c17-92c6-2b3ad5a5739c","html_url":"https://github.com/vito/bass","commit_stats":{"total_commits":1567,"total_committers":12,"mean_commits":"130.58333333333334","dds":0.01212507977026167,"last_synced_commit":"d52cd641b6aabe237482f13e73a3a79487fd6c00"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vito%2Fbass","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vito%2Fbass/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vito%2Fbass/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vito%2Fbass/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vito","download_url":"https://codeload.github.com/vito/bass/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254301431,"owners_count":22047904,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build","buildkit","language","lisp"],"created_at":"2024-10-13T19:39:28.691Z","updated_at":"2025-05-15T08:05:19.543Z","avatar_url":"https://github.com/vito.png","language":"Go","readme":"# bass\n\n[![Discord](https://img.shields.io/discord/939941216215240774?color=7389D8\u0026label=\u0026logo=discord\u0026logoColor=ffffff\u0026labelColor=6A7EC2)](https://discord.gg/HFW85RyUtK)\n\nBass is a low-fidelity Lisp dialect for the glue code driving your project.\n\nhttps://github.com/vito/bass/assets/1880/ab05445c-95f7-44b6-a67b-d9fc8eb02d41\n\n## reasons you might be interested\n\n* you're sick of YAML and want to write code instead of config and templates\n* you'd like to have a uniform stack between dev and CI\n* you'd like be able to audit or rebuild published artifacts\n* you're nostalgic about Lisp\n\n## what the thunk?\n\nBass is a functional language for scripting commands, represented by\n[thunks][t-thunk]. A thunk is a serializable recipe for a command to run,\nincluding all of its inputs, and their inputs, and so on. ([Why are they called\nthunks?][why-thunks])\n\nThunks lazily run their command to produce a `stdout` stream, an output\ndirectory, and an exit status. These results are cached indefinitely, but only\nwhen the command succeeds.\n\n```clojure\n$ bass\n=\u003e (from (linux/alpine) ($ cat *dir*/README.md))\n\n\n        ██████\n      ██████████\n    ██████████████\n    ████  ██  ████\n    ██████████████\n    ██████  ██████\n  ████    ██    ████\n    ████      ████\n\n\u003cthunk JU61UMJQ70FMI: (.cat)\u003e\n=\u003e (thunk? (from (linux/alpine) ($ cat *dir*/README.md)))\ntrue\n```\n\nTo run a thunk and raise an error if the command fails, use [`(run)`][b-run].\nTo get `true` or `false` instead, use [`(succeeds?)`][b-succeeds].\n\n```clojure\n=\u003e (def thunk (from (linux/alpine) ($ echo \"Hello, world!\")))\nthunk\n=\u003e (run thunk)\n; Hello, world!\nnull\n=\u003e (succeeds? thunk)\ntrue\n=\u003e (succeeds? (from (linux/alpine) ($ sh -c \"exit 1\")))\nfalse\n```\n\nTo access a thunk's output directory, use a [thunk path][t-thunk-path]. Thunk\npaths can be passed to other thunks. Filesystem timestamps in thunk paths are\nnormalized to `1985-10-06 08:15 UTC` to support reproducible builds.\n\n```clojure\n=\u003e (def thunk (from (linux/alpine) ($ cp *dir*/README.md ./some-file)))\nthunk\n=\u003e (run (from (linux/alpine) ($ head \"-1\" thunk/some-file)))\n; # bass\nnull\n```\n\nTo parse values from a thunk's `stdout` or from a thunk path, use\n[`(read)`][b-read].\n\n```clojure\n=\u003e (next (read (from (linux/alpine) ($ head \"-1\" thunk/some-file)) :raw))\n\"# bass\\n\"\n=\u003e (next (read thunk/some-file :lines))\n\"# bass\"\n=\u003e (next (read thunk/some-file :unix-table))\n(\"#\" \"bass\")\n```\n\nTo serialize a thunk or thunk path to JSON, use [`(json)`][b-json] or\n[`(emit)`][b-emit] it to `*stdout*`. Pipe a thunk path to `bass --export | tar\n-xf -` to extract it, or pipe a thunk to `bass --export | docker load` to\nexport a thunk to Docker.\n\n```sh\n$ ./bass/build -i src=./ | bass --export | tar -xf -\n$ ls bass.linux-amd64.tgz\n```\n\nThis, and generally everything, works best when your thunks are\n[hermetic][t-hermetic].\n\n#### tl;dr\n\nIt's a bit of a leap, but I like to think of Bass as a purely functional,\nlazily evaluated Bash.\n\nInstead of running commands that mutate machine state, Bass has a read-only\nview of the host machine and passes files around as values in ephemeral,\nreproducible filesystems addressed by their creator thunk.\n\n[b-read]: https://bass-lang.org/stdlib.html#binding-read\n[b-run]: https://bass-lang.org/stdlib.html#binding-run\n[b-succeeds]: https://bass-lang.org/stdlib.html#binding-succeeds?\n[b-json]: https://bass-lang.org/stdlib.html#binding-json\n[b-emit]: https://bass-lang.org/stdlib.html#binding-emit\n\n## example\n\nRunning a [thunk][t-thunk]:\n\n```clojure\n(def thunk\n  (from (linux/ubuntu)\n    ($ echo \"Hello, world!\")))\n\n(run thunk)\n```\n\nPassing [thunk paths][t-thunk-path] around:\n\n```clojure\n; use git stdlib module\n(use (.git (linux/alpine/git)))\n\n; returns a thunk dir containing compiled binaries\n(defn go-build [src pkg]\n  (subpath\n    (from (linux/golang)\n      (cd src\n        ($ go build -o ./built/ $pkg)))\n    ./built/))\n\n(defn main []\n  (let [src git:github/vito/booklit/ref/master/\n        bins (go-build src \"./cmd/...\")]\n    ; kick the tires\n    (run (from (linux/ubuntu)\n           ($ bins/booklit --version)))\n\n    (emit bins *stdout*)))\n```\n\n### irl examples\n\n* [Bass](bass/)\n* [Booklit](https://github.com/vito/booklit/tree/master/bass)\n\n## what's it for?\n\nBass typically replaces CI `.yml` files, `Dockerfile`s, and Bash scripts.\n\nInstead of writing `.yml` DSLs interpreted by some CI system, you write real\ncode. Instead of writing ad-hoc `Dockerfile`s and pushing/pulling images, you\nchain thunks and share them as code. Instead of writing Bash scripts, you write\nBass scripts.\n\nBass scripts have limited access to the host machine, making them portable\nbetween dev and CI environments. They can be used to codify your entire\ntoolchain into platform-agnostic scripts.\n\nIn the end, the purpose of Bass is to run [thunks][t-thunk]. Thunks are\nserializable command recipes that produce files or streams of values. Files\ncreated by thunks can be easily passed to other thunks, forming one big\nsuper-thunk that recursively embeds all of its dependencies.\n\nBass is designed for [hermetic][t-hermetic] builds but it stops short of\nenforcing them. Bass trades purism for pragmatism, sticking to familiar albeit\nfallible CLIs rather than abstract declarative configuration. For your\nartifacts to be reproducible your thunks must be hermetic, but if you simply\ndon't care yet, YOLO `apt-get` all day and fix it up later.\n\nFor a quick run-through of these ideas, check out the [Bass homepage][bass].\n\n[bass]: https://bass-lang.org\n[llb]: https://github.com/moby/buildkit/blob/master/docs/solver.md\n[t-thunk]: https://bass-lang.org/bassics.html#term-thunk\n[t-thunk-path]: https://bass-lang.org/bassics.html#term-thunk%20path\n[t-hermetic]: https://bass-lang.org/bassics.html#term-hermetic\n\n### how does it work?\n\nTo run a thunk, Bass's [Buildkit][buildkit] runtime translates it to one big\n[LLB][llb] definition and solves it through the client API. The runtime handles\nsetting up mounts and converting thunk paths to string values passed to the\nunderlying command.\n\nThe runtime architecture is modular, but Buildkit is the only implementation at\nthe moment.\n\n\n## start playing\n\n* prerequisites: `git`, `go`, `upx`\n\n```sh\n$ git clone https://github.com/vito/bass\n$ cd bass\n$ make -j install\n```\n\nBass runs thunks with [Buildkit][buildkit-quickstart], so you'll need\n`buildkitd` running somewhere, somehow.\n\nIf `docker` is installed and running Bass will use it to start Buildkit\nautomatically and you can skip the rest of this section.\n\n### Linux\n\nThe included `./hack/buildkit/` scripts can be used if you don't have\n`buildkitd` running already.\n\n```sh\n$ ./hack/buildkit/start # if needed\n$ bass ./demos/go-build-git.bass\n```\n\n[buildkit-quickstart]: https://github.com/moby/buildkit#quick-start\n\n### macOS\n\nmacOS support works by just running Buildkit in a Linux VM.\n\nUse the included [`lima/bass.yaml`](lima/bass.yaml) template to manage the VM\nwith [`limactl`][lima].\n\n```sh\n$ brew install lima\n$ limactl start ./lima/bass.yaml\n$ bass ./demos/go-build-git.bass\n```\n\n[lima]: https://github.com/lima-vm/lima\n\n### Windows\n\nSame as Linux, using WSL2. Windows containers should work [once Buildkit\nsupports it][windows].\n\n[windows]: https://github.com/moby/buildkit/issues/616\n\n## editor setup\n\n* vim config: [bass.vim][bass.vim]\n\n[bass.vim]: https://github.com/vito/bass.vim\n\n```vim\nPlug 'vito/bass.vim'\n\nlua \u003c\u003cEOF\nrequire'bass_lsp'.setup()\nEOF\n```\n\n## cleaning up\n\nThe Buildkit runtime leaves snapshots around for caching thunks, so if you\nstart to run low on disk space you can run the following to clear them:\n\n```\n$ bass --prune\n```\n\n\n## the name\n\nBass is named after the :loud_sound:, not the :fish:. Please do not think of\nthe :fish: every time. It will eventually destroy me.\n\n\n## rationale\n\n### motivation\n\nAfter 6 years working on [Concourse][concourse] I felt pretty unsatisfied and\nburnt out. I wanted to solve CI/CD \"once and for all\" but ended up being\noverwhelmed with complicated problems that distracted from the core goal:\ndatabase migrations, NP hard visualizations, scalability, resiliency, etc. etc.\netc.\n\nWhen it came to adding core features, it felt like building a language confined\nto a declarative YAML schema and driven by a complex state machine. So I wanted\nto try just building a damn language instead, since that's what I had fun with\nback in the day ([Atomy][atomy], [Atomo][atomo], [Hummus][hummus],\n[Cletus][cletus], [Pumice][pumice]).\n\n### why a new Lisp?\n\nI think the pattern of YAML DSLs interpreted by DevOps services may be evidence\nof a gap in our toolchain that could be filled by something more expressive.\nI'm trying to discover a language that fills that gap while being small enough\nto coexist with all the other crap a DevOps engineer has to keep in their head.\n\nAfter writing enterprise cloud software for so long, it feels good to return to\nthe loving embrace of `(((one thousand parentheses)))`. For me, Lisp is the\nmost fun you can have with programming. Lisps are also known for doing a lot\nwith a little - which is exactly what I need for this project.\n\n### Kernel's influence\n\nBass is a descendant of the [Kernel programming language][kernel]. Kernel is\nthe tiniest Lisp dialect I know of - it has a primitive form _beneath_\n`lambda` called `$vau` ([`op`][b-op] in Bass) which it leverages to replace the\nmacro system found in most other Lisp dialects.\n\nUnfortunately this same mechanism makes Kernel difficult to optimize for\nproduction applications, but Bass targets a domain where its own performance\nwon't be the bottleneck, so it seems like a good opportunity to share Kernel's\nideas with the world.\n\n[b-op]: https://bass-lang.org/stdlib.html#binding-op\n\n### Clojure's influence\n\nBass marries Kernel's semantics with Clojure's vocabulary and ergonomics,\nbecause you should never have to tell a coworker that the function to get the\nfirst element of a list is called :car:. A practical Lisp should be accessible\nto engineers from any background.\n\n[arrow]: https://bass-lang.org/stdlib.html#binding--%3e\n[t-operative]: https://bass-lang.org/bassics.html#term-operative\n\n\n## is it any good?\n\nIt's pretty close.\n\nI'm using it for my projects and enjoying it so far, but there are still some\nlimitations and rough edges.\n\n\n## project expectations\n\nThis project is built for fun and is developed in my free time. I'm just trying\nto build something that I would want to use for my own projects. I don't plan\nto bear the burden of large enterprises using it, but I'm interested in\ncollaborating with and supporting hobbyists.\n\n\n## how can I help?\n\nTry it out! I'd love to hear [experience reports][discussions] especially if\nthings don't go well. This project is still young, and it only gets better the\nmore it gets used.\n\nPull requests are very welcome, but this is still a personal hobby so I will\nprobably push back on contributions that substantially increase the maintenance\nburden or technical debt (...unless they're wicked cool).\n\nFor more guidance, see the [contributing docs](CONTRIBUTING.md).\n\n[discussions]: https://github.com/vito/bass/discussions\n\n\n## thanks\n\n* John Shutt, creator of the [Kernel] programming language.\n* Rich Hickey, creator of the [Clojure] programming language.\n* The [Buildkit project][buildkit], which powers the default runtime.\n* [MacStadium], who have graciously donated hardware for testing macOS support.\n\n\u003cimg alt=\"MacStadium logo\" src=\"https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png\" width=\"200\" /\u003e\n\n[MacStadium]: https://www.macstadium.com/\n[kernel]: https://web.cs.wpi.edu/~jshutt/kernel.html\n[clojure]: https://clojure.org/\n\n[go]: https://golang.org\n[concourse]: https://github.com/concourse/concourse\n[oci]: https://github.com/opencontainers/image-spec\n[atomo]: https://github.com/vito/atomo\n[atomy]: https://github.com/vito/atomy\n[pumice]: https://github.com/vito/pumice\n[cletus]: https://github.com/vito/cletus\n[hummus]: https://github.com/vito/hummus\n[resources]: https://concourse-ci.org/resources.html\n[tasks]: https://concourse-ci.org/tasks.html\n[jq]: https://stedolan.github.io/jq/\n[concourse-types]: https://resource-types.concourse-ci.org/\n[json]: https://www.json.org/\n[streams]: https://en.wikipedia.org/wiki/Standard_streams\n[buildkit]: https://github.com/moby/buildkit\n[booklit-test]: https://github.com/vito/booklit/blob/master/ci/test.yml\n[booklit-build]: https://github.com/vito/booklit/blob/master/ci/build.yml\n[why-thunks]: https://github.com/vito/bass-loop/discussions/4\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvito%2Fbass","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvito%2Fbass","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvito%2Fbass/lists"}