Групповая разработка сайтов через git — автоматическое создание/удаление сайтов из git-бранчей

от автора

В этой статье я расскажу, как создать групповую среду разработки сайтов через git с помощью git-хуков. Статья рассчитана на опытных системных администраторов, я лишь опишу алгоритм.
Многое на эту тему уже обсуждалось, а я добавлю, как автоматически создавать или удалять сайты при создании/удалении бранчей в git-репозитории. Такая возможность может пригодиться, к примеру, если над разными частями сайта работают разные программисты и нужны разные площадки (бранчи). После основной разработки и тестирования выполняется merge в основную ветку, а бранчи и тестовые сайты удаляются или архивируются.

Как это работает: по-умолчанию создается master-ветка, по которой доступен основной сайт. По необходимости программисты создают бранчи, которые автоматически становятся доступны по адресу branchname.projectname.domain.ru. Внесенные изменения тестируются и мержатся в основную ветку (будь то master или любая другая). А если бранч удалить, автоматически удаляется и одноименный сайт.

Настройка git

Создаем git-репозиторий, настраиваем его доступность по адресу factory.domain.ru/git/projectname и разрешаем авторизацию без пароля для IP, с которого будет работать сервер разработки. Настройка git-сервера неоднократно описывалась, поэтому детали этого пункта я пропущу.

Создаем git-хуки

Создаем файл /srv/git/projectname/hooks/post-update (этот скрипт будет автоматически обновлять код на сайте при изменениях в репозитории; для переменной PROJECT назначьте название проекта, а для GIT_URL — ссылку к git-репозиторию):

#!/bin/sh  PATH=/usr/bin:/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH PROJECT="projectname" GIT_URL="http://factory.domain.ru/git"  git update-server-info  if [ ! -f $1 ]; then     exit 0 fi  while [ -n "$1" ] do     REF=`echo $1 | awk --field-separator="/" '{print $2}'`     if [ $REF = "branches" -o $REF = "heads" ]; then         BRANCH=`echo $1 | awk --field-separator="/" '{print $3}'`          if [ ! -d /srv/www/$PROJECT/repo/master ]; then             mkdir -p /srv/www/$PROJECT/repo             GIT_SSL_NO_VERIFY=true git clone $GIT_URL/$PROJECT /srv/www/$PROJECT/repo/master         fi          if [ ! -d /srv/www/$PROJECT/repo/$BRANCH ]; then             GIT_SSL_NO_VERIFY=true git clone -b $BRANCH $GIT_URL/$PROJECT /srv/www/$PROJECT/repo/$BRANCH         else             cd /srv/www/$PROJECT/repo/$BRANCH             GIT_SSL_NO_VERIFY=true git fetch origin             GIT_SSL_NO_VERIFY=true git reset --hard origin/$BRANCH             GIT_SSL_NO_VERIFY=true git clean -d -f             GIT_SSL_NO_VERIFY=true git checkout             GIT_SSL_NO_VERIFY=true git pull         fi     fi     shift done 

Создаем файл /srv/git/projectname/hooks/update (этот скрипт будет автоматически создавать/удалять сайты при создании/удалении новых бранчей; для переменной PROJECT назначьте название проекта):

#!/bin/sh  refname="$1" oldrev="$2" newrev="$3"  PROJECT="projectname"  # --- Safety check if [ -z "$GIT_DIR" ]; then         echo "Don't run this script from the command line." >&2         echo " (if you want, you could supply GIT_DIR then run" >&2         echo "  $0 <ref> <oldrev> <newrev>)" >&2         exit 1 fi  if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then         echo "Usage: $0 <ref> <oldrev> <newrev>" >&2         exit 1 fi  # --- Check types # if $newrev is 0000...0000, it's a commit to delete a ref. zero="0000000000000000000000000000000000000000" if [ "$newrev" = "$zero" ]; then         newrev_type=delete else         newrev_type=$(git cat-file -t $newrev) fi  BRANCH=`echo $1 | awk --field-separator="/" '{print $3}'`  delete () {     mv /srv/www/$PROJECT/repo/$BRANCH /srv/www/$PROJECT/repo/$BRANCH.removed_by_git     rm -rf /srv/www/$PROJECT/repo/$BRANCH.removed_by_git }  case "$refname","$newrev_type" in         refs/heads/*,delete)                 # delete branch                 delete                 ;;         refs/remotes/*,delete)                 # delete tracking branch                 delete                 ;; esac  exit 0 
Настраиваем nginx и apache для поддержки доменов branch.projectname.domain.ru

Создаем vhost-конфиг и подключаем его к основному конфигу nginx:

server {     listen 80;     # projectname замените на название проекта, а domain - на основную часть домена. master-ветка будет доступна по адресу projectname.domain.ru     server_name ~^(?P<branch>.*)\.projectname\.domain\.ru$ projectname.domain.ru;      if ($branch = "") {         set $branch "master";     }      access_log /srv/www/projectname/logs/projectname.domain.ru-acc main;     error_log /srv/www/projectname/logs/projectname.domain.ru-err;              # Пример проксирования трафика на apache mod_php     location / {         proxy_pass http://127.0.0.1:8080;         proxy_redirect off;         proxy_set_header Host $host;         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         proxy_read_timeout 300;         client_max_body_size 256m;         proxy_buffer_size 16k;         proxy_buffers 32 16k;     }      # За исключением папки "data", которая будет доступна по адресу http://projectname.domain.ru/data. Из этой папки отдается только статика, даже если будут загружены исполняемые файлы.     # Я сделал, чтобы запись через php была разрешена только в data, таким образом можно избежать git-конфликтов, а так же частично обезопаситься от уязвимостей в коде.     # Если на уровне кода сложно сделать загрузку файлов вне DOCUMENT_ROOT, можно сделать симлинк и добавить его в репозиторий.     # Таким образом, права на /srv/www/projectname/data делаем, к примеру, apache:apache (или chmod 777), а на /srv/www/projectname/repo - username:username.     location ^~ /data/ {         root /srv/www/projectname;     }      # Статику отдаем в обход apache.     location ~* \.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|swf|flv|avi|djvu|mp3|mp4|ogv)$ {         root /srv/www/projectname/repo/$branch/htdocs;     }      # Запрещаем доступ к папке ".git".     location ~ /\.git {         deny all;     }      # Если площадка используется исключительно для разработки, рекомендую создать файл /srv/www/robots.txt с содержимым, запрещающим индексацию сайта (комментарии в начале строк в robots.txt нужно убрать):     # User-Agent: *     # Disallow: /     # Таким образом, внезависимости от того, есть в репозитории robots.txt или нет, поисковики будут получать файл из /srv/www, который будет запрещать индексацию разрабатываемого сайта.     location = /robots.txt {         root /srv/www;     } } 

Остался лишь vhost-конфиг для apache:

<VirtualHost *>     # Хардкод-домен для master-бранча     DocumentRoot /srv/www/projectname/repo/master/htdocs     ServerName projectname.domain.ru     ErrorLog /srv/www/projectname/logs/projectname.domain.ru-err      <Location />         php_admin_value open_basedir "/usr/share/pear:/srv/www/projectname:/tmp"         php_admin_value upload_tmp_dir "/srv/www/projectname/tmp"         php_admin_value session.save_path "/srv/www/projectname/tmp"         php_admin_value memory_limit "256M"         php_value post_max_size "256M"         php_value upload_max_filesize "256M"     </Location>      <Directory /srv/www/projectname/repo>         Options Includes FollowSymLinks MultiViews         AllowOverride All         Order allow,deny         Allow from all     </Directory> </VirtualHost>  <VirtualHost *>     # Реализация "бранчей на лету" с использованием VirtualDocumentRoot     # При использовании rewrite-правил через .htaccess в рамках VirtualDocumentRoot может возникать ошибка 500, в этом случае может помочь настройка "RewriteBase /" в .htaccess.     VirtualDocumentRoot /srv/www/projectname/repo/%1/htdocs     ServerName dev.projectname.domain.ru     ServerAlias *.projectname.domain.ru     ErrorLog /srv/www/projectname/logs/projectname.domain.ru-err      <Location />         php_admin_value open_basedir "/usr/share/pear:/srv/www/projectname:/tmp"         php_admin_value upload_tmp_dir "/srv/www/projectname/tmp"         php_admin_value session.save_path "/srv/www/projectname/tmp"         php_admin_value memory_limit "256M"         php_value post_max_size "256M"         php_value upload_max_filesize "256M"     </Location>      <Directory /srv/www/projectname/repo>         Options Includes FollowSymLinks MultiViews         AllowOverride All         Order allow,deny         Allow from all     </Directory> </VirtualHost> 

Вот и все! Этот алгоритм можно прикрутить, например, к Redmine для автоматического создания репозиториев при создании новых проектов, а так же для управления правами доступа. Хорошей идеей будет написать плагин с разграничением прав доступа, который даст возможность делать деплой или роллбек на продакшен (в том числе с использованием Capistrano).

Подобную реализацию мы используем для большого рекламного агентства со штатом больше 100 человек и им очень нравится, ведь автоматизация значительно уменьшает ошибки, допущенные за счет человеческого фактора, а главное — не надо ночью будить админа, чтобы создать новый срочный проект или прописать доступы.

Пишите в комментариях, какие вопросы по этой теме вам наиболее интересны — в новой статье обязательно опишу!

ссылка на оригинал статьи http://habrahabr.ru/post/158855/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *