Pull to refresh

Развертывание Mercurial репозиториев через FastCGI с использованием Nginx на FreeBSD

Reading time5 min
Views1.7K
Поддался я влиянию моды и захватывающим перспективам DVCS с недавних пор. Это вытолкнуло меня с наезженной колеи Subversion + Trac и заставило искать новые схемы как хранить исходные тексты в разных компаниях. И предоставлять для них удобный доступ разработчикам, заказчикам и другим заинтересованным личностям.

Так сложилось, что я специализируюсь во 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.
Tags:
Hubs:
Total votes 11: ↑9 and ↓2+7
Comments2

Articles