Tool Overview


Much of what compose.mk does is focused on helping you to generate automation scaffolding, but that approach requires a pre-existing project and some project integration.

In Stand-alone Mode, you can still use many of those features but skip the usual project integration. In other words, project Makefiles, project docker-compose files, etc become optional. This is less powerful and less flexible, but still results in a neat little swiss army knife that has some unique capabilities. Most use-cases for stand-alone mode will fall into one of these categories:

  1. Wrapped versions of common tools, so that they are available by default in more environments without adding more dependencies. For example, ./compose.mk jq defaults to using local executable if one is available, and falls back to a dockerized version of the tool otherwise. See the tool wrappers section of this page for a more complete manifest.
  2. General bash kung-fu, which might help clean up other scripts or can be used interactively. In many cases, compose.mk idioms are easier to read and write. For examples, see here, here, etc, and refer to the full target API.
  3. Runner interface for interactive-selection for make-targets or compose-containers. See the separate documentation & demos here.
  4. Loading compose files just-in-time. Separate documentation here, but briefly.. this gives access to all of the scaffolded targets, and if no downstream targets are given, the default behaviouir is to open interactive shells for each tool container in a TUI.

Tool Wrappers


Some tools are so useful that we'd really like to assume they are available everywhere. How can that happen though if we're not sure whether they are installed? To address this, compose.mk publishes several tool-wrappers that default to using a local executable if one is available, and falls back to a dockerized version of the tool otherwise. (In case you end up using dockerized tools, they are pulled just in time but cached afterwards.)

A few examples of tools that compose.mk wraps in one way or another include:

  • jq, for querying JSON
  • jb, for constructing JSON
  • yq, for querying yaml and other stuff
  • charmbracelet/glow, for rendering markdown on console
  • charmbracelet/gum, for a variety of pretty text-formatting tricks
  • pygments, for syntax highlighting data or code
  • chafa, for rendering images on console
  • tmux and friends, for console geometry management (see also: embedded tui)
  • makeself for creating self-extracting archives (see also: packaging)

Proxy Wrappers


The first 3 tools jq, jb, and yq are the most generally useful. They all have extensive documentation elsewhere to cover what they do and how to use them, and mostly compose.mk tries to stay out the way.

For tools like these, compose.mk generally takes the approach of using it directly if possible, and then transparently falls back to docker if the tool is missing.. Calling them with compose.mk looks like this:

# Generate JSON with jb
$ ./compose.mk jb key=val
{"key":"val"}

# Generate JSON with jb (alternate)
$ echo key=val | ./compose.mk jb
{"key":"val"}

# Parse JSON with jq
$ ./compose.mk jb key=val | ./compose.mk jq .key
"val"

# Passing arguments to jq works, here is an unquoted result
$ ./compose.mk jb key=val | ./compose.mk jq -r .key
val

# Now try yaml input, with yq
$ echo "key: val" | ./compose.mk yq .key
val

The "wrapper" part is easy enough to see from the examples above, just wrapping familiar invocations with compose.mk. But what exactly is being "proxied" here? Well, the eagle-eyed reader will have noticed that it's kind of crazy that this actually works, because behind the scenes running ./compose.mk ... is roughly the same as make -f compose.mk ..., and so you'd expect that the tail part of the command line is arguments to make. And yet.. there is no make-target called .key.

Under the hood, invocations for proxy-wrappers begin with an actual make target, but before make parses beyond that, execution is short-circuited, and the rest of the command-line is consumed and passed on to the tool. For details about how this works, see the documentation for signals.

Caveats

Proxy-wrappers are very useful for simple stuff, but the wrapped tools jq, yq, and jb all have big surface area with a lot of edge-cases for optional arguments, loading files, with or without pipes. If your use-case involves lots of flags or nested quotes or something, maybe don't assume that the wrapped invocation is 1:1 with the original tool. There's only so much that can be done here for the standalone-mode for compose.mk, but note that library-style usage is more flexible.

Other Wrappers


Setting aside the big 3 tools that use proxy-wrappers, other wrappers are simpler because compose.mk gets tactical and won't even try to expose a 1:1 interface. Sometimes we accept arguments, but mostly we just pass data on pipes.

You can see a few examples below, but this isn't an exhuastive list. See also the documentation for streams and pipes.

Summary
#!/usr/bin/env -S bash -x -euo pipefail
# tests/tool-wrappers.sh:
#   Exercise some of the tool-wrappers that are part of stand-alone mode.
#
# Part of the `compose.mk` repo. This file runs as part of the test-suite.
#
# USAGE: bash -x tests/tool-wrappers.sh

# Use `stream.chafa` to preview an image on the console with chafa
cat docs/img/icon.png | ./compose.mk stream.chafa

# Use `stream.chafa` to preview an image on the console with chafa (alternate)
cat docs/img/icon.png | ./compose.mk stream.img.preview

# Preview image without streams
./compose.mk io.preview.img/docs/img/icon.png

# Preview multiple images
find docs/img/icon.png | ./compose.mk flux.each/io.preview.img

# Use `stream.glow` to preview markdown
cat README.md | ./compose.mk stream.glow

# Use `stream.glow` to preview markdown (alternate)
cat README.md | ./compose.mk stream.markdown

# Use `stream.pygmentize` to syntax-highlight code 
# This uses pygments, default style, and a best-guess lexer
cat Makefile | ./compose.mk stream.pygmentize

# Use `stream.pygmentize` to syntax-highlight code 
# This uses pygments with an explicit lexer and default style
cat Makefile | lexer=Makefile ./compose.mk stream.pygmentize 

# Use `stream.pygmentize` to syntax-highlight code
# This uses pygments with an explicit style and lexer
cat Makefile | style=monokai lexer=Makefile ./compose.mk stream.pygmentize 

# # Use `stream.json.pygmentize` to preview JSON (minified)
./compose.mk jb key=val | ./compose.mk stream.json.pygmentize

# Use `stream.json.pygmentize` to preview JSON (expanded)
./compose.mk jb key=val | ./compose.mk jq . | ./compose.mk stream.json.pygmentize

# Use `stream.peek` to preview data.
# Put this somewhere in the middle of a pipe
./compose.mk jb key=val | ./compose.mk stream.peek | ./compose.mk jq .

# Pull data from yaml with yq
echo 'one: two' | ./compose.mk yq .one

Many of these targets are also available as macros, and might have more documentation available as part of the library overview. See also the API reference.

For stuff related to tmux, see the separate documentation for the embedded TUI. For a demo involving chafa, see this workflow example or the notebooking demo. Many demos use jq and jb, but see especially the documentation for structured IO and the documentation for stages.

References



  1. If you're wondering how and why stand-alone mode can work in the first place: since compose.mk is executable, ./compose.mk ... is roughly equivalent to make -f compose.mk ...