tips - mvaltas/grind GitHub Wiki
Since all calls inside definitions are actually bash function calls it is possible to compose checks. Here's an advanced example of using Appdmg API to install based not only on the presence or not of the application but also on which version is installed.
# IntelliJ installation based on version
INTELLIJ_VERSION="2016.2.5"
function intellij_check() {
unless_dir "/Applications/IntelliJ IDEA.app"
stop_on_fail
current_version=$(xpath '/Applications/IntelliJ IDEA.app/Contents/Info.plist' "//key[.='CFBundleShortVersionString']/following-sibling::*[1]/text()" 2> /dev/null)
unless "[[ "${current_version}" == "${INTELLIJ_VERSION}" ]]"
}
appdmg_custom "https://download.jetbrains.com/idea/ideaIU-${INTELLIJ_VERSION}.dmg"
unless "intellij_check"
Upgrading from 2016.2.4
to 2016.2.5
will result in:
do_run: _appdmg_process 'https://download.jetbrains.com/idea/ideaIU-2016.2.5.dmg'
unless: intellij_check
unless_dir: /Applications/IntelliJ IDEA.app
unless: [[ -d "/Applications/IntelliJ IDEA.app" ]]
[SKIP]
unless: [[ 2016.2.4 == 2016.2.5 ]]
[RUN]
[SKIP]
grind
has specific rules to elect a definition for running, one of which is the machine hostname. Let's say you write a definition (mydefinition) and want to make it available for two machines named "desktop" and "laptop". One way is to have a copy of the definition in each machine directory:
machines/laptop/mydefinition
machines/desktop/mydefinition
The problem with this is that an update on mydefinition would require you to copy the new version over. One solution is to use symbolic links (ln -s
) and host the definition in a directory outside of grind
main directory recursion. Like:
singles/mydefinition
machines/laptop/mydefinition@ -> ../../singles/mydefinition
machines/desktop/mydefinition@ -> ../../singles/mydefinition
Using symbolic links you can reuse definitions without having to copying them over to all directories needed.
Although it is not recommended for one definition depend on another, since complexity can rise quite rapidly. It is possible to "include" a definition into another using run_def
. Let's say singles/test
depends on singles/test2
, so in singles/test
we write:
run_def "singles/test2"
do_run "some cmd"
unless "some check"
When grind
is about to run this definition it will first run singles/test2
and then proceed to run this definition, which gives you a mechanism to define dependencies and maybe reuse. Keep in mind that this also allows for circular dependencies to be defined and grind
won't prevent that, it will loop forever. Use it carefully.
Some commands may require administrative privileges to run, a safe way to do this on macOS is to prompt for the admin password using macOS own mechanism. Here's an example:
HOMEBREW_BASH="/usr/local/bin/bash"
function _as_admin_run() {
local cmd="${1}"
in_yellow "Requiring admin privileges\n"
osascript -e "do shell script \"${cmd}\" with administrator privileges "
}
do_run "_as_admin_run 'echo ${HOMEBREW_BASH} >> /etc/shells'"
unless "grep -q '${HOMEBREW_BASH}' /etc/shells"
This definition adds a new line to /etc/shells
if it is not there, /etc/shells
is protected from default user changes. The function will prompt the user for admin privileges and then execute the shell command to add a new line into /etc/shells
.