Recipes

Small scripts that put a line on the island: when a push is done, a command ends, the disk is filling up. Copy one, and it works. Each does one thing, and none of them updates itself or downloads anything.

Before you start

  1. Press Install next to Command under Build & Test in Settings, so the runnock command is there (it goes in ~/.local/bin).
  2. Check that External alerts is on in Settings under Notch.
  3. Every recipe first checks that runnock is installed and says nothing if it is not, so one that you keep never gets in the way of your own scripts.

A line when you commit

Shows the commit message on the island after every commit in a repository.

Save it as .git/hooks/post-commit in the repository and run chmod +x on it.

#!/bin/sh
# Runnock recipe: a line on the island after every commit.
# Save as .git/hooks/post-commit in the repository, then: chmod +x .git/hooks/post-commit
rn() { command -v runnock >/dev/null 2>&1 && runnock alert "$@" >/dev/null 2>&1; true; }

top=$(git rev-parse --show-toplevel)
subject=$(git log -1 --pretty=%s | cut -c1-70)
# "open" has to be under your home folder or /tmp, so leave it out for a repository kept anywhere else.
case "$top" in
  "$HOME"/*|/tmp/*|/private/tmp/*) rn --id "git-commit" --title "committed" --body "$subject" --open "$top" ;;
  *)                rn --id "git-commit" --title "committed" --body "$subject" ;;
esac

Know when a push is done

gpush runs git push and says on the island whether it worked, for a slow remote you do not want to watch.

Add it to ~/.zshrc, open a new terminal, and use gpush where you would use git push.

# Runnock recipe: git push, and a line on the island when it is done.
# Add to ~/.zshrc, open a new terminal, and use `gpush` where you would use `git push`.
gpush() {
  local branch; branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
  if git push "$@"; then
    command -v runnock >/dev/null 2>&1 && runnock alert --id "git-push" --title "pushed" --body "$branch" >/dev/null 2>&1
    return 0
  else
    local code=$?
    command -v runnock >/dev/null 2>&1 && runnock alert --id "git-push" --title "push failed" --body "$branch" --level fail >/dev/null 2>&1
    return $code
  fi
}

A line when any command ends

rn-after runs a command and says on the island how it ended and how long it took. A failure stays until you dismiss it.

Add it to ~/.zshrc, then run, for example, rn-after pnpm test. The command's own exit code is passed on.

# Runnock recipe: run a command, then say how it ended and how long it took.
# Add to ~/.zshrc, open a new terminal: rn-after pnpm test
rn-after() {
  local started=$SECONDS name=${1:t}
  "$@"
  local code=$? took=$(( SECONDS - started ))
  if command -v runnock >/dev/null 2>&1; then
    if [ $code -eq 0 ]; then
      runnock alert --title "${name:0:16}" --body "done in ${took}s" >/dev/null 2>&1
    else
      runnock alert --title "${name:0:16}" --body "failed (exit $code) after ${took}s" --level fail >/dev/null 2>&1
    fi
  fi
  return $code
}

Wait for a GitHub Actions run

rn-gh-run watches a run with the gh CLI and says on the island whether it passed.

Needs the gh CLI signed in. Add it to ~/.zshrc, then rn-gh-run <run-id>, or with no id to pick one.

# Runnock recipe: watch a GitHub Actions run and say how it went. Needs the gh CLI, signed in.
# Add to ~/.zshrc: rn-gh-run <run-id>   (without an id, gh lets you pick one)
rn-gh-run() {
  local repo; repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)")
  if gh run watch "$@" --exit-status; then
    command -v runnock >/dev/null 2>&1 && runnock alert --id "gh-run" --title "CI passed" --body "$repo" >/dev/null 2>&1
  else
    local code=$?
    command -v runnock >/dev/null 2>&1 && runnock alert --id "gh-run" --title "CI failed" --body "$repo" --level fail >/dev/null 2>&1
    return $code
  fi
}

A line when a download ends

rn-get downloads a file with curl and says on the island when it has finished or failed.

Add it to ~/.zshrc, then rn-get https://example.com/big.iso. The file goes in the current folder.

# Runnock recipe: download with curl, and a line on the island when it ends.
# Add to ~/.zshrc: rn-get https://example.com/big.iso
rn-get() {
  local url=2877 file=${1:t}
  if curl -fL --output "${file:-download}" "$url"; then
    command -v runnock >/dev/null 2>&1 && runnock alert --id "rn-get" --title "downloaded" --body "${file:0:70}" --open "$PWD" >/dev/null 2>&1
  else
    local code=$?
    command -v runnock >/dev/null 2>&1 && runnock alert --id "rn-get" --title "download failed" --body "${file:0:70}" --level fail >/dev/null 2>&1
    return $code
  fi
}

A timer on the island

rn-timer waits a number of minutes and then puts a line on the island that waits for you.

Add it to ~/.zshrc, then rn-timer 25 "stand up". It keeps running in the background, so the terminal is free.

# Runnock recipe: rn-timer <minutes> <text>   e.g. rn-timer 25 "stand up"
# Add to ~/.zshrc. It runs in the background; the line stays on the island for 5 minutes.
rn-timer() {
  local minutes=${1:?minutes} text=${2:-timer}
  ( sleep $(( minutes * 60 )) && command -v runnock >/dev/null 2>&1 && \
    runnock alert --id "rn-timer" --title "timer" --body "${text:0:80}" --level action --ttl 300 >/dev/null 2>&1 ) &!
}

Warn before the disk fills up

Checks the free space on the startup disk and puts a line on the island when it falls below a limit. Run it on a schedule.

Save it somewhere (for example ~/bin/rn-disk-low), chmod +x it, and run it every hour with cron or launchd. The limit is the first argument in GB (default 15).

#!/bin/sh
# Runnock recipe: say so when the startup disk has less than N GB free (default 15).
# Run it every hour, e.g. with cron:  0 * * * *  $HOME/bin/rn-disk-low 15
limit=${1:-15}
free_kb=$(df -k / | awk 'NR==2 {print undefined}')
free_gb=$(( free_kb / 1024 / 1024 ))
if [ "$free_gb" -lt "$limit" ] && command -v runnock >/dev/null 2>&1; then
  runnock alert --id "disk-low" --title "disk space low" --body "${free_gb} GB free" --level action --ttl 3600 --open "$HOME" >/dev/null 2>&1
fi
exit 0

Low battery

Puts a line on the island when the battery is low and not charging. Run it every few minutes.

Save it (for example ~/bin/rn-battery-low), chmod +x it, and run it every 5 minutes with cron or launchd. The limit is the first argument in percent (default 15).

#!/bin/sh
# Runnock recipe: say so when the battery is below N percent (default 15) and not charging.
# Run it every 5 minutes, e.g. with cron:  */5 * * * *  $HOME/bin/rn-battery-low 15
limit=${1:-15}
status=$(pmset -g batt)
percent=$(printf '%s' "$status" | grep -Eo '[0-9]+%' | head -1 | tr -d '%')
if [ -n "$percent" ] && [ "$percent" -lt "$limit" ] && printf '%s' "$status" | grep -q 'discharging' && command -v runnock >/dev/null 2>&1; then
  runnock alert --id "battery-low" --title "battery low" --body "${percent}% left" --level action --ttl 600 >/dev/null 2>&1
fi
exit 0

A certificate about to expire

Checks when the TLS certificate of a site expires and puts a line on the island when it is less than two weeks away.

Save it (for example ~/bin/rn-cert-expiry), chmod +x it, and run it once a day: rn-cert-expiry example.com. It connects to that site on port 443.

#!/bin/sh
# Runnock recipe: say so when a site's certificate expires in under 14 days.
# Run it once a day:  rn-cert-expiry example.com
host=${1:?usage: rn-cert-expiry <host>}
end=$(printf '' | openssl s_client -servername "$host" -connect "$host:443" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
[ -n "$end" ] || exit 0
end_epoch=$(date -j -f "%b %e %T %Y %Z" "$end" +%s 2>/dev/null) || exit 0
days=$(( (end_epoch - $(date +%s)) / 86400 ))
if [ "$days" -lt 14 ] && command -v runnock >/dev/null 2>&1; then
  runnock alert --id "cert-$host" --title "cert expires" --body "$host in ${days} days" --level action --ttl 86400 >/dev/null 2>&1
fi
exit 0

From Shortcuts and automations

A runnock:// link does what the command does, so a Shortcut or an automation can put a line on the island.

In Shortcuts, add an Open URLs action with the link, and use it in a personal automation (a Focus ending, arriving somewhere, a time of day).

# Runnock recipe: links for Shortcuts and automations. Each one is a runnock://alert link.
# Parameters: title (1-16 characters), body (up to 80), level (info, action, fail), open (a path under your home folder or /tmp), id, ttl (seconds).

# A reminder that waits for you for 30 seconds:
runnock://alert?title=stand%20up&body=Time%20for%20a%20break&level=action

# After a Focus mode ends:
runnock://alert?title=focus%20over&body=Back%20to%20normal&id=focus

# Open a project folder from the island:
runnock://alert?title=ready&body=Open%20the%20project&open=/Users/YOU/Projects/app&level=action