Stop Writing Messy Bash Scripts: Build Real Internal Tools with Mush

In the DevOps world, internal tooling is not a luxury—it's a necessity. Whether you're deploying to Kubernetes, releasing software, or bootstrapping new services, custom scripts often bridge the gap between teams, tools, and infrastructure. But let's face it: internal scripts tend to start as quick-and-dirty Bash hacks, and over time, they become unreadable, unmaintainable, and fragile. Sound familiar? What if we could treat Bash like a real programming language, with structure, modules, versioning, and installable tools? That’s where Mush comes in. The State of Internal Tooling in DevOps Almost every DevOps team builds their own tools. Here are some real-world examples: Tool Name What It Does deploy.sh Deploy a service with custom settings release.sh Create a new release and update changelogs check-env Verify if the developer machine has the required dependencies onboard.sh Set up a new repo with templates and CI/CD kube-wrapper Simplify Kubernetes commands across namespaces and clusters git-policies Enforce Git commit rules and tag naming These tools often grow from a one-liner in a README to a critical script used by the entire team—yet we rarely invest in making them maintainable. Why Bash Scripts Get Ugly No module system (copy-paste culture) Zero discoverability (--help? What help?) Hard to test Impossible to version cleanly No proper CLI structure or autocompletion Unclear dependencies What if we could fix all of that—without leaving Bash? Meet Mush: Modular Shell Scripting Mush is a lightweight ecosystem that gives your Bash scripts: A clear project structure (src/, tests/, mush.yaml) Modular architecture (module primitive to load other scripts) Built-in CLI command dispatching Automatic --help generation Dependency management via Git Easy install and distribution (mush build, mush install) Think of it like Cargo for Shell. A Real Example: Hello, Bash Tool Let’s build a simple CLI tool using Mush. mush new hello-bash cd hello-bash Create your main file: # src/main.sh main() { echo "Hello, Bash World!" } Your config file: # Manifest.toml name: hello version: 0.1.0 Build and run: mush build --release ./bin/hello Result: $ ./bin/hello Hello, Bash World! You now have a real executable, with structure, dispatch, and versioning—using only Bash. Internal DevOps Tooling with Mush Let’s imagine some real DevOps use cases: 1. A CLI to Standardize Deployments # src/deploy.sh module utils devops::deploy() { env="$1" utils::banner "Deploying to $env..." kubectl apply -f k8s/$env.yaml } ./devops deploy staging Now you have devops deploy, devops help, devops version... all for free. 2. Kubernetes Wrapper Tool kubetool logs app-name --env prod kubetool switch-context dev kubetool apply-all teamX Under the hood: one script per command in src/, loaded with module and structured like a Go or Rust CLI. 3. Project Onboarding Tool onboard new-service Clones a template repo Sets up CI/CD Creates GitHub repo Registers service in monitoring Organized as: src/ ├── main.sh ├── git.sh ├── ci.sh ├── register.sh Reused across teams. Versioned. Maintained. Documented. Why Mush Matters For DevOps teams, Mush is a game changer: No more spaghetti Bash Encourages structure and reuse Teams can collaborate on shared modules Tools can be versioned, installed, and reused Easy to write, even easier to maintain Mush brings modern software development practices to one of the most widely used languages in infrastructure—Bash. Final Thoughts Every time you write a script like deploy.sh, you’re building a tool. Why not treat it like one? Mush lets you build real, structured, maintainable tools—with zero dependencies and 100% Bash. So next time your team needs a CLI helper, skip the Python and Go boilerplate. Use Mush, and make your scripts proud. Try it out GitHub: github.com/javanile/mush brew install javanile/tap/mush mush init my-tool && cd my-tool && mush build Happy scripting!

Apr 23, 2025 - 00:08
 0
Stop Writing Messy Bash Scripts: Build Real Internal Tools with Mush

In the DevOps world, internal tooling is not a luxury—it's a necessity. Whether you're deploying to Kubernetes, releasing software, or bootstrapping new services, custom scripts often bridge the gap between teams, tools, and infrastructure.

But let's face it: internal scripts tend to start as quick-and-dirty Bash hacks, and over time, they become unreadable, unmaintainable, and fragile. Sound familiar?

What if we could treat Bash like a real programming language, with structure, modules, versioning, and installable tools?

That’s where Mush comes in.

The State of Internal Tooling in DevOps

Almost every DevOps team builds their own tools. Here are some real-world examples:

Tool Name What It Does
deploy.sh Deploy a service with custom settings
release.sh Create a new release and update changelogs
check-env Verify if the developer machine has the required dependencies
onboard.sh Set up a new repo with templates and CI/CD
kube-wrapper Simplify Kubernetes commands across namespaces and clusters
git-policies Enforce Git commit rules and tag naming

These tools often grow from a one-liner in a README to a critical script used by the entire team—yet we rarely invest in making them maintainable.

Why Bash Scripts Get Ugly

  • No module system (copy-paste culture)
  • Zero discoverability (--help? What help?)
  • Hard to test
  • Impossible to version cleanly
  • No proper CLI structure or autocompletion
  • Unclear dependencies

What if we could fix all of that—without leaving Bash?

Meet Mush: Modular Shell Scripting

Mush is a lightweight ecosystem that gives your Bash scripts:

  • A clear project structure (src/, tests/, mush.yaml)
  • Modular architecture (module primitive to load other scripts)
  • Built-in CLI command dispatching
  • Automatic --help generation
  • Dependency management via Git
  • Easy install and distribution (mush build, mush install)

Think of it like Cargo for Shell.

A Real Example: Hello, Bash Tool

Let’s build a simple CLI tool using Mush.

mush new hello-bash
cd hello-bash

Create your main file:

# src/main.sh
main() {
  echo "Hello, Bash World!"
}

Your config file:

# Manifest.toml
name: hello
version: 0.1.0

Build and run:

mush build --release
./bin/hello

Result:

$ ./bin/hello
Hello, Bash World!

You now have a real executable, with structure, dispatch, and versioning—using only Bash.

Internal DevOps Tooling with Mush

Let’s imagine some real DevOps use cases:

1. A CLI to Standardize Deployments

# src/deploy.sh
module utils

devops::deploy() {
  env="$1"
  utils::banner "Deploying to $env..."
  kubectl apply -f k8s/$env.yaml
}
./devops deploy staging

Now you have devops deploy, devops help, devops version... all for free.

2. Kubernetes Wrapper Tool

kubetool logs app-name --env prod
kubetool switch-context dev
kubetool apply-all teamX

Under the hood: one script per command in src/, loaded with module and structured like a Go or Rust CLI.

3. Project Onboarding Tool

onboard new-service
  • Clones a template repo
  • Sets up CI/CD
  • Creates GitHub repo
  • Registers service in monitoring

Organized as:

src/
├── main.sh
├── git.sh
├── ci.sh
├── register.sh

Reused across teams. Versioned. Maintained. Documented.

Why Mush Matters

For DevOps teams, Mush is a game changer:

  • No more spaghetti Bash
  • Encourages structure and reuse
  • Teams can collaborate on shared modules
  • Tools can be versioned, installed, and reused
  • Easy to write, even easier to maintain

Mush brings modern software development practices to one of the most widely used languages in infrastructure—Bash.

Final Thoughts

Every time you write a script like deploy.sh, you’re building a tool.

Why not treat it like one?

Mush lets you build real, structured, maintainable tools—with zero dependencies and 100% Bash.

So next time your team needs a CLI helper, skip the Python and Go boilerplate. Use Mush, and make your scripts proud.

Try it out

Happy scripting!