This repository has been archived by the owner on Feb 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsalt-ssh-nanny
executable file
·172 lines (145 loc) · 5.65 KB
/
salt-ssh-nanny
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#!/bin/sh
###############################################################################
#
scriptname='salt-ssh-nanny'
#
# Nannying wrapper script for salt-ssh
# David Spencer, Open Data Services Co-operative
#
# This wrapper script ensures the local branch of opendataservices-deploy (and
# the embedded private pillar repo) is up-to-date with the Github upstream, so
# we don't have little accidents.
#
# This wrapper should be symlinked at /usr/local/bin/salt-ssh to your local
# clone of the deploy repo, so that $PATH causes the wrapper to be invoked
# instead of the real salt-ssh binary. You need to do this manually:
#
# ln -s $(pwd)/salt-ssh-nanny /usr/local/bin/salt-ssh
#
# All arguments are passed unchanged to the real salt-ssh command, except:
# -f [must be first argument]
# Invoke salt-ssh without checking the state of git
#
# Exit status:
# 1 Nanny says no. Rebase your branch or try again with '-f'.
# 2 You're in the wrong directory, it wouldn't have worked anyway.
#
###############################################################################
# Configurables:
# Recognisable substring of our upstream Github repo name:
UPSTREAM="${UPSTREAM:-OpenDataServices/opendataservices-deploy}"
# How many seconds is too long since the last 'git fetch'?
INTERVAL="${INTERVAL:-3600}"
###############################################################################
repo_needs_fetch()
# Function to determine whether a git repo in the current directory has been
# fetched recently (i.e. in the last $INTERVAL seconds).
# Returns: 0 = needs to be fetched, 1 = doesn't need to be fetched
{
prevfetch=0
if [ -f '.git/FETCH_HEAD' ]; then
prevfetch="$(stat -c %Y .git/FETCH_HEAD)"
elif [ -f '.git/HEAD' ]; then
prevfetch="$(stat -c %Y .git/HEAD)"
fi
if [ $(( $(date +%s) - prevfetch )) -gt "$INTERVAL" ]; then
return 0
fi
return 1
}
###############################################################################
set -eu
nanny='no'
# Only do the nannying if it's our deploy repo
# (use a pattern, because the URL could be http: or git: or git@)
case "$(git remote get-url --all origin 2>/dev/null)" in
*${UPSTREAM}*)
nanny='yes'
;;
*)
;;
esac
# (f)orcibly suppress nannying if requested
if [ "${1}" = '-f' ]; then
nanny='no'
shift
fi
###############################################################################
# Nannying department
if [ "$nanny" = 'yes' ]; then
# (1) Ensure that we're in the deploy repo's root directory
if [ ! -d '.git' ]; then
echo "${scriptname}: You're not in the deploy repo root directory." >&2
exit 2
elif [ ! -f '.git/HEAD' ]; then
echo "${scriptname}: Are you sure this is a git repo?" >&2
exit 2
fi
# Looks like a git repo, so remember whether we are on the master branch
onmaster='no'
[ "$(git rev-parse --abbrev-ref HEAD)" = 'master' ] && onmaster='yes'
# (2) Fetch the deploy repo if needed
if repo_needs_fetch; then
echo "${scriptname}: Updating from $(git remote get-url origin)"
git fetch --all --prune
fi
if [ "$onmaster" = 'yes' ]; then
# (3) Keep the master branch tidy
# We shouldn't have a dirty local tree if we're on master (i.e. deploying to live)
if [ "$onmaster" = 'yes' ] && [ -n "$(git status -s .)" ]; then
echo "${scriptname}: Your local tree is dirty." >&2
echo "${scriptname}: Please commit/stash/whatever, or specify '-f' to force execution as-is." >&2
exit 1
fi
# If we're behind origin/master, fast-forward master
# (and if that fails, you've been abusing the master branch)
if git merge-base --is-ancestor HEAD origin/master; then
set +e
git merge --ff-only origin/master
failed=$?
set -e
if [ "$failed" != 0 ]; then
echo "${scriptname}: Your local master branch can't fast-forward to sync with origin/master." >&2
echo "${scriptname}: Please resolve the conflicts and try again." >&2
exit 1
fi
# If we're ahead of master, that's ok but we need to maintain situational awareness ;)
elif git merge-base --is-ancestor origin/master HEAD; then
echo "${scriptname}: This is a polite reminder: you are ahead of origin/master, please push sometime." >&2
else
# Welcome to hell
echo "${scriptname}: Your local master branch conflicts with origin/master." >&2
echo "${scriptname}: Please resolve the conflicts and try again." >&2
exit 1
fi
else
# (4) For other local branches, origin/master should be an ancestor of the latest local commit
if ! git merge-base --is-ancestor origin/master HEAD; then
echo "${scriptname}: Your local branch is out of date with respect to origin/master." >&2
echo "${scriptname}: Please rebase, or specify '-f' to force execution as-is." >&2
exit 1
fi
fi
# (5) Fetch the private pillar if needed and fast-forward
(
cd pillar/private
if repo_needs_fetch; then
echo "${scriptname}: Updating from $(git remote get-url origin)"
git fetch --all
# Try to fast-forward -- this will fail if the local branch conflicts or local tree is dirty
# (having a dirty tree is only a problem if the local branch needs to be updated)
set +e
git merge --ff-only origin/master
failed=$?
set -e
if [ "$failed" != 0 ]; then
echo "${scriptname}: The private pillar is out of date or dirty and can't fast-forward." >&2
echo "${scriptname}: Please resolve the conflicts and try again, or specify '-f' to force execution." >&2
exit 1
fi
fi
)
fi
###############################################################################
# Finally, hand off to the real salt-ssh
exec /usr/bin/salt-ssh "${@}"