Поддался я влиянию моды и захватывающим перспективам DVCS с недавних пор. Это вытолкнуло меня с наезженной колеи Subversion + Trac и заставило искать новые схемы как хранить исходные тексты в разных компаниях. И предоставлять для них удобный доступ разработчикам, заказчикам и другим заинтересованным личностям.
Так сложилось, что я специализируюсь во FreeBSD и не так хорошо разбираюсь в Linux'ах. И еще предпочитаю где можно использовать Nginx вместо Apache httpd. Поэтому решил я сделать для себя унифицированную архитектуру, которая позволит хранить неограниченное количество репозиториев и разграничивать для них доступ различных группам людей на этой платформе.
Само собой, Bitbucket — наше все. Но у любого разработчика есть закрытые проекты, которые в паблик выкладывать не хотелось бы. Можно, конечно, платить $50 в месяц за возможность хостить 25 проектов на bitbucket'е. Я лично считаю, что лучше эти деньги потратить на dedicated сервер и поднять себе сколько угодно проектов. Будет не так удобно, но зато свое и с возможностью тюнинга, бекапа и других вкусностей.
Необходимо иметь хранилище репозиториев Mercurial с возможностью доступа к ним через web. Права доступа должны указываться чтение/запись для конкретных пользователей.
Детально о методах развертывания нескольких репозиториев можно ознакомиться на официальной странице. Для этого используется cgi скрипт hgwebdir.cgi. Мы, само-собой, будем использовать его FastCGI версию hgwebdir.fcgi.
Mercurial не включает в поставку самодостаточного FastCGI сервера, который можно запустить на нужном порту. Предполагается, что hgwebdir.fcgi будет запущен средствами apache httpd или lighttpd. Чтоб решить эту задачу, воспользуемся fastcgi wrapper'ом spawn-fcgi.
Итак, первым делом устанавливаем следующие порты
Копируем дефолтный скрипт в нашу директорию с настройками
Редактируем наш hgwebdir.fcgi. У меня он выглядит вот так
Далее в фале конфигурации /usr/home/www/cgi/hgweb.config прописываем пути к коллекциям и настройку адреса
Параметр baseurl говорит о том, что все ссылки, которые будет генерировать hgwebdir будут содержать /hg вначале пути (помните, у нас адрес, по которому должны быть доступны репозитории dev.example.com/hg/)
В файле конфигурации FreeBSD /etc/rc.conf добавляем следующие строки
В качестве spawn_fcgi_app можно указать сам скрипт, а не путь к интерпретатору питона. Но тогда сервис не будет корректно останавливаться, т.к. системные скрипты будут искать процесс с именем hgwebdir.fcgi чтоб остановить, а реально процесс называется python, т.к. hgwebdir.fcgi — это программа, запускаемая с помощью интерпретатора.
Теперь можно запускать демона
В файле конфигурации /usr/local/etc/nginx/nginx.conf создайте новую секцию server примерно следующего содержания
В этом месте будьте предельно внимательны. Nginx по умолчанию не устанавливает переменных, которые необходимы для коректной работы с mercurial. Поэтому я постараюсь объяснить значение каждой строки. Мне они давались изучением исходников mercurial'а :)
set_real_ip_from, real_ip_header — как я упоминал выше, внешний https доступ обслуживает отдельный nginx, стоящий на сервере, который смотрит в мир. Чтоб на нашем локальном сервере в логи попадали реальные адреса клиентов, а не адрес этого внешнего сервера, который проксирует запросы, и были добавлены эти директивы.
if блок и переменная PATH_INFO — для определения репозитория, к которому идет обращение, hgwebdir использует переменную PATH_INFO. Нам нужно отрезать начало пути /hg, т.к. в противном случае hgwebdir будет думать, что вы пытаетесь обратиться к репозиторию под именем «hg». Здесь нужно быть внимательным и для вычисления PATH_INFO использовать переменную nginx'а $uri, а не $request_uri, как упоминают некоторые мануалы в интернете. Т.к. $request_uri содержит в себе еще и $args помимо пути. И если передать его в PATH_INFO, то вы будете получать 404 Not Found при попытке работать с репозиторием.
client_max_body_size — по умолчанию, максимальная длина запроса к nginx 1М. Естественно, если вы попытаетесь закомитить изменений больше, чем на 1М, или сделать первоначальный push большого репозитория, то запрос не пройдет. Поэтому этот размер увеличен до 100М. Думаю, в большинстве случаев этого достаточно.
include fastcgi_params — подключаем другие стандартные переменные типа QUERY_STRING и т.д.
REMOTE_USER — в этой переменной hgwebdir ожидает получить имя пользователя.
Ну и последние две строчки — настройка http basic auth авторизации.
Все, теперь можете поправить другие, не связанные с hg параметры в nginx.conf (например, куда логи складывать) и запускать веб сервер.
Добавляем
Теперь мы готовы создать первый репозиторий. Все делается от пользователя www, т.к. именно под ним работает nginx и hgwebdir и ему нужны права на запись в эту директорию. Давайте сделаем репозиторий test. Дадим права на чтение пользователю user1 и права на чтение/запись пользователю user2.
Редактируем test/.hg/hgrc чтоб он выглядел так
Готово, можно проверять доступ.
Помимо повального мигрирования с subversion на mercurial я сейчас пристально смотрю на redmine вместо trac. Мне понравилась идея, что пользователи могут сами регистрироваться и создавать проекты. К сожалению, я не нашел готового решения как сделать автоматическое создание hg репозитория для созданного проекта. Причем с клонированием прав доступа по описанной в этой статье схеме. Если кто-то знает — буду благодарен за ссылочку. В любом случае, я собираюсь решить эту задачу в ближайшем будущем либо написанием плагина к redmine, либо более другим/правильным способом.
Если вам понравилась эта статья и вы бы хотели увидеть продолжение, оставляейте отзывы. Я тогда поделюсь новым знаниями интеграции нашего нового окружения с redmine.
Так сложилось, что я специализируюсь во FreeBSD и не так хорошо разбираюсь в Linux'ах. И еще предпочитаю где можно использовать Nginx вместо Apache httpd. Поэтому решил я сделать для себя унифицированную архитектуру, которая позволит хранить неограниченное количество репозиториев и разграничивать для них доступ различных группам людей на этой платформе.
Само собой, Bitbucket — наше все. Но у любого разработчика есть закрытые проекты, которые в паблик выкладывать не хотелось бы. Можно, конечно, платить $50 в месяц за возможность хостить 25 проектов на bitbucket'е. Я лично считаю, что лучше эти деньги потратить на dedicated сервер и поднять себе сколько угодно проектов. Будет не так удобно, но зато свое и с возможностью тюнинга, бекапа и других вкусностей.
Постановка задачи
Необходимо иметь хранилище репозиториев Mercurial с возможностью доступа к ним через web. Права доступа должны указываться чтение/запись для конкретных пользователей.
Конкретно по пунктам
- Репозитории должны храниться в директории /usr/home/www/repos/
- Настройки Mercurial хранятся в /usr/home/www/repos/cgi/
- Хостинг происходит на локальном сервере. Поэтому доступ только по протоколу http. Снаружи доступ будет по https обслуживаться уже другим сервером так-же на базе nginx и перебрасываться на локальный на 80-й порт.
- Допустим, сервер будет называться dev.example.com. Список репозиториев должен быть доступен по адресу
http://dev.example.com/hg/
Установка необходимых компонентов
Детально о методах развертывания нескольких репозиториев можно ознакомиться на официальной странице. Для этого используется cgi скрипт hgwebdir.cgi. Мы, само-собой, будем использовать его FastCGI версию hgwebdir.fcgi.
Mercurial не включает в поставку самодостаточного FastCGI сервера, который можно запустить на нужном порту. Предполагается, что hgwebdir.fcgi будет запущен средствами apache httpd или lighttpd. Чтоб решить эту задачу, воспользуемся fastcgi wrapper'ом spawn-fcgi.
Итак, первым делом устанавливаем следующие порты
- www/spawn-fcgi
- devel/mercurial
- www/nginx
- www/py-flup
Настраиваем hgwebdir
Копируем дефолтный скрипт в нашу директорию с настройками
sudo -u www mkdir /usr/home/www/cgi/
cp /usr/local/share/mercurial/www/hgwebdir.fcgi /usr/home/www/cgi/
Редактируем наш hgwebdir.fcgi. У меня он выглядит вот так
#!/usr/bin/env python
from mercurial import demandimport; demandimport.enable()
from mercurial.hgweb.hgwebdir_mod import hgwebdir
from flup.server.fcgi import WSGIServer
WSGIServer(hgwebdir('/usr/home/www/cgi/hgweb.config')).run()
Далее в фале конфигурации /usr/home/www/cgi/hgweb.config прописываем пути к коллекциям и настройку адреса
[collections]
/usr/home/www/repos = /usr/home/www/repos
[web]
baseurl = /hg
Параметр baseurl говорит о том, что все ссылки, которые будет генерировать hgwebdir будут содержать /hg вначале пути (помните, у нас адрес, по которому должны быть доступны репозитории dev.example.com/hg/)
Настраиваем spawn-fcgi
В файле конфигурации FreeBSD /etc/rc.conf добавляем следующие строки
# Mercurial
spawn_fcgi_enable="YES"
spawn_fcgi_app="/usr/local/bin/python"
spawn_fcgi_app_args="/usr/home/www/cgi/hgwebdir.fcgi"
spawn_fcgi_pidfile="/var/run/hg.pid"
spawn_fcgi_bindsocket="/var/run/hg.socket"
В качестве spawn_fcgi_app можно указать сам скрипт, а не путь к интерпретатору питона. Но тогда сервис не будет корректно останавливаться, т.к. системные скрипты будут искать процесс с именем hgwebdir.fcgi чтоб остановить, а реально процесс называется python, т.к. hgwebdir.fcgi — это программа, запускаемая с помощью интерпретатора.
Теперь можно запускать демона
sudo /usr/local/etc/rc.d/spawn-fcgi start
Настраиваем nginx
В файле конфигурации /usr/local/etc/nginx/nginx.conf создайте новую секцию server примерно следующего содержания
server { listen 80; server_name dev.example.com; set_real_ip_from 172.16.224.1; real_ip_header X-Forwarded-For; location / { root /usr/local/www/nginx; index index.html index.htm; } location /hg { if ($uri ~ /hg(/.*)$) { set $path $1; } client_max_body_size 100M; fastcgi_pass unix:/var/run/hg.socket; include fastcgi_params; fastcgi_param PATH_INFO $path; fastcgi_param REMOTE_USER $remote_user; auth_basic "Mercurial repositories"; auth_basic_user_file hg_htpasswd; } }
В этом месте будьте предельно внимательны. Nginx по умолчанию не устанавливает переменных, которые необходимы для коректной работы с mercurial. Поэтому я постараюсь объяснить значение каждой строки. Мне они давались изучением исходников mercurial'а :)
set_real_ip_from, real_ip_header — как я упоминал выше, внешний https доступ обслуживает отдельный nginx, стоящий на сервере, который смотрит в мир. Чтоб на нашем локальном сервере в логи попадали реальные адреса клиентов, а не адрес этого внешнего сервера, который проксирует запросы, и были добавлены эти директивы.
if блок и переменная PATH_INFO — для определения репозитория, к которому идет обращение, hgwebdir использует переменную PATH_INFO. Нам нужно отрезать начало пути /hg, т.к. в противном случае hgwebdir будет думать, что вы пытаетесь обратиться к репозиторию под именем «hg». Здесь нужно быть внимательным и для вычисления PATH_INFO использовать переменную nginx'а $uri, а не $request_uri, как упоминают некоторые мануалы в интернете. Т.к. $request_uri содержит в себе еще и $args помимо пути. И если передать его в PATH_INFO, то вы будете получать 404 Not Found при попытке работать с репозиторием.
client_max_body_size — по умолчанию, максимальная длина запроса к nginx 1М. Естественно, если вы попытаетесь закомитить изменений больше, чем на 1М, или сделать первоначальный push большого репозитория, то запрос не пройдет. Поэтому этот размер увеличен до 100М. Думаю, в большинстве случаев этого достаточно.
include fastcgi_params — подключаем другие стандартные переменные типа QUERY_STRING и т.д.
REMOTE_USER — в этой переменной hgwebdir ожидает получить имя пользователя.
Ну и последние две строчки — настройка http basic auth авторизации.
Все, теперь можете поправить другие, не связанные с hg параметры в nginx.conf (например, куда логи складывать) и запускать веб сервер.
Добавляем
nginx_enable="YES"
в /etc/rc.conf и делаем sudo /usr/local/etc/rc.d/nginx start
Пример создания репозитория
Теперь мы готовы создать первый репозиторий. Все делается от пользователя www, т.к. именно под ним работает nginx и hgwebdir и ему нужны права на запись в эту директорию. Давайте сделаем репозиторий test. Дадим права на чтение пользователю user1 и права на чтение/запись пользователю user2.
# Идем в директорию с репозиториями
cd /usr/home/www/repos
# Создаем новый репозиторий
sudo -u www hg init test
# Добавляем пользователей в nginx
# -c - создать файл
# htpasswd - из пакета www/apache22
sudo htpasswd -b -c /usr/local/etc/nginx/hg_htpasswd user1 pass1
sudo htpasswd -b /usr/local/etc/nginx/hg_htpasswd user2 pass2
Редактируем test/.hg/hgrc чтоб он выглядел так
[web]
# Нам не нужен ssl, поскольку о нем заботится nginx
push_ssl = False
allow_push = user2
allow_read = user1,user2
Готово, можно проверять доступ.
Вместо заключения
Помимо повального мигрирования с subversion на mercurial я сейчас пристально смотрю на redmine вместо trac. Мне понравилась идея, что пользователи могут сами регистрироваться и создавать проекты. К сожалению, я не нашел готового решения как сделать автоматическое создание hg репозитория для созданного проекта. Причем с клонированием прав доступа по описанной в этой статье схеме. Если кто-то знает — буду благодарен за ссылочку. В любом случае, я собираюсь решить эту задачу в ближайшем будущем либо написанием плагина к redmine, либо более другим/правильным способом.
Если вам понравилась эта статья и вы бы хотели увидеть продолжение, оставляейте отзывы. Я тогда поделюсь новым знаниями интеграции нашего нового окружения с redmine.