2
#######################################################################
8
# This script syncs changes from a developer repository to a
9
# _READONLY_public_repository_
11
# It supports pushing or pulling the changes via ssh and svn tools.
12
# It is intended to be run manually or from cron.
14
#######################################################################
16
# Things you have to take care of:
18
# 1. You need write access to the directory structure on both boxes
19
# for more see the warning at:
20
# http://svnbook.red-bean.com/html-chunk/ch06s03.html
22
# 2. For running it from cron i suggest the use of the ssh agent e.g
23
# via keychain <http://www.gentoo.org/proj/en/keychain.xml>
24
# in general read "man ssh-agent" and "man ssh-keygen".
26
# 3. Do NOT run it from post commit scripts it can lead to broken public
29
# 4. You do not have to be afraid of the public repos. If the pipe is
30
# broken nothing will be committed to it.
31
# 21:40:47 <@sussman> tberman: welcome to atomic commits.
33
# 5. For local syncing use "svnadmin hotcopy"
34
# see: "svnadmin help hotcopy"
36
#######################################################################
39
# - Martin Furter <mf@rola.ch>
40
# - Marcus Rückert <darix@irssi.org>
41
# - Joerg Sonnenberger <joerg@bec.de>
43
# with suggestions from:
44
# - tberman (#svn at freenode)
45
# - he actually needed such a script :)
46
# - Erik Huelsmann <e.huelsmann@gmx.net>
47
# - the little if for remote version :)
49
# - Ben Collins-Sussman <sussman@collab.net>
50
# - for some help with the svn commands
51
# - Bjørn Magnus Mathisen <epic@generation.no>
52
# - for pointing out i forgot to replace one ssh with $LSSH
53
# - John Belmonte <john@neggie.net>
54
# - for pointing out that we use stderr for a non-error message
57
# our biggest users atm are Mono Project and MonoDevelop
58
# Mono Team currently mirrors the repos every 30 minutes!:)
59
# see http://www.mono-project.com/contributing/anonsvn.html
62
# The same as svn itself. for latest version check:
63
# http://svn.collab.net/repos/svn/trunk/subversion/LICENSE
65
# Thanks to the subversion team for their great work.
68
# If you do not like our solution check:
70
# + http://svn.collab.net/repos/svn/trunk/contrib/client-side/svn-push/svn-push.c
72
# + https://open.datacore.ch/read-only/
73
# - SVN::Mirror and SVN::Web
74
# + http://svn.elixus.org/repos/member/clkao/
75
# + http://svn.elixus.org/svnweb/repos/browse/member/clkao/
80
# + print a non-error message to stdout instead of stderr.
82
# + added DUMPPARAMS to specify the params for the svnadmin dump call
83
# + added note about svnadmin 1.1 and "--deltas" cmdline option
84
# * made the script POSIX sh-compatible
85
# (zsh in native-mode does not work atm!)
86
# * changed handling of default settings
87
# * created config file has all lines commented out now
88
# + check if necessary values are set
89
# + added note about current users
90
# * fixed documentation in the default config
93
# + added comandline switch -v which shows the version.
94
# + using markers now to find the config so it's not twice in this script
95
# + added/changed some more documentation
98
# + added comandline switches: -C (create config) -f (read config file)
99
# -h (help) -q (quiet).
104
#######################################################################
106
# uncomment this line for debugging
109
#_CONFIG_START_MARKER
110
#######################################################################
112
# svnmirror default config
114
# Everything starting with "R" refers to the remote host
115
# with "L" to the local host
117
# the required variables are LREPOS, RREPOS and RHOST
119
#######################################################################
123
# push == Mirror from local repository to remote (readonly) repository
124
# pull == Mirror from remote repository to local (readonly) repository
129
# keychain = path to keychain sh file
130
# If the variable is not set, the script tries to read
131
# "$HOME/.keychain/`uname -n`-sh", if it is readable.
138
# see "svnadmin help dump" for it.
139
# default is "--incremental"
141
# if you use svn 1.1 on both you should add "--deltas"
142
# it should speed up the transfer on slow lines.
144
DFLT_DUMPPARAMS="--incremental"
146
# Absolute path of svnlook on the local host.
147
DFLT_LSVNLOOK="/usr/bin/svnlook"
149
# Absolute path of svnadmin on the local host.
150
DFLT_LSVNADMIN="/usr/bin/svnadmin"
152
# Absolute path to the repository on the local host.
154
DFLT_LREPOS="/path/to/repos"
156
# Absolute path of ssh on the local host.
157
# '-c blowfish -C' to speed up the transfer a bit :)
158
DFLT_LSSH="/usr/bin/ssh -c blowfish -C"
160
# Name or IP of the remote host.
164
# UNIX username on the remote host.
165
# defaults to the local username.
168
# Absolute path of svnlook on the remote host.
169
DFLT_RSVNLOOK="/usr/bin/svnlook"
171
# Absolute path of svnadmin on the remote host.
172
DFLT_RSVNADMIN="/usr/bin/svnadmin"
174
# Absolute path to the repository on the remote host.
176
DFLT_RREPOS="/path/to/repos"
178
# Additional commands you want to run before loading the dump in to the repos
179
# e.g. setting umask or change group via newgrp.
181
# Make sure the last char is a ";".
187
#######################################################################
189
# create a config file
191
#######################################################################
196
if [ -f "${CFGFILE}" ]; then
197
echo "config file '${CFGFILE}' already exists." >&2
201
if [ ! -f "$0" ]; then
202
SVNMIRROR=`which "$0"`
203
if [ $? -ne 0 ]; then
204
echo "could not locate $0" >&2
208
STARTLINE=`grep -n "^#_CONFIG_START_MARKER" "$SVNMIRROR" | sed 's/:.*$//'`
209
ENDLINE=`grep -n "^#_CONFIG_END_MARKER" "$SVNMIRROR" | sed 's/:.*$//'`
210
ENDLINE=`expr ${ENDLINE} - 1`
211
LINECOUNT=`expr ${ENDLINE} - ${STARTLINE}`
212
head -n ${ENDLINE} "$SVNMIRROR" | tail -$LINECOUNT | sed -e 's/^#/##/' -e 's/^DFLT_/# /g' | \
213
sed 's/default config/config/' > "$CFGFILE"
216
#######################################################################
218
# parse the commandline
220
#######################################################################
228
echo " svnmirror.sh [-f configfile] [-h] [-q] [-v]"
229
echo " svnmirror.sh -C configfile"
231
echo " -C configfile create a config file"
232
echo " -f configfile read config from the specified file"
233
echo " -h show this help"
234
echo " -q quiet (-q -q for really quiet)"
235
echo " -v show version"
237
echo "Note: For -f, configfile is relative to the current PATH settings"
244
while getopts C:f:hqv OPTION; do
247
create_config "${OPTARG}"
251
if [ ${CONFIGREAD} = true ]; then
252
echo "config already read" >&2
254
elif [ ! -r "${OPTARG}" ]; then
255
echo "cannot read config file '${OPTARG}'" >&2
266
if [ -z "${QUIET}" ]; then
273
echo "svnmirror.sh $VERSION"
277
echo " for help use $0 -h"
283
#######################################################################
287
#######################################################################
288
MODE="${MODE:-$DFLT_MODE}"
289
DUMPPARAMS="${DUMPPARAMS:-$DFLT_DUMPPARAMS}"
290
LSVNLOOK="${LSVNLOOK:-$DFLT_LSVNLOOK}"
291
LSVNADMIN="${LSVNADMIN:-$DFLT_LSVNADMIN}"
292
LSSH="${LSSH:-$DFLT_LSSH}"
293
RUSER="${RUSER:-$USER}"
294
RSVNLOOK="${RSVNLOOK:-$DFLT_RSVNLOOK}"
295
RSVNADMIN="${RSVNADMIN:-$DFLT_RSVNADMIN}"
296
LADDITIONAL="${LADDITIONAL:-$DFLT_LADDITIONAL}"
297
RADDITIONAL="${RADDITIONAL:-$DFLT_RADDITIONAL}"
299
echo "${LREPOS:?Required variable LREPOS is not set in config file}" > /dev/null
300
echo "${RREPOS:?Required variable RREPOS is not set in config file}" > /dev/null
301
echo "${RHOST:?Required variable RHOST is not set in config file}" > /dev/null
303
KEYCHAIN="${KEYCHAIN:-$HOME/.keychain/`uname -n`-sh}"
304
[ -n "${KEYCHAIN}" -a -r "${KEYCHAIN}" ] && . ${KEYCHAIN}
306
#######################################################################
310
#######################################################################
314
# getting version of the remote repository
316
RVERSION=`${LSSH} ${RUSER}@${RHOST} ${RSVNLOOK} youngest ${RREPOS}`
317
if [ -z "${RVERSION}" ] ; then
318
echo "getting version of remote repository failed" >&2
323
# getting version of the local repository
326
LVERSION=`${LSVNLOOK} youngest ${LREPOS}`
327
if [ -z "${LVERSION}" ] ; then
328
echo "getting version of local repository failed" >&2
333
# compare revision numbers
335
if [ ${RVERSION} -eq ${LVERSION} ] ; then
336
[ ${VERBOSE} = true ] && \
337
echo "both repositories are already at ${LVERSION}"
345
if [ ${MODE} = "push" ] ; then
346
if [ ${RVERSION} -gt ${LVERSION} ] ; then
347
echo "revision of remote repos is higher than local one" >&2
350
REVRANGE="`expr ${RVERSION} + 1`:${LVERSION}"
351
[ ${VERBOSE} = true ] && \
352
echo -n "syncing r${REVRANGE} to ${RHOST} ";
353
${LSVNADMIN} dump ${QUIET} ${DUMPPARAMS} -r${REVRANGE} ${LREPOS} | \
354
${LSSH} ${RUSER}@${RHOST} "${RADDITIONAL} ${RSVNADMIN} load ${QUIET} ${RREPOS}" || \
356
elif [ ${MODE} = "pull" ] ; then
357
if [ ${LVERSION} -gt ${RVERSION} ] ; then
358
echo "revision of local repos is higher than remote one" >&2
361
REVRANGE="`expr ${LVERSION} + 1`:${RVERSION}"
362
[ ${VERBOSE} = true ] && \
363
echo -n "syncing r${REVRANGE} from ${RHOST} ";
364
${LSSH} ${RUSER}@${RHOST} \
365
"${RADDITIONAL} ${RSVNADMIN} dump ${QUIET} ${DUMPPARAMS} -r${REVRANGE} ${RREPOS}" | \
366
${LSVNADMIN} load ${QUIET} ${LREPOS} || \
369
echo "invalid mode \"${MODE}\" specified!" >&2
372
if [ ${RC} -ne 0 ]; then
375
[ ${VERBOSE} = true ] && \
376
echo "successfull completed."