File: svnmirror.sh

package info (click to toggle)
subversion 1.4.2dfsg1-3
  • links: PTS
  • area: main
  • in suites: etch
  • size: 37,284 kB
  • ctags: 32,888
  • sloc: ansic: 406,472; python: 38,378; sh: 15,438; cpp: 9,604; ruby: 8,313; perl: 5,308; java: 4,576; lisp: 3,860; xml: 3,298; makefile: 856
file content (420 lines) | stat: -rwxr-xr-x 12,334 bytes parent folder | download | duplicates (3)
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
#!/bin/sh
#######################################################################
#
# svnmirror.sh
#
VERSION="0.0.7"
#
# This script syncs changes from a developer repository to a
#             _READONLY_public_repository_
#
# It supports pushing or pulling the changes via ssh and svn tools.
# It is intended to be run manually or from cron.
#
#######################################################################
#
# Things you have to take care of:
#
# 1. You need write access to the directory structure on both boxes
#    for more see the warning at:
#    http://svnbook.red-bean.com/html-chunk/ch06s03.html
#
# 2. For running it from cron i suggest the use of the ssh agent e.g
#    via keychain <http://www.gentoo.org/proj/en/keychain.xml>
#    in general read "man ssh-agent" and "man ssh-keygen".
#
# 3. Do NOT run it from post commit scripts it can lead to broken public
#    repositories!
#
# 4. You do not have to be afraid of the public repos. If the pipe is
#    broken nothing will be committed to it.
#    21:40:47 <@sussman> tberman:  welcome to atomic commits.
#
# 5. For local syncing use "svnadmin hotcopy"
#    see: "svnadmin help hotcopy"
#
#######################################################################
#
# Authors:
#    - Martin Furter <mf@rola.ch>
#    - Marcus Rückert <darix@irssi.org>
#    - Joerg Sonnenberger <joerg@bec.de>
#
#    with suggestions from:
#       - tberman (#svn at freenode)
#         - he actually needed such a script :)
#       - Erik Huelsmann <e.huelsmann@gmx.net>
#         - the little if for remote version :)
#         - status reply
#       - Ben Collins-Sussman <sussman@collab.net>
#         - for some help with the svn commands
#       - Bjørn Magnus Mathisen <epic@generation.no>
#         - for pointing out i forgot to replace one ssh with $LSSH
#       - John Belmonte <john@neggie.net>
#         - for pointing out that we use stderr for a non-error message
#       - Loic Calvez <l.calvez@openchange.org>
#         - filtering
#
# Users:
#    our biggest users atm are Mono Project and MonoDevelop
#    Mono Team currently mirrors the repos every 30 minutes!:)
#    see http://www.mono-project.com/contributing/anonsvn.html
#
#    Openchange.org team is using this script with an excluding filter
#    to synchronise private and public svn server once a day.
#
# License:
#    The same as svn itself. for latest version check:
#    http://svn.collab.net/repos/svn/trunk/subversion/LICENSE
#
# Thanks to the subversion team for their great work.
#
# Links:
#    If you do not like our solution check:
#       - svnpush
#         + http://svn.collab.net/repos/svn/trunk/contrib/client-side/svn-push/svn-push.c
#       - svn replicate
#         + https://open.datacore.ch/read-only/
#       - SVN::Mirror and SVN::Web
#         + http://svn.elixus.org/repos/member/clkao/
#         + http://svn.elixus.org/svnweb/repos/browse/member/clkao/ 
#
# Changes:
#  0.0.7
#    + allow optional filtering using svndumpfilter
#
#  0.0.6
#    + print a non-error message to stdout instead of stderr.
#  0.0.5
#    + added DUMPPARAMS to specify the params for the svnadmin dump call
#    + added note about svnadmin 1.1 and "--deltas" cmdline option
#    * made the script POSIX sh-compatible
#      (zsh in native-mode does not work atm!)
#    * changed handling of default settings
#    * created config file has all lines commented out now
#    + check if necessary values are set
#    + added note about current users
#    * fixed documentation in the default config
#
#  0.0.4
#    + added comandline switch -v which shows the version.
#    + using markers now to find the config so it's not twice in this script
#    + added/changed some more documentation
#
#  0.0.3
#    + added comandline switches: -C (create config) -f (read config file)
#      -h (help) -q (quiet).
#
#  0.0.2
#    * initial version
#
#######################################################################
#
# uncomment this line for debugging
# set -x
#
#_CONFIG_START_MARKER
#######################################################################
#
# svnmirror default config
#
# Everything starting with "R" refers to the remote host
# with "L" to the local host
#
# the required variables are LREPOS, RREPOS and RHOST
#
#######################################################################
#
# Mode:
#
# push == Mirror from local repository to remote (readonly) repository
# pull == Mirror from remote repository to local (readonly) repository
#
DFLT_MODE="push"

#
# keychain = path to keychain sh file
# If the variable is not set, the script tries to read
# "$HOME/.keychain/`uname -n`-sh", if it is readable.
#
DFLT_KEYCHAIN=""

#
# DUMPPARMS
#
# see "svnadmin help dump" for it.
# default is "--incremental"
# 
# if you use svn 1.1 on both you should add "--deltas"
# it should speed up the transfer on slow lines.
#
DFLT_DUMPPARAMS="--incremental"

# Absolute path of svnlook on the local host.
DFLT_LSVNLOOK="/usr/bin/svnlook"

# Absolute path of svnadmin on the local host.
DFLT_LSVNADMIN="/usr/bin/svnadmin"

# Absolute path of svndumpfilter on the local machine.
DFLT_LSVNDUMPFILTER="/usr/bin/svndumpfilter"

# Absolute path to the repository on the local host.
# REQUIRED
DFLT_LREPOS="/path/to/repos"

# Absolute path of ssh on the local host.
#   '-c blowfish -C' to speed up the transfer a bit :)
DFLT_LSSH="/usr/bin/ssh -c blowfish -C"

# Name or IP of the remote host.
# REQUIRED
DFLT_RHOST="host"

# UNIX username on the remote host.
# defaults to the local username.
DFLT_RUSER="user"

# Absolute path of svnlook on the remote host.
DFLT_RSVNLOOK="/usr/bin/svnlook"

# Absolute path of svnadmin on the remote host.
DFLT_RSVNADMIN="/usr/bin/svnadmin"

# Absolute path of svndumpfilter on the local machine.
DFLT_RSVNDUMPFILTER="/usr/bin/svndumpfilter"

# Absolute path to the repository on the remote host.
# REQUIRED
DFLT_RREPOS="/path/to/repos"

# Additional commands you want to run before loading the dump in to the repos
# e.g. setting umask or change group via newgrp.
#
# Make sure the last char is a ";".
#
DFLT_LADDITIONAL=""
DFLT_RADDITIONAL=""

#
# with the filter directive you can optionally include a svndumpfilter
# into the pipe. see svndumpfilter help for more.
#
# examples:
#
# this will skip trunk/foo.conf from syncing
# 
# FILTER="exclude trunk/foo.conf"
#
# this filter will only sync trunk
#
# FILTER="include trunk/"
#
#
DFLT_FILTER=""
#_CONFIG_END_MARKER
#######################################################################
#
# create a config file
#
#######################################################################

create_config()
{
    CFGFILE="$1"
    if [ -f "${CFGFILE}" ]; then
        echo "config file '${CFGFILE}' already exists." >&2
        exit 1
    fi
    SVNMIRROR="$0"
    if [ ! -f "$0" ]; then
        SVNMIRROR=`which "$0"`
        if [ $? -ne 0 ]; then
            echo "could not locate $0" >&2
            exit 1
        fi
    fi
    STARTLINE=`grep -n "^#_CONFIG_START_MARKER" "$SVNMIRROR" | sed 's/:.*$//'`
    ENDLINE=`grep -n "^#_CONFIG_END_MARKER" "$SVNMIRROR" | sed 's/:.*$//'`
    ENDLINE=`expr ${ENDLINE} - 1`
    LINECOUNT=`expr ${ENDLINE} - ${STARTLINE}`
    head -n ${ENDLINE} "$SVNMIRROR" | tail -$LINECOUNT | sed -e 's/^#/##/' -e 's/^DFLT_/# /g' | \
        sed 's/default config/config/' > "$CFGFILE"
}

#######################################################################
#
# parse the commandline
#
#######################################################################
#
# 

show_help()
{
    echo ""
    echo "usage:"
    echo "  svnmirror.sh [-f configfile] [-h] [-q] [-v]"
    echo "  svnmirror.sh -C configfile"
    echo ""
    echo "  -C configfile   create a config file"
    echo "  -f configfile   read config from the specified file"
    echo "  -h              show this help"
    echo "  -q              quiet (-q -q for really quiet)"
    echo "  -v              show version"
    echo ""
    echo "Note: For -f, configfile is relative to the current PATH settings"
    echo ""
}

CONFIGREAD=false
QUIET=""
VERBOSE=true
while getopts C:f:hqv OPTION; do
    case "$OPTION" in
        C)
            create_config "${OPTARG}"
            exit 0
            ;;
        f)
            if [ ${CONFIGREAD} = true ]; then
                echo "config already read" >&2
                exit 1
            elif [ ! -r "${OPTARG}" ]; then
                echo "cannot read config file '${OPTARG}'" >&2
                exit 1
            else
                . ${OPTARG}
            fi
            ;;
        h)
            show_help
            exit 0
            ;;
        q)
            if [ -z "${QUIET}" ]; then
                QUIET="-q"
            else
                VERBOSE=false
            fi
            ;;
        v)
            echo "svnmirror.sh $VERSION"
            exit 0
            ;;
        \?)
            echo "  for help use $0 -h"
            exit 1
            ;;
    esac
done

#######################################################################
#
# add default values
#
#######################################################################
MODE="${MODE:-$DFLT_MODE}"
DUMPPARAMS="${DUMPPARAMS:-$DFLT_DUMPPARAMS}"
LSVNLOOK="${LSVNLOOK:-$DFLT_LSVNLOOK}"
LSVNADMIN="${LSVNADMIN:-$DFLT_LSVNADMIN}"
LSVNDUMPFILTER="${LSVNDUMPFILTER:-$DFLT_LSVNDUMPFILTER}"
LSSH="${LSSH:-$DFLT_LSSH}"
RUSER="${RUSER:+$RUSER@}"
RSVNLOOK="${RSVNLOOK:-$DFLT_RSVNLOOK}"
RSVNADMIN="${RSVNADMIN:-$DFLT_RSVNADMIN}"
RSVNDUMPFILTER="${RSVNDUMPFILTER:-$DFLT_RSVNDUMPFILTER}"
LADDITIONAL="${LADDITIONAL:-$DFLT_LADDITIONAL}"
RADDITIONAL="${RADDITIONAL:-$DFLT_RADDITIONAL}"

echo "${LREPOS:?Required variable LREPOS is not set in config file}" > /dev/null
echo "${RREPOS:?Required variable RREPOS is not set in config file}" > /dev/null
echo "${RHOST:?Required variable RHOST is not set in config file}" > /dev/null

KEYCHAIN="${KEYCHAIN:-$HOME/.keychain/`uname -n`-sh}"
[ -n "${KEYCHAIN}" -a -r "${KEYCHAIN}" ] && . ${KEYCHAIN}

#######################################################################
#
# the actual script
#
#######################################################################
#

#
# getting version of the remote repository
#
RVERSION=`${LSSH} ${RUSER}${RHOST} ${RSVNLOOK} youngest ${RREPOS}`
if [ -z "${RVERSION}" ] ; then
    echo "getting version of remote repository failed" >&2
    exit 1
fi

#
# getting version of the local repository
#
${LADDITIONAL}
LVERSION=`${LSVNLOOK} youngest ${LREPOS}`
if [ -z "${LVERSION}" ] ; then
    echo "getting version of local repository failed" >&2
    exit 1
fi

#
# compare revision numbers
#
if [ ${RVERSION} -eq ${LVERSION} ] ; then
    [ ${VERBOSE} = true ] && \
        echo "both repositories are already at ${LVERSION}"
    exit 0
fi

#
# syncing
#
RC=0
if [ ${MODE} = "push" ] ; then
    if [ ${RVERSION} -gt ${LVERSION} ] ; then
        echo "revision of remote repos is higher than local one" >&2
        exit 1
    fi
    DUMPFILTER="cat"
    if [ -n "$FILTER" ] ; then
        DUMPFILTER="${LSVNDUMPFILTER} ${FILTER}"
        [ ${VERBOSE} = true ] && echo "using filter 'svndumpfilter ${FILTER}"
    fi
    REVRANGE="`expr ${RVERSION} + 1`:${LVERSION}"
    [ ${VERBOSE} = true ] && \
        echo -n "syncing r${REVRANGE} to ${RHOST} ";
    ${LSVNADMIN} dump ${QUIET} ${DUMPPARAMS} -r${REVRANGE} ${LREPOS} | ${DUMPFILTER} | \
    ${LSSH} ${RUSER}${RHOST} "${RADDITIONAL} ${RSVNADMIN} load ${QUIET} ${RREPOS}" || \
    RC=1
elif [ ${MODE} = "pull" ] ; then
    if [ ${LVERSION} -gt ${RVERSION} ] ; then
        echo "revision of local repos is higher than remote one" >&2
        exit 1
    fi
    DUMPFILTER=""
    if [ -n "$FILTER" ] ; then
        DUMPFILTER="| ${LSVNDUMPFILTER} ${FILTER}"
        [ ${VERBOSE} = true ] && echo "using filter 'svndumpfilter ${FILTER}"
    fi
    REVRANGE="`expr ${LVERSION} + 1`:${RVERSION}"
    [ ${VERBOSE} = true ] && \
        echo -n "syncing r${REVRANGE} from ${RHOST} ";
    ${LSSH} ${RUSER}${RHOST} \
    "${RADDITIONAL} ${RSVNADMIN} dump ${QUIET} ${DUMPPARAMS} -r${REVRANGE} ${RREPOS} ${DUMPFILTER}" | \
    ${LSVNADMIN} load ${QUIET} ${LREPOS} || \
    RC=1
else
    echo "invalid mode \"${MODE}\" specified!" >&2
    exit 1
fi
if [ ${RC} -ne 0 ]; then
    echo "failed" >&2
else
    [ ${VERBOSE} = true ] && \
        echo "successfull completed."
fi
exit ${RC}