The Wizard of Sh

… or how Plan9 inspired me to create my own Installation Wizard.

Recently I had the urge to create yet another project. And as any person who is opinionated about computers, programming, or computer programming in general, I have several ideas floating in the back of my mind.

One of those ideas is blatantly ripped of inspired by the installation process of the Plan9 operating system. The topic of Plan9 would deserve it’s own series of blogposts, so I’ll focus on what matters right for this specific one. When you install Plan9 you are greeted by the window manager rio, which contains two important shell windows. One displays the current tasks which you can choose by clicking on the name. The other contains descriptions and granular steps to complete the task. For the most part you just have to choose some kind of implementation, like which file system you want to format the drive with. The prompt displays the options in a list denoted with angled brackets. When you just press enter, the first option in the list is selected, otherwise you can type out the entire name. Sometimes you also have the choice to finish a task yourself by typing ! and executing the appropriate commands in an interactive shell. After a task is finished, the list in the other window updates automatically and displays tasks that depended on the previously finished.

I decided to implement something similar using Rust and a simple YAML file format to define tasks. And because this tool was inspired by a installation wizard is it only natural to use a wizardry naming scheme! 🧙 Therefore, I’d like to introduce you to the mage tool and it’s corresponding spellbook file format, the Magefile.1 A Magefile is a simple yaml file containing tasks. A task is an arbitrarily named map[^js] that contains the scripts field, which itself is a list of strings that are interpreted as shell commands. The user then selects one of those scripts interactively when executing the task.

An example:

hello world:
  scripts:
  - echo hello world

another task:
  scripts:
  - "false"
  - exit 1
  - "true"

This does nothing meaningful in itself, but demonstrates the layout of the Magefile. Of course, you can also specify that a task depends on another with a special depends_on key. The mage tool then lets you select a task that has all it’s dependencies fulfilled and you get to choose which script you want to run to solve the task.

There still are some features that I want to implement. For one, it would be nice if you could share environment variables instead of falling back to the file system. It would also be nice if you could specify a list of steps instead of alternatives. Ultimately you should be able to specify pre- and postconditions and maybe drop down to a shell to solve a task yourself. A not-goal is to automatically finish each task like a Make script. You wouldn’t need a new tool for that.

The implementation of mage uses the petgraph library and cool terminal interfaces form dialouger so it’s really nice to use! You can check it out at gitlab or install it with cargo through cargo install novice2.


  1. This description documents the behaviour of the early alpha version. It may be different on the current release.
  2. The crate-name mage was already reserved, so I had to settle for another one :/