среда, 3 июня 2015 г.

Развертывание приложения по хуку из github

Схема работы следующая. В github на репозиторий ставится хука - post на мой URL. Главное чтобы данные приходили не в json. На веб сервере запрос разбирается и добавляется задача в gearman какой скрипт выполнить и с какими параметрами (название ветки). В crone крутится скрипт который случает очередь gearman'а и при получении задания - выполняет его. Почему нужен gearman? Развертывание может происходить достаточно долго и если его выполнять сразу при получении запроса от github времени может не хватить. В схеме с gearman развертывание может длиться сколь угодно по времени и нет конкуренции, так как все запросы обрабатываются последовательно.
Скрипт для получения хук index.php
<?php

const QUEUE_NAME = 'deploy';

$map = [
    '/hook/super-secret' => [
        'name' => 'Project',
        'deploy' => '/opt/deploy/init.sh',
        'remove' => '/opt/deploy/remove.sh'
    ],
];

function log_message($message)
{
    $message = '[' . date('c') . '] ' . $message . PHP_EOL;
    file_put_contents(__DIR__ . '/../logs/deploy.log', $message, FILE_APPEND);
}

$uri = $_SERVER['REQUEST_URI'];

if (isset($map[$uri]) && isset($_POST['payload'])) {
    $data = json_decode($_POST['payload'], true);
    $branch = isset($data['ref']) ? $data['ref'] : null;
    $deleted = isset($data['deleted']) ? $data['deleted'] : false;

    if ($deleted) {
        log_message(sprintf('[%s] Delete branch %s', $map[$uri]['name'], $branch));
    } else {
        log_message(sprintf('[%s] Deploy branch %s', $map[$uri]['name'], $branch));
    }

    if (preg_match('/^refs\/heads\/(.+)$/', $branch, $match) && $match[1]) {
        $data = [
            'branch' => $match[1],
            'script' => $deleted ? $map[$uri]['remove'] : $map[$uri]['deploy'],
        ];
        $client = new GearmanClient();
        $client->addServer();
        $client->doHighBackground(QUEUE_NAME, json_encode($data));
    }
}
Скрипт для обработки заданий gearman deploy.php
<?php
/*
 *  flock -n /var/lock/php_deploy.lock -c "php /opt/deploy/deploy.php"
 */
const QUEUE_NAME = 'deploy';

function log_message($message)
{
    $message = '[' . date('c') . '] ' . $message . PHP_EOL;
    file_put_contents(__DIR__ . '/logs/deploy.log', $message, FILE_APPEND);
}

$worker = new GearmanWorker();
$worker->addServer();
$worker->setTimeout(60000);
$worker->addFunction(
    QUEUE_NAME,
    function ($job) {
        $data = @json_decode($job->workload(), true);
        if (isset($data['script'], $data['branch'])) {
            log_message($data['script'] . ' ' . $data['branch']);
            system($data['script'] . ' ' . $data['branch']);
        }

        return 0;
    }
);

do {
    $worker->work();
} while ($worker->returnCode() == GEARMAN_SUCCESS);
deploy.php добавляем в cron вот так flock -n /var/lock/php_deploy.lock -c "php /opt/deploy/deploy.php" чтобы не запускалось больше одной копии.

1 комментарий:

  1. Еще можно выполнить fastcgi_finish_request() чтобы сразу закрыть соединение и после запустить сценарий деплоя.

    ignore_user_abort(true);
    set_time_limit(0);
    fastcgi_finish_request();
    // Deployment...

    ОтветитьУдалить