Skip to content

CHICPRO

  • Life Log
  • Cycling Log
  • Photo Log
    • Portrait
    • Landscape
    • Flower
    • Etc
  • Coding Log
  • Information

[PHP] github webhook을 이용한 소스코드 서버 자동배포

2018-05-16 by 편리
  • 우분투 16.04, git 2.7.4 환경을 기준으로 테스트 했습니다.

서비스에 사용되는 서버가 소수 일 때는 각 서버에 접속해서 git pull 명령어를 실행하면 됐지만 점점 더 많아지니까 소스 배포도 일이 되기에 github 를 이용해 소스코드를 서버에 자동 배포할 수 있는 환경을 구축하기로 했다. github 에서 제공하는 webhook 을 이용하면 문제를 해결할 수 있다. 환경 구축에 참고한 내용은 https://github.com/mboynes/github-deploy 이다.

#0. 준비

보통 웹서버는 www-data 등의 사용자로 실행이 된다. 웹루트의 소유자가 www-data 등의 사용자가 아니라면 퍼미션이 없기 때문에 바로 git pull 의 명령어를 실행시킬 수 없다. 이를 위해 www-data 사용자가 sudo 명령어를 사용할 수 있도록 수정했다. 배포 명령을 실행하는 deploy.php 파일은 웹루트에 위치한다.

# visudo

위 명령어를 실행한 후 아래 라인을 추가한다. www-data 사용자에게 비밀번호 입력없이 git 명령어를 사용할 수 있도록 한다.

www-data   ALL=(ALL) NOPASSWD: /usr/bin/git

다음으로 각 서버에서 git 사용을 위한 기본적인 설정을 한다.

$ git config --global user.email "example@example.com"
$ git config --global user.name "Example"
$ git config --global push.default simple

위에서 이메일과 이름은 변경한다. deploy 로그를 기록하기 위해 deploy.php 파일의 상위 디렉토리에 logs 디렉토리를 생성하고 www-data 사용자가 파일을 쓸 수 있도록 퍼미션을 수정한다. 보통은 chmod 707 logs 와 같이  실행한다. logs 디렉토리 안에 deploy.log 파일을 생성해 둔다. 소유권은 www-data 에게 준다.

#1. git pull 명령어를 실행할 php 추가

<?php
# Array of the authorized IP addresses who can POST here. You can override this in your config if you so choose.
$authorized_ips = array(
    '207.97.227.253',
    '50.57.128.197',
    '108.171.174.178',
    '50.57.231.61',
    '54.235.183.49',
    '54.235.183.23',
    '54.235.118.251',
    '54.235.120.57',
    '54.235.120.61',
    '54.235.120.62'
);

# Put your deploy config file in the same dir as this file
if ( file_exists( dirname( __FILE__ ) . '/deploy-config.php' ) )
    include_once( 'deploy-config.php' );

# A regex matching the ref of the "push". `git pull` will only run if this matches. Default is the master branch.
if ( !defined( 'REF_REGEX' ) )
    define( 'REF_REGEX', '#^refs/heads/master$#' );

if ( !defined( 'LOG_WRITE' ) )
    define( 'LOG_WRITE', true );

# Log location; make sure it exists
if ( !defined( 'LOG' ) )
    define( 'LOG', '../logs/deploy.log' );

# Where is your repo directory? This script will chdir to it. If %s is present, it gets replaced with the repository name
if ( !defined( 'REPO_DIR' ) )
    define( 'REPO_DIR', dirname( __FILE__ ) . "/" );

# Where is your git binary, and what command would you like to run?
if ( !defined( 'GIT_COMMAND' ) )
    define( 'GIT_COMMAND', 'git pull' );

# Do we want to do IP verification?
if ( !defined( 'VERIFY_IP' ) )
    define( 'VERIFY_IP', true );

# If defined, $_POST gets logged
# define( 'DUMP_POSTDATA', true );

# In your webhook URL to github, you can append ?auth={{ this field }} as a very simple gut-check authentication.
# define( 'AUTH_KEY', 'whatever-you-want' );


if ( is_writable( LOG ) && $handle = fopen( LOG, 'a' ) ) {
    # Sweet taste of victory
    if(LOG_WRITE)
        fwrite( $handle, date( 'Y-m-d H:i:s' ) . "\n==============================\n" );
    else
        @fclose( $handle );
} else {
    @fclose( $handle );
    header( $_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500 );
    die( 'Please complete installation' );
}

# Do some authentication
if ( defined( 'AUTH_KEY' ) && ( !isset( $_GET['auth'] ) || AUTH_KEY != $_GET['auth'] ) ) {
    $error = "Auth key doesn't match";
} elseif ( !isset( $_POST['payload'] ) ) {
    $error = '$_POST["payload"] is not set';
} elseif ( VERIFY_IP && !in_array( $_SERVER['REMOTE_ADDR'], $authorized_ips ) ) {
    $error = "{$_SERVER['REMOTE_ADDR']} is not authorized. Authorized IPs are: " . implode( ', ', $authorized_ips );
} else {
    $error = false;
}
if ( false !== $error ) {
    if(LOG_WRITE)
        fwrite( $handle, "*** ALERT ***\nFailed attempt to access deployment script!\n\nMESSAGE: $error\n\n" . print_r( $_SERVER, 1 ) . print_r( $_REQUEST, 1 ) . "\n\n\n" );
    @fclose( $handle );
    header( $_SERVER['SERVER_PROTOCOL'] . ' 401 Unauthorized', true, 401 );
    die( "You don't have permission to access this page." );
}

# We're authorized, let's do this!
if(LOG_WRITE) {
    $content = '';
    if ( defined( 'DUMP_POSTDATA' ) )
        $content .= print_r( $_POST, 1 ) . "\n\n";

    if ( false === fwrite( $handle, $content ) ) {
        echo "Couldn't write to log!\n";
    }
}

$payload = json_decode( $_POST['payload'] );
if ( preg_match( REF_REGEX, $payload->ref ) ) {
    # If we have a commit to master, we can pull on it
    # If commit message has [deployment], pull
    $cnt = count($payload->commits);
    $is_deploy = false;

    for($i=0; $i<$cnt; $i++) {        
        if(preg_match('#\[deployment\]#i', $payload->commits[$i]->message)) {
            $is_deploy = true;
            break;
        }
    }

    if($is_deploy === true) {
        $userInfo = posix_getpwuid(fileowner(__FILE__));
        $owner = $userInfo['name'];
        $output = array( 'bash> ' . sprintf(GIT_COMMAND, $owner) );
        chdir( sprintf( REPO_DIR, $payload->repository->name ) );        
        exec( sprintf(GIT_COMMAND, $owner) . ' 2>&1', $output );

        if(LOG_WRITE)
            fwrite( $handle, "`$payload->ref` matches, executing:\n" . sprintf(GIT_COMMAND, $owner) . "\n" . implode( "\n", $output ) . "\n" );
    }
} else {
    if(LOG_WRITE)
        fwrite( $handle, "`$payload->ref` doesn't match the ref criteria\n" );
}

if(LOG_WRITE)
    fwrite( $handle, date( 'Y-m-d H:i:s' ) . " Over and out!\n\n\n" );
@fclose( $handle );

?>

deploy.php 의 코드는 위와 같다. 그리고 아래는 deploy-config.php 의 코드이다. 마킹된 95 ~ 105라인의 코드에서 커밋 메세지 중 [deployment] 라는 문구가 포함되어 있으면 git pull 명령을 실행하도록 한다.

<?php
# A regex matching the ref of the "push". <code>git pull</code> will only run if this matches. Default is the master branch.
define( 'REF_REGEX', '#^refs/heads/master$#' );

# Write Log File
define('LOG_WRITE', false);

# Log location; make sure it exists
define( 'LOG', '../logs/deploy.log' );

# Where is your repo directory? This script will chdir to it. If %s is present, it gets replaced with the repository name
define( 'REPO_DIR', dirname( __FILE__ ) . "/" );

# If set to true, $_POST gets logged
define( 'DUMP_POSTDATA', false );

# In your webhook URL to github, you can append ?auth={{ this field }} as a very simple gut-check authentication
define( 'AUTH_KEY', 'some_auth_key' );

# Where is your git binary, and what command would you like to run?
define( 'GIT_COMMAND', 'sudo -u %s /usr/bin/git pull' );

# Do we want to do IP verification?
define( 'VERIFY_IP', false );

#2. webhook 등록

github.com 에서 repo 설정에 webhook 을 등록한다. webhook 등록 때 url은 아래와 같은 형식으로 등록해야 한다.

http://example.com/deploy.php?auth=some_auth_key

 

위의 과정을 거친 후 commit 메세지에 [deployment] 문구가 들어가면 webhook 이 실행되어 git pull 명령을 실행하게 된다. 제대로 실행되지 않는다면 log 파일의 내용을 확인해서 조치한다.

Post navigation

Previous Post:

[Ubuntu] Monit 설치 및 설정

Next Post:

[PHP] xml 포맷의 데이터를 배열(Array)로 변환

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Recent Posts

  • SK 세븐모바일 유심 셀프교체
  • php 배열 연산에서 + 와 array_merge 의 차이
  • pcntl_fork 를 이용한 다중 프로세스 실행
  • 아이폰 단축어를 이용하여 주중 공휴일엔 알람 울리지 않게 하기
  • 구글 캘린더 전체일정 재동기화
  • OpenLiteSpeed 웹서버에 HTTP 인증 적용
  • OpenLiteSpeed 웹어드민 도메인 연결
  • WireGuard를 이용한 VPN 환경 구축
  • Ubuntu 22.04 서버에 OpenLiteSpeed 웹서버 세팅
  • 맥 vim 세팅

Recent Comments

  • 편리 on 업무관리용 그누보드 게시판 스킨
  • 임종섭 on 업무관리용 그누보드 게시판 스킨
  • 캐논 5D 펌웨어 | Dslr 펌웨어 업그레이드 방법 82 개의 베스트 답변 on 캐논 EOS 30D 펌웨어 Ver 1.0.6 , EOS 5D 펌웨어 Ver 1.1.1
  • Top 5 캐논 5D 펌웨어 Top 89 Best Answers on 캐논 EOS 30D 펌웨어 Ver 1.0.6 , EOS 5D 펌웨어 Ver 1.1.1
  • 편리 on 워드프레스 애니메이션 gif 파일을 mp4로 변환하여 출력하기
  • 임팀장 on 워드프레스 애니메이션 gif 파일을 mp4로 변환하여 출력하기
  • 편리 on Notepad++ NppFTP 플러그인 수동 설치
  • paul-j on Notepad++ NppFTP 플러그인 수동 설치
  • YS on Windows 10 iCloud 사진 저장 폴더 변경
  • 편리 on Docker를 이용한 Centos7 + httpd + php 5.4 개발환경 구축

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org
© 2025 CHICPRO | Built using WordPress and SuperbThemes