Document your steps through scripts
Step 1. Step 2. Step 3…and then sometimes you’re done…setting up your workstation, pulling some data from your phone, or trying to bring up a blog. Sometimes not, sometimes there are more steps. Why not write a script instead?
A script can both document the steps and provide a way for the computer to run them for you. The disadvantage of writing a script is the time it takes to do so.
One strategy I’ve found is to iteratively develop the script over a few runs of the same steps. I’ll start out with a script that is mostly comments or just a URL reference to the steps. Then flush it out making many hardcoded assumptions. Then I’ll parameterize it and flush it out more. Each time I work on the script I’ll devote only as much time as I’m willing to: 5 minutes, 10 minutes, 30 minutes, and then move on with my day. Eventually I have a script.
This is the strategy I used to write the script to configure my machine for blogging.
Example
To make this post more concrete, I’ll walk through creating a part of my configure-machine-for-blogging script from scratch.
First I open my browser to https://jekyllrb.com/docs/installation/macos.
Then I create a new text file with:
#!/bin/sh
xcode-select --install
Boom! I keep myself focused on writing the script just for me and my machine:
#!/bin/sh
xcode-select --install
export SDKROOT=$(xcrun --show-sdk-path)
Now I run my script. Next I hit section Install Ruby and read through it. Then I amend my script to ignore the output of “xcode-select” and check if “ruby” is in my PATH because the latter is the invariant:
#!/bin/sh
xcode-select --install >/dev/null 2>&1
if ! which ruby >/dev/null 2>&1; then
echo "Couldn't find ruby. Is it installed?" >&2
exit 1
fi
export SDKROOT=$(xcrun --show-sdk-path)
By the way, I redirect my “echo” output to standard error (>&2
) since such a message reads like an error. This also future proofs the script for use in a pipeline.
Continuing, I skip some sections that don’t apply to my machine and get to the Install Jekyl section:
It tells me to install Jekyll. Then get my Ruby version to build the PATH to export, but it only uses the major and minor version numbers. I chose to compute that version string and after reading the Ruby API docs came up with a Ruby script and encoded that into my shell script:
#!/bin/sh
xcode-select --install >/dev/null 2>&1
if ! which ruby >/dev/null 2>&1; then
echo "Couldn't find ruby. Is it installed?" >&2
exit 1
fi
export SDKROOT=$(xcrun --show-sdk-path)
gem install --user-install bundler jekyll
rubyVersion=$(ruby -e 'puts [RbConfig::CONFIG["MAJOR"], RbConfig::CONFIG["MINOR"], 0].join(".")') # Ignore teeny version
pathToRubyGemBinaryDirectory="$HOME/.gem/ruby/$rubyVersion/bin"
Now I have a variable pathToRubyGemBinaryDirectory
whose value is exactly the path that needs to be in my PATH. I only need to export it if it’s not already in my PATH. I wrote some helper functions to check if it’s already in my PATH. When I got to the part of exporting the PATH and amending my .zshrc I chose to just emit instructions to do this for now because I keep that file organized and didn’t want to code something more involved:
#!/bin/sh
hasDirectoryInPathLikeVariable()
{
... # Elided for brevity.
}
hasDirectoryInPathEnvironmentVariable()
{
... # Elided for brevity.
}
xcode-select --install >/dev/null 2>&1
if ! which ruby >/dev/null 2>&1; then
echo "Couldn't find ruby. Is it installed?" >&2
exit 1
fi
export SDKROOT=$(xcrun --show-sdk-path)
gem install --user-install bundler jekyll
rubyVersion=$(ruby -e 'puts [RbConfig::CONFIG["MAJOR"], RbConfig::CONFIG["MINOR"], 0].join(".")') # Ignore teeny version
pathToRubyGemBinaryDirectory="$HOME/.gem/ruby/$rubyVersion/bin"
hasRubyGemBinaryDirectoryInPath=$(hasDirectoryInPathEnvironmentVariable "$pathToRubyGemBinaryDirectory")
if [ "$hasRubyGemBinaryDirectoryInPath" = "0" ]; then
echo "Ensure the following is in your PATH:" >&2
echo " $pathToRubyGemBinaryDirectory" >&2
fi
And that’s it for this example. The script evolved from this: I encoded more steps, enabled some diagnostics, and ran it through ShellCheck. The full script is at:
https://github.com/dydz/dydz.github.io/blob/main/configure-machine-for-blogging