У меня есть работоспособный вариант, на основе гайда (ссылку не найду) и нескольких гугло-часов поисков.
Вариант 100% рабочий, но буду рад если кто-то заметит в нем косяки и поправит/посоветует как лучше.
И так
VPS с Ubuntu, на нем установлен соответственно git и созданы 2 папки под сайты
test.example.com (тестовая версия сайта)
и example.com (боевая версия)
Создать на сервере директорию для репозиториев, например:
sudo mkdir /var/www/git
Далее я дал права на папку с сайтами и репозиториями пользователю www-data (собственно с правами и была основная морока при реализации)
sudo chown -R www-data:www-data /var/www
И все делал от имени этого пользователя (под ним работает apache или nginx).
Сгенерировал ему ключи
sudo -u www-data ssh-keygen -t rsa
Создал конфиг файл
sudo -u www-data nano /var/www/.ssh/config
и добавил в него
Host bitbucket.org
IdentityFile ~/.ssh/id_rsa
Сгенерированый ключ нужно добавить в битбакет (Settings -> Deployment keys).
Если далее будет проблема с ключами, проверить права доступа к ним, если что выставить:
sudo chmod 0755 /var/www/.ssh
sudo chmod 0600 /var/www/.ssh/id_rsa
sudo chmod 0600 /var/www/.ssh/id_rsa.pub
sudo chmod 0644 /var/www/.ssh/known_hosts
Далее, клонируем репозиторий от имени пользователя www-data с ключем --mirror. Это в общем пустой репозиторий, без файлов. В гайде описывалось почему именно --mirror.
cd /var/www/git
sudo -u www-data git clone --mirror git@bitbucket.org:username/example.git
Копируем непосредственно файлы репозитория
cd example.git
sudo -u www-data GIT_WORK_TREE=/var/www/example.com/public_html git checkout -f master
Обратите внимание на путь GIT_WORK_TREE. Суть в том, что у нас файлы сайта будут храниться отдельно от папки .git.
В примере выше я копирую ветку master.
Далее, создаем скрипт деплоя
cd /var/www/example.com/public_html
sudo -u www-data mkdir deploy
cd deploy
sudo -u www-data touch deploy.log
sudo -u www-data nano deploy.php
Рекомендуется обозвать папку и сам файл deploy.php каким-нибудь неадекватным набором символов вроде deploy-fseuo77w7rwegfe.php.
Собственно сам скрипт deploy.php, рассчитанный на 2 ветки, master (можно переименовать её в dev например и production.
<?php
// Examine the Bitbucket payload that’s being sent to deployment script
file_put_contents('deploy.log', serialize($_POST['payload']) . "\n", FILE_APPEND);
$repo_dir = '/var/git-repos/example.git';
$master_dir = '/var/www/dev.example.com/public_html';
$production_dir = '/var/www/example.com/public_html';
// Full path to git binary is required if git is not in your PHP user's path. Otherwise just use 'git'.
$git_bin_path = 'git';
$update = false;
// Parse data from Bitbucket hook payload
$payload = json_decode($_POST['payload']);
if (empty($payload->commits)){
// When merging and pushing to bitbucket, the commits array will be empty.
// In this case there is no way to know what branch was pushed to, so we will do an update.
$update = true;
} else {
foreach ($payload->commits as $commit) {
$branch = $commit->branch;
if ($branch === 'master' || isset($commit->branches) && in_array('master', $commit->branches)) {
$update = true;
break;
}
elseif ($branch === 'production' || isset($commit->branches) && in_array('production', $commit->branches)) {
$update = true;
break;
}
}
}
if ($update) {
// Do a git checkout to the web root
exec('cd ' . $repo_dir . ' && ' . $git_bin_path . ' fetch');
exec('cd ' . $repo_dir . ' && GIT_WORK_TREE=' . $master_dir . ' ' . $git_bin_path . ' checkout -f master');
exec('cd ' . $repo_dir . ' && GIT_WORK_TREE=' . $production_dir . ' ' . $git_bin_path . ' checkout -f production');
// Log the deployment
$commit_hash = shell_exec('cd ' . $repo_dir . ' && ' . $git_bin_path . ' rev-parse --short HEAD');
file_put_contents('deploy.log', date('m/d/Y h:i:s a') . " Deployed branch: " . $branch . " Commit: " . $commit_hash . "\n", FILE_APPEND);
}
?>
Остается только добавить post hook с ссылкой на deploy.php (Settings -> Hooks).
Получается что каждый раз когда мы пушим изменения в репозиторий (не важно в какую ветку) обновляются файлы на сервере в обоих ветках.