-
Notifications
You must be signed in to change notification settings - Fork 11
/
auto-sync-daemon
executable file
·147 lines (129 loc) · 4.79 KB
/
auto-sync-daemon
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/env zsh
BRANCH=$( git config --default master auto-sync.branch )
process_inotify_batch () {
while read dir action file; do
# echo >&2 "inotifywait: dir=$dir action=$action file=$file"
try_sync
# This should not be unnecessary since we are relying on inotifywait
# to only return a single event; the filtering is done by that
# process, not this loop.
# echo >&2 "inotifywait: break"
break
done
}
try_sync () {
if check_ssh; then
# Skip sync if we found lockfiles which indicate that
# performing a git-annex sync might be problematic due to
# unsaved local changes.
#
# Unfortunately there is no easy way to add a check for lock
# files based on which files the pending git merge run by
# git-annex would change, because there isn't a git hook which
# runs right at the beginning of the merge:
#
# https://stackoverflow.com/questions/76596057/how-to-implement-custom-checks-before-starting-a-git-merge
#
# So instead we have to do a wider blanket check for lock
# files corresponding to files which would be changed by the
# sync are currently being edited. This is better than
# checking when *any* files in the repo are being edited,
# but it's still overkill relative to what we could do if
# there was a pre-merge git hook.
tmp="$(mktemp --tmpdir auto-sync.lockfiles.XXXXXXX)"
git config -z --get-all auto-sync.lockfile > "$tmp"
locked=$(git ls-files --other --ignored -X "$tmp")
rm "$tmp"
if [ -n "$locked" ]; then
echo >&2 "Skipping annex sync; found lock files: $locked"
return
fi
# Do a sync regardless of whether auto-commit did anything,
# because another remote may have pushed changes to our
# synced/master branch. First give other remotes a chance
# to completely finish their push, just in case of any races.
sleep 5
git-annex-clean-sync
else
echo >&2 "WARNING: can't connect to ssh-agent; skipping annex sync"
fi
}
check_ssh () {
if ! git remote -v | grep -q ssh; then
echo >&2 "No ssh remotes; skipping ssh agent check".
return 0
fi
if ! detect_ssh_agent; then
echo >&2 "Failed to detect ssh agent!"
return 1
fi
if ! ssh-add -l; then
echo >&2 "Failed to detect identities in ssh agent!"
return 1
fi
}
main () {
if ! inotifywait --help | grep -q -- '--include'; then
echo >&2 "inotifywait doesn't support --include; aborting!"
exit 1
fi
if [ $# != 1 ]; then
cat <<EOF >&2
Usage: $(basename $me) REPO-DIR
EOF
exit 1
fi
repo_dir="$1"
cd "$repo_dir"
if [[ -e HEAD ]] && [[ -e info ]] && [[ -e objects ]] && [[ -e refs ]] &&
[[ -e branches ]]
then
mode=bare
git_dir=.
elif [[ -d .git ]]; then
mode=worktree
git_dir=.git
else
echo >&2 "`pwd` isn't a git repo; aborting!"
exit 1
fi
sleep_secs=5
while true; do
# limit of exponential back-off in seconds
max_sleep=$( git config --default 600 auto-sync.max-sleep )
if ! check_ssh; then
echo >&2 "Sleeping ${sleep_secs}s ..."
sleep $sleep_secs
sleep_secs=$(( sleep_secs * 2))
if (( sleep_secs > max_sleep )); then
sleep_secs=$max_sleep
fi
continue
fi
# Maximum time to wait for an update before syncing.
max_wait=$( git config --default 360 auto-sync.max-wait )
echo "Waiting up to $max_wait minutes for an update to $BRANCH branch ..."
# This was a massive PITA to get right. It seems that if you
# specify specific files then it will look up the inodes on
# start-up and only monitor those. There's some similar weirdness
# with moves too. Suffice to say that it's necessary to use
# --include and watch the whole directory. -r is needed to catch
# the synced/ subdirectory too. For more clues see
# https://unix.stackexchange.com/questions/164794/why-doesnt-inotifywatch-detect-changes-on-added-files
#
# FIXME: try to switch to using http://eradman.com/entrproject/
inotifywait \
-q -r \
--include "/($BRANCH|synced/($BRANCH|git-annex))\$" \
-e create -e modify -e move -e delete \
-t $(( max_wait * 60 )) \
"$git_dir/refs/heads" |
process_inotify_batch
if (( pipestatus[1] == 2 )); then
echo "Timed out waiting $max_wait minutes for update; syncing now ..."
try_sync
fi
done
}
me="$0"
main "$@"