ShellPack: Bash Dependency Resolver & Bundler Prototype

Shell Bash Dependency-Resolver Bundler

Intro

I came up recently with ShellPack, a prototype tool to fetch and optionally bundle Bash scripts. I’m using it now asdf-orb, checkout-orb and shell-gr, and it is a such a relief.

I’m eager to hear your thoughts, assuming you’re familiar with Bash 😊

Body

Bash scripting can be challenging. It is not my language of choice but truth is Bash or sh is present on every Unix-like operating system, and sometimes it is the only viable option. One of my major headaches with Bash is that you can share code snippets between projects, at least not easily. You could share some bits using submodules, but it is a bit cumbersome.

Recently I worked on CircleCI orbs to share some of my configs and scripts across projects, and Bash is a must there. Moreover, CircleCI expects from you a single Bash file per command. Not only it has to be Bash, but it has to be Bash in one file. Enough is enough. There has to be a way out of this situation, I thought. And then I came up with an idea, and subsequently a prototype of a Bash bundler. Next, it transformed into lightweight dependency resolver fetching script pieces from the web when requested in the file, and bundling into one file if needed. If not, just saving them locally.

The basic idea is relatively simple: leverage source to gather information about what is needed and at the same where is located. I’ll give you an example:

#!/bin/bash

SHELL_GR_DIR="${ROOT_DIR}/.github_deps/rynkowsg/shell-gr@v0.1.0"
source "${SHELL_GR_DIR}/lib/trap.bash"

main() {
  filepath="$(mktemp -t "temp-$(date +%Y%m%d_%H%M%S)-XXXXX")"
  add_on_exit "rm -f ${filepath}"
  # do some important bits with the file...
}

main

add_on_exit is provided from repository shell-gr. The tool reads through the scripts and if encounter source decides to what to do with it. If the path contains certain tokens, like .github_deps here, it is assessed as a remote dependency.

The idea is to leverage source to gather information about what is needed and at the same where is located. The dependency remote location is conflated with local location. From path take information where to find the dependency file and where to save it.

With this little constrain (having specific source path for remote dependencies), the tool can scan the script and fetch what we need (including transitive dependencies if they meet the same constrain - expected source path format). Then we can pack all into one file or just run it normally with Bash wihtout packing.

For more complete example you can look att src/scripts/install_asdf.bash and Makefile at asdf-orb repo:

At this point it is implemented as a Babashka script, but if it sounds like a good idea I could release it as a binary with GraalVM with relatively little effort.

Final

I’d love to hear your thoughts. How do you manage code reusability in your scripts anyway? Sounds like something you would use?

This is only a static page, so if you like to comment please do under this LinkedIn post, this Twitter/X thread or simply email me.

Yours sincerely.