Svnsync to jeden z wielu programów wchodzący w skład pakietu subversion. Służy do synchronizacji  dwóch  repozytoriów – pierwotnego i kopii. Ze względu na tę funkcjonalność narzędzie to idealnie może służyć do tworzenia kopii całego repozytorium na innym komputerze.

Mi się przydało w osobliwej sytuacji, gdzie do dyspozycji w firmie  miałem „działowego” vps. Chciałem wprowadzić trac’a jako domyślny task manager dla wszystkich projektów z działu. Jednak repozytoria tych projektów znajdują się na innej maszynie, a dostanie się do nich wymusza otwarcie tunelu. Jednak obecna, stabilna wersja trac (0.11) nie obsługuje jeszcze zdalnego repozytorium, a administrator jest mega zajęty  i nie ma czasu na moje widzi misie 🙂

Z pomocą przyszedł program svnsync.

Zasada jest bardzo prosta – inicjujemy na komputerze (hostującym kopię) projekt kopię :

svnadmin create /home/services/subversion/kopia
cd /home/services/subversion/kopia
find -type f | xargs chmod 666
find -type d | xargs chmod 777
chown -Rf svnuser:svn *

Dalej na komputerze kopii dodajemy nowego uzytkownika w svn, który będzie mial możliwość zapisywania plików w tym repozytorium. Najprościej:

htpasswd -nmb svnsync moje_haslo >> /etc/htpasswd/svn_dav

Następnie w katalogu /home/services/subversion/kopia/hooks należy dodać pliki:

start-commit

#!/bin/sh
 
USER="$2"
 
if [ "$USER" = "svnsync" ]; then exit 0; fi
 
echo "Tylko użytkownik svnsync może dodawać nowe rewizje" >&2
exit 1

oraz plik pre-revprop-change

#!/bin/sh
 
USER="$2"
 
if [ "$USER" = "svnsync" ]; then exit 0; fi
 
echo "Tylko użytkownik svnsync może dodawać nowe rewizje" >&2
exit 1

Następnie:

chmod 775 /home/services/subversion/kopia/hooks/start-commit
chmod 775 /home/services/subversion/kopia/hooks/pre-revprop-change

Przeznaczenie tych plików? Sprawa prosta, kopia służy tak naprawdę tylko do odczytu. Nie chcę aby to repozytorium było robocze, ponieważ synchronizacja odbywa się tylko w jedną stronę. Udostępnienie kopii w formie roboczej doprowadziło by do powstania dwóch niespójnych repozytoriów.

Jest tylko jeden użytkownik tego repozytorium – svnsync, dzięki temu mam pewność, że nikt nie zaciągnie sobię tego repozytorium i nie będzie nic commit‚ował. W rezultacie uzyskam dokładną kopię repozytorium, na którym mi zależy.

Bardzo ważna uwaga! svnsync synchronizuje cały projekt, a nie jego rozgałęzienia. Nie możemy zatem synchronizować tylko wybranego katalogu.

Następnie musimy zainicjować synchronizację przez wywołanie polecenia svnsync z parametrem initialize. Jeśli w grę wchodzi kilka repozytoriów najlepiej napisać prosty skrypt np. taki:

#!/bin/sh
 
PROJECTNAME_FROM="$1"
PROJECTNAME_TO="$2"
SVNSYNC=/usr/bin/svnsync
FROM=https://host_oryginal/PROJECTNAME_FROM
TO=https://host_kopia/$PROJECTNAME_TO
SYNC_USER=svnsync
SYNC_PASS=skomplikowane_haslo
SOURCE_USER=uzytkownik_oryginalu
SOURCE_PASS=haslo_uzytkownika_oryginalu
 
$SVNSYNC initialize $TO $FROM --sync-username $SYNC_USER --sync-password $SYNC_PASS  --source-username $SOURCE_USER --source-password $SOURCE_PASS &  exit 0

Wtedy wywołanie takiego skryptu sprowadza się jedynie do:

svnsync_init oryginal kopia

Mi się przydał, bo synchronizowałem ok dziesięć projektów.

Nasuwa się dalej pytanie – jak synchronizować kopię z oryginałem?

Rozwiązań jest kilka. Można np. użyć crona,  który co okręślony interval czasu odpalał by skrypt podobny do tego:

#!/bin/sh
 
PROJECTNAME_TO="$1"
 
SVNSYNC=/usr/bin/svnsync
TO=https://host_kopia/$PROJECTNAME_TO
SYNC_USER=svnsync
SYNC_PASS=skomplikowane_haslo
SOURCE_USER=uzytkownik_oryginalu
SOURCE_PASS=haslo_uzytkownik_oryginalu
 
$SVNSYNC --non-interactive sync $TO --sync-username $SYNC_USER --sync-password $SYNC_PASS --source-username $SOURCE_USER --source-password $SOURCE_PASS &  exit 0
chmod 775 /home/services/subversion/kopia/hooks/svnsync_init

Polecenie svnsync z parametrem sync służy do synchronizowania repozytorium.

Wywołanie skryptu synchronizującego wyglądało by tak:

svnsync_sync kopia

Takie rozwiązanie jest jednak bardzo niewygodne. Poza tym, że dodatkowo co jakiś określony czas obciążamy maszynę kopię, to w dodatku zmiany w repozytorium pojawią się dopiero za chwilę (np. za 5 minut). Ja nie mogę czekać tak długo. Przypominam, że zależy mi na pracy z trac‚iem. Każdy commit musi być rejestrowany automatycznie. Poza tym w moim przypadku, aby wywolać polecenie svn up, muszę otworzyć tunel. Mogę to zrobić jedynie z ręki i nie mógłbym wtedy doprowadzić do zamknięcia tego tunelu.

Z pomocą znów przychodzą hooks na serwerze oryginał. W katalogu /home/services/subversion/oryginal/hooks edytujemy plik:

post-commit

#!/bin/sh
 
SVNSYNC=/usr/bin/svnsync
TO=https://host_kopia/kopia
SYNC_USER=svnsync
SYNC_PASS=skomplikowane_haslo
SOURCE_USER=uzytkownik_oryginal
SOURCE_PASS=haslo_uzytkownika_oryginal
 
$SVNSYNC --non-interactive sync $TO --sync-username $SYNC_USER --sync-password $SYNC_PASS --source-username $SOURCE_USER --source-password $SOURCE_PASS &  exit 0
chmod 775 /home/services/subversion/kopia/hooks/post-commit

Opcja –non-interactive spowoduje, że w razie niepowodzenia synchronizacji użytkownicy pracujący przy repozytrium nie zobaczą żadnego błędu podczas commit‚a, jeśli takowy miałby wystąpić. W praktyce takie rozwiązanie działa błyskawicznie i bezbłędnie.

Podsumowując, osiągnąłęm swój cel. Na maszynie kopia mam zainstalowanego trac‚a, który bazuje na repozytorium znajdujące się na maszynie oryginal. W praktyce Każdy commit w repozytorium oryginal spowoduje zarejestrowanie zmiany w trac’u.

Super od teraz możemy  zarządzać projektami 🙂