Level 18: Macbook setup and Bash scripting fun, p2
If you haven’t read part 1, click here!
There were a few improvements from last time that I wanted to implement.
- Refactor each separate chunk of installations into separate files
- Write more re-usable functions to be shared across sets of installs
- Add more installations/basic features
Let’s start with #3. In the first couple of commits, i added a few more integrations like slack, skype, etc etc.
For #2, I had originally started with a ‘get-it-done’ mentality. Even if the code wasn’t particularly pretty, I wanted to align to my basic design principle of idempotency. Because of that, I added quite a bit more fluff in terms of content than absolutely necessary. In the book, Clean Code, the author mentions that it takes a lot of refactoring to get to the “ideal” state. With that in mind, I tried to take it easier and just code and get everything down and functional.
verify_cask() {
if [[ $(brew cask list | grep $1 | head -c1 | wc -c) -eq 0 ]]; then
echo ${1}":: not installed" | awk '{print toupper($0)}'
$2
verify_cask $1 $2
else
echo ${1}":: installed" | awk '{print toupper($0)}'
fi
}
Even though I had a basic verify_program
method, I created a separate one because the command to verify installations for bash versus casks were different. However, the general breakdown of the method was the same. In my main method, I use individual install_X
methods to make my calls.
verify_cask slack install_slack
There is a really neat utility for statically analyzing sh scripts called shellcheck. There’s an online version as well as one that can be used via terminal/brew. With this tool, I received some pointers on how I could make the code more robust. The snippet above became this:
verify_cask() {
if "$1" -v | grep -q "no ${1}" || [[ $("$1" -v | head -c1 | wc -c) -eq 0 ]]; then
echo "${1}:: not installed" | awk '{print toupper($0)}'
verify_program "$1" "$2"
else
echo "${1}:: installed" | awk '{print toupper($0)}'
fi
}
Note the quoting around the parameter substitutions, for more overtly clear indication of what substitution is happening.
The following commit was more substantial because I made use of the array data structure in bash and iterated through the respective verify methods for each type of installation, hence achieving more re-usability. individual installation calls became:
brews=(
ruby
node
)
for BREW in "${brews[@]}"
do
:
verify_brew "$BREW"
done
That being said, all the code was in a mega script. The next commit resolved this issue by chunking everything up into separate scripts that are grouped by the type of installation, ie. brews.sh, casks.sh, alias.sh. This led to a much nicer view of the main script:
main() {
# import other scripts
. lib/alias.sh
. lib/brews.sh
. lib/casks.sh
# create main working folder
cd ~ || exit
For more functionality, I also included some fancy ruby gems as well as a more extensive list of vscode extensions.
Otherwise, sorry for the short post. A lot of real-life happening these past two weeks but I’ll be back with more soon. As always, the code can be found on my GitHub, here.