Skip to content

Instantly share code, notes, and snippets.

@stvhay
Forked from skrajewski/backup.sh
Last active November 11, 2022 01:20
Show Gist options
  • Save stvhay/0954febc5baf98068a5da51968f9a728 to your computer and use it in GitHub Desktop.
Save stvhay/0954febc5baf98068a5da51968f9a728 to your computer and use it in GitHub Desktop.
Automate your macOS backup to Backblaze B2 using Restic and launchd.
#!/bin/bash
log () { echo "$(date +"%Y-%m-%d %T") " "$@"; }
home_dir='' # FILL THIS OUT
name='remote'
restic_path='/opt/homebrew/bin' # ADJUST AS NEEDED
pid_file="${home_dir}/.restic/${name}.pid"
timestamp_file="${home_dir}/.restic/${name}.timestamp"
hc_ping="https://hc-ping.com/uuid_here" # REPLACE WITH YOUR UUID
backup_period="+3H"
local_wifi='' # FILL THIS OUT
# Configuration Variables
B2_ACCOUNT_ID=$(/usr/bin/security find-generic-password -s backup-restic-b2-accound-id -w) || { log "B2 Account ID retrieval failed"; exit 10; }
B2_ACCOUNT_KEY=$(/usr/bin/security find-generic-password -s backup-restic-b2-account-key -w) || { log "B2 Account Key retrieval failed"; exit 10; }
export B2_ACCOUNT_ID
export B2_ACCOUNT_KEY
export RESTIC_REPOSITORY='' # FILL THIS OUT
export RESTIC_PASSWORD_COMMAND='/usr/bin/security find-generic-password -s backup-restic-password-repository -w'
# Checks if a backup is already running.
backup_running () {
if [ -f "$pid_file" ]; then
if ps -p "$(cat $pid_file)" > /dev/null; then
log "File $pid_file exists and $(cat $pid_file) running. Backup is already in progress."
return 1
else
log "File $pid_file exists but process " "$(cat $pid_file)" " not found. Removing PID file."
rm -f "$pid_file"
log "Removing restic lock..."
"${restic_path}/restic" --verbose unlock
fi
fi
}
# Checks if its too soon to take a backup.
time_too_soon () {
if [ -f "$timestamp_file" ]; then
time_run=$(cat "$timestamp_file")
current_time=$(date +"%s")
if [[ $current_time -lt $time_run ]]; then
return 1
fi
fi
}
# Check if you are not on local wifi.
not_local_wifi () {
if [[ "$(/usr/sbin/networksetup -getairportnetwork en0 | grep -E "$local_wifi")" == "" ]]; then
log "Unsupported network."
return 1
fi
}
# Check if power is not connected.
power_not_connected () {
if [[ $(pmset -g ps | head -1) =~ "Battery" ]]; then
log "Computer is not connected to the power source."
return 1
fi
}
# Run the backup
run_backup () {
"${restic_path}/restic" cache --cleanup
"${restic_path}/restic" backup \
--verbose \
--files-from "${home_dir}/.restic/${name}.include" \
--exclude-file "${home_dir}/.restic/${name}.exclude"
backup_success=$?
if [ $backup_success -ne 0 ]; then
log "Error running restic backup."
return 1
fi
log "Backup success."
}
# Prune the backup database
prune_backup () {
${restic_path}/restic forget \
--keep-daily 7 \
--keep-weekly 5 \
--keep-monthly 12 \
--keep-yearly 10 \
--prune
prune_success=$?
# Exit if pruning is a failure.
if [ $prune_success -ne 0 ]; then
log "Pruning failed."
return 1
fi
log "Pruning finished."
}
# Perform Checks
backup_running || exit 0
time_too_soon || exit 0
not_local_wifi || exit 0
power_not_connected || exit 0
echo "$$" > "$pid_file"
# Run backup
log "Backup start"
run_backup || { rm -f "$pid_file"; log "Backup failed."; exit 5; }
# Update backup timestamp
date -v $backup_period +"%s" > "$timestamp_file"
log "Backup finished."
# Prune backup database
prune_backup || { rm -f "$pid_file"; log "Pruning failed."; exit 6; }
curl -fsS -m 10 --retry 5 "$hc_ping" > /dev/null
rm -f "$pid_file"
<!-- Edit the paths to point to the correct locations -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.myself.backup_remote</string>
<key>Program</key>
<string>/Users/myself/.local/bin/backup_remote.sh</string>
<key>ProcessType</key>
<string>Background</string>
<key>StartInterval</key>
<integer>300</integer>
<key>WorkingDirectory</key>
<string>/Users/myself</string>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>/Users/myself</string>
</dict>
<key>StandardOutPath</key>
<string>/Users/myself/Library/Logs/com.myself.backup_remote.out.log</string>
<key>StandardErrorPath</key>
<string>/Users/myself/Library/Logs/com.myself.backup_remote.error.log</string>
</dict>
</plist>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment