Поднял на днях для личных нужд сервер с Mercurial репозиториями. С авторизацией и шифрованием, как и положено личным нуждам. Почему не Git? Да кто его знает. Git’а мне и на работе хватает, кроме того, уж очень инопланетянский у него интерфейс. Mercurial в этом плане мне более симпатичен. А почему не CVS/SVN? Мне нравится возможность иметь локально репозиторий со всей историей. Кроме того в DVCS работа с ветками сделана поприятнее.
В общем-то в процессе поднимания ничего особенного нет, вся информация доступна в Интернете. Но для интересующихся приведу последовательность команд с краткими комментариями. Установка происходит на Debian Lenny с nginx и без Apache.
Ставим пакеты mercurial и apache2-utils. Последний понадобится для создания файла с паролями для авторизации.
apt-get install mercurial apache2-utils
Создаем пользователя, из под которого будет работать сервер.
useradd -c "Mercurial Server" -d /var/hg -r -s /bin/false -U hg
Создаем структуру директорий.
mkdir -p /var/hg/{conf,logs,repos}
Ставим правильные права.
chown hg:hg /var/hg/{logs,repos} chmod o-rwx /var/hg/{logs,repos}
Должно получиться как-то так:
# ls -l /var/hg total 12 drwxr-xr-x 2 root root 4096 2010-05-14 15:53 conf drwxr-x--- 2 hg hg 4096 2010-05-14 16:19 logs drwxr-x--- 3 hg hg 4096 2010-05-14 16:14 repos
Создаем пробный репозиторий.
su -s /usr/bin/hg hg init /var/hg/repos/test
Проверяем.
# ls -l /var/hg/repos total 4 drwxr-xr-x 3 hg hg 4096 2010-05-17 10:06 test
Теперь создаем файл /var/hg/conf/webdir.conf следующего содержания:
[collections] /var/hg/repos = /var/hg/repos
Такой конфиг позволяет не описывать репозитории по одному, а указать все сразу в виде коллекции. Еще нам понадобится файл /var/hg/conf/hgrc, который мы будем копировать в каждый новый репозиторий. В этом файле отключается встроенный в Mercurial SSL (потому что мы будем использовать SSL в nginx) и разрешается всем push (потому что мы будем использовать авторизацию в nginx).
# cat /var/hg/conf/hgrc [web] push_ssl = false allow_push = * # cp /var/hg/conf/hgrc /var/hg/repos/test/.hg/
Все готово для запуска сервера. Чтобы было совсем хорошо, создаем скрипт /etc/init.d/hg-serve с таким содержимым:
#!/bin/sh ### BEGIN INIT INFO # Provides: hg-serve # Required-Start: $remote_fs # Required-Stop: $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Mercurial server ### END INIT INFO PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="Mercurial server" NAME=hg HOME=/var/$NAME DAEMON=/usr/bin/$NAME PIDFILE=$HOME/logs/$NAME.pid DAEMON_ARGS="serve -d -A $HOME/logs/access.log -E $HOME/logs/error.log \ -p 9001 -a 127.0.0.1 --webdir-conf $HOME/conf/webdir.conf \ --pid-file $PIDFILE" SCRIPTNAME=/etc/init.d/$NAME [ -x "$DAEMON" ] || exit 0 . /lib/init/vars.sh . /lib/lsb/init-functions do_start() { start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \ --test > /dev/null || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \ --chuid hg:hg -- $DAEMON_ARGS || return 2 } do_stop() { start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \ --pidfile $PIDFILE --name $NAME RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 \ --exec $DAEMON [ "$?" = 2 ] && return 2 rm -f $PIDFILE return "$RETVAL" } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; restart|force-reload) log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 exit 3 ;; esac :
Добавляем в автозагрузку.
update-rc.d hg-serve defaults
И запускаем.
/etc/init.d/hg-serve start
Проверяем, что все работает.
# hg clone http://localhost:9001/test destination directory: test no changes found updating working directory 0 files updated, 0 files merged, 0 files removed, 0 files unresolved # cd test # touch test # hg add test # hg commit -m test No username found, using 'root@example.com' instead # hg push pushing to http://localhost:9001/test searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files # cd .. && rm -rf test
Теперь настраиваем nginx. Сначала заводим пользователя с паролем для авторизации.
htpasswd -c /var/hg/conf/htpasswd user
Далее создаем самоподписанный (а для личных нужд другого и не требуется) SSL сертификат. Привожу только команды, подробнее о процессе можно почитать, например, тут.
cd /var/hg/conf openssl genrsa -des3 -out server.key 1024 openssl req -new -key server.key -out server.csr cp server.key server.key.org openssl rsa -in server.key.org -out server.key openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
Исправляем права для секретных файлов.
chgrp www-data /var/hg/conf/{htpasswd,server.*} chmod o-rwx /var/hg/conf/{htpasswd,server.*}
Проверяем.
# ls -l /var/hg/conf total 28 -rw-r--r-- 1 root root 38 2010-05-14 15:41 hgrc -rw-r----- 1 root www-data 19 2010-05-17 10:30 htpasswd -rw-r----- 1 root www-data 1001 2010-05-14 15:45 server.crt -rw-r----- 1 root www-data 725 2010-05-14 15:45 server.csr -rw-r----- 1 root www-data 891 2010-05-14 15:45 server.key -rw-r----- 1 root www-data 963 2010-05-14 15:45 server.key.org -rw-r--r-- 1 root root 44 2010-05-14 15:53 webdir.conf
Создаем конфиг для nginx /etc/nginx/sites-available/hg.example.com:
server { listen 443; server_name hg.example.com; access_log /var/log/nginx/hg.example.com.access.log; ssl on; ssl_certificate /var/hg/conf/server.crt; ssl_certificate_key /var/hg/conf/server.key; ssl_session_timeout 5m; ssl_protocols SSLv2 SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; ssl_prefer_server_ciphers on; location / { auth_basic "Mercurial Repositories"; auth_basic_user_file /var/hg/conf/htpasswd; proxy_pass http://localhost:9001; } }
Включаем конфиг и перезапускаем nginx.
cd /etc/nginx/sites-enabled ln -s /etc/nginx/sites-available/hg.example.com /etc/init.d/nginx restart
Финальная проверка с удаленной машины.
$ hg clone https://hg.example.com/test http authorization required realm: Mercurial Repositories user: user password: destination directory: test requesting all changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd test $ ls test $ echo >>test $ hg commit -m test $ hg push http authorization required realm: Mercurial Repositories user: user password: pushing to https://hg.example.com/test searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files