summaryrefslogtreecommitdiff
path: root/bin/macos-defaults-diff
blob: 14274e3b6bd5672bfc1499f0a716e79b73f71129 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/bin/bash

# Find the `defaults` key behind a setting you changed in System Settings, so it
# can go in macos-defaults.sh instead of being remembered.
#
#   macos-defaults-diff            snapshot, wait while you change one setting,
#                                  then print what moved
#   macos-defaults-diff snap       take the snapshot only
#   macos-defaults-diff show       compare now against the last snapshot
#
# It reads both the global domains and the per-host ones (`-currentHost`), which
# is where several trackpad, mouse and font settings live and which a plain
# `defaults read` never shows.
#
# Change ONE setting at a time. macOS writes unrelated keys constantly -- window
# positions, recent items, sidebar widths -- so a diff after three changes is
# mostly noise with your key hidden in it.
#
# Quit System Settings before the second snapshot. It holds changes in memory
# and cfprefsd may not have written them yet; the line below flushes that cache.
#
# Runs under macOS's /bin/bash 3.2.

set -euo pipefail

if [[ "$(uname)" != "Darwin" ]]; then
    >&2 echo "macos-defaults-diff: macOS only"
    exit 1
fi

dir="${TMPDIR:-/tmp}/macos-defaults-diff"
mkdir -p "${dir}"

snapshot() {
    local tag="$1"
    # cfprefsd caches writes; without this a change made seconds ago can be
    # missing from the read that is supposed to reveal it.
    killall cfprefsd 2>/dev/null || true
    sleep 1
    defaults read > "${dir}/${tag}.txt" 2>/dev/null || true
    defaults -currentHost read > "${dir}/${tag}-host.txt" 2>/dev/null || true
}

report() {
    if [[ ! -f "${dir}/before.txt" ]]; then
        >&2 echo "macos-defaults-diff: no snapshot yet -- run 'macos-defaults-diff snap' first"
        exit 1
    fi
    snapshot after
    local found=""
    if ! diff -u "${dir}/before.txt" "${dir}/after.txt" > "${dir}/delta.txt" 2>&1; then
        found=y
        echo "=== global domains"
        grep -E '^[+-]' "${dir}/delta.txt" | grep -vE '^(\+\+\+|---)' | sed 's/^/  /'
    fi
    if ! diff -u "${dir}/before-host.txt" "${dir}/after-host.txt" > "${dir}/delta-host.txt" 2>&1; then
        found=y
        echo "=== this host only -- these need 'defaults -currentHost write'"
        grep -E '^[+-]' "${dir}/delta-host.txt" | grep -vE '^(\+\+\+|---)' | sed 's/^/  /'
    fi
    if [[ -z "${found}" ]]; then
        echo "nothing changed."
        echo "Some settings are not in defaults at all: anything behind Privacy &"
        echo "Security lives in the SIP-protected TCC database, and a few panes use"
        echo "private APIs. Those cannot be scripted -- note them in macos-defaults.sh"
        echo "as manual steps instead."
        return
    fi
    echo
    echo "The domain is the nearest heading above the changed line in:"
    echo "  ${dir}/delta.txt"
    echo "Write it as: defaults write <domain> \"<key>\" -bool/-int/-string <value>"
    echo "then add it to macos-defaults.sh, and restart whatever owns it"
    echo "(killall Dock, killall Finder, or log out)."
}

case "${1:-}" in
    snap)
        snapshot before
        echo "snapshot taken. Change one setting, then: macos-defaults-diff show"
        ;;
    show)
        report
        ;;
    "")
        snapshot before
        echo "Snapshot taken. Now change ONE setting in System Settings, quit it,"
        printf 'then press Enter: '
        read -r _
        report
        ;;
    -h|--help)
        sed -n '3,21p' "$0" | sed 's/^# \{0,1\}//'
        ;;
    *)
        >&2 echo "macos-defaults-diff: unknown argument: $1"
        exit 1
        ;;
esac