Bitbucket や GitHub で自動デプロイするためのサンプル PHP スクリプトを拾って改造してみた

GitHub and SourceTree

GitHub や Bitbucket などの Git ホスティングサービスの Hook や Webhook サービスを使って、Git に Push した時、自動的にサーバー側で最新版の master ブランチを pull するための PHP を拾ってきて、ちょっと改造しました。

大きいプロジェクトであれば、きちんと Travis CI などのサービスを使いましょう。

ちなみに、Bitbucket では、5ユーザーまでの小規模プロジェクトであれば、無料で非公開 Git を作ることができるので、オススメです。

宣伝:ウェブサイトを作るCMS は concrete5 で決まり!(世界では、アメリカ陸軍、スイス政府、ミニクーパー等、日本でも、市町村サイト、国公立大学で使われています。)

 

豆知識: Hook とは

Git には、標準で hook と呼ばれる機能があります。アクションの各段階において、シェルスクリプトを実行できるものです。

UNIX系の OS を使っていて黒い画面に詳しい人は、詳しくは、

を見ると、いろいろできます。

 

豆知識: Webhook とは

Git の hook は Mac を含めた UNIX 系のPCで黒い画面を使いこなしている人しか恩恵はありませんが、Web関係者で Git を使って楽をしたい人にオススメなのが、GitHub が提供している Webhooks や、 Bitbucket の Hooks 機能です。

GitHub や Bitbucket でこの機能を使いこなすと、例えば、 Git で master ブランチ Push された時、自動的に本番サーバーに Git の情報を Fetch/Pull して FTP でアップする必要がなくなります。

ちょっと高度に活用すると、 develop という名前のブランチを作って、それは開発環境サーバーに自動的に Fetch/Pull するように設計できます。

さらに高度に活用すると、 Push された時に本番や環境サーバーでも Grunt とか Gulp とかの自動タスクをを本番サーバーでも走らせたりなどを行うことが出来ます。

 

Webhook を PHP スクリプトを使って、自動的に git pull されるスクリプトを見つけて、ちょっと改造してみた。

これらの Webhook は、GitHub や Bitbucket 側で指定したサーバーなどに信号を送るだけです。なので、信号を受け取ってから、サーバー側でアクションを起こす仕組みを作る必要があります。

今回は、GitHub や Bitbucket から、「 git fetch 」する PHP スクリプトを叩く仕組みを探して改造してみました。

いろいろ探してたんですが「Using Bitbucket for Automated Deployments」や「Automated git deployments from Bitbucket」というブログ記事をのサンプルを見つけさせていただきました。

 

サーバー準備

まず、サーバーの準備が必要です。

サーバーは

  • トップディレクトリに WordPress や concrete5 がインストールされている想定
  • PHP が exec() 使える
  • Git がインストール
  • 非公開 Git レポジトリを使う場合は、 SSH 公開鍵のサポート
  • 「https 」がサポートするとちょっとだけセキュア
  • Basic 認証

をサポートしているといいかもです。

今日準備する Deploy コードは PHP を利用しているので、PHP が動くサーバーである必要があります。

 

Git レポジトリを作成 & .gitignore ファイルを作成

Git レポジトリを作成し、concrete5 や WordPress などの CMS サイトを使っている人は .gitignore ファイルを作りましょう。

参考までに、WordPress や concrete5 に最適化された gitignore ファイルのサンプルを置いておきます。

ここでは、

  • concrete5 の files 以下のファイル郡を無視
  • WordPress の wp-contens/uploads/ フォルダ以下のファイル郡を無視
  • 後述の deployment.phpdeployment.log を無視
  • 追加アドオンやプラグインはあえて保存
  • WordPress や concrete5 の本体もあえて管理

しています。適宜編集してやってください。

#
# .gitignore sample for concrete5 & WordPress projects
# concrete5 & WordPress 用 .gitignore サンプルファイル
#
# rename this file to '.gitignore' when you actually use it
# 使用の際はファイル名を[.gitignore]に変更してください
#
 
# ignore system files
.DS_Store
thumbs.db
__MACOSX
._*

# ignore the error log and .htaccess and others
error_log
.htaccess
.ftpquota

# ignore everything but the index.html
/application/config/database.php
/application/config/doctrine/*
/application/config/generated_overrides/*
/application/files/*
!/application/files/.gitkeep
!/application/files/.gitignore
# /concrete/*
/updates/*
/config/site.php
/files/*
!/files/index.html
!/files/.gitkeep
!/files/.gitignore
/wp-content/blogs.dir/*
/wp-content/cache/*
/wp-content/uploads/*

# ignore sitemap.xml
sitemap.xml

# Ignore hook log
deployments.php
deployments.log

 

Deploy 用にサーバーの公開鍵を GitHub や Bitbucket に登録

各サーバーで、Deploy 用公開鍵を生成します。

下記ステップの詳しくは「4.3 Git サーバー – SSH 公開鍵の作成」を参照。

  1. cd ~/
  2. mkdir .ssh (.ssh ディレクトリが作成されていない場合、作成済みの場合は cd ~/.ssh/)
  3. ssh-keygen
  4. 鍵の保存先はデフォルトのまま「.ssh/id_rsa」を指定
  5. パスフレーズは空のまま。そのまま「改行 (Return)」キーを2回押す
  6. cat ~/.ssh/id_rsa.pub で表示されるキーをコピーして、GitHub や BitBucket にコピー
  7. GitHub や Bitbucket の該当ページに公開鍵を登録
    1. GitHub の登録場所 https://github.com/[自分のID]/[GitHubレポジトリ]/settings/keys
    2. Bitbucket の登録場所 https://bitbucket.org/[自分のID]/[BitBucketレポジトリ]/admin/deploy-keys

 

サーバー上に git clone + git checkout

次に、Git Clone を行います。BASIC 認証や IP 制限などで非公開の領域であれば、普通に git pull しても良いかもしれません。

しかし、公開する Web サーバー領域に git pull すると、 /.git/ ディレクトリの中も公開サーバーにいれられてしまうので、あまり良い方法ではありません。そのために、git clone --mirror をして、Web 公開領域に git checkout するのが良いでしょう。

自動デプロイは git fetch して git checkout するという方法をとります。

公開するブランチを master とします。ただ、本来ならは、公開用は production などのネーミングで行ったほうが良いでしょう。

cd [Git レポジトリを格納する場所]
git clone --mirror [GitHub か Bitbucket 用 URL] [オプション: 作成する Git レポジトリ名]
cd [Git レポジトリ名 もしくは作成したGitレポジトリフォルダ名]
GIT_WORK_TREE=[Web公開領域のパス] git checkout -f master

2015/1 時点、日本のレンタルホスティングでは、SSH 公開・非公開鍵を使って、非公開 Git レポジトリからもやりとりできるのは VPS などのサービスしか対応していないと思います。他にできた人、ご指摘お願いします。

 

Deploy 実行用コードの設置

下記のコードを、deployement.php として保存して、自分のサーバーに合わせて変数を変えてください。

ダウンロードしやすいよう、 GitHub にもUPしました。

<?php
/*
 *	GitHub & Bitbucket Deployment Sample Script
 *	Originally found at
 *	http://brandonsummers.name/blog/2012/02/10/using-bitbucket-for-automated-deployments/
 *	http://jonathannicol.com/blog/2013/11/19/automated-git-deployments-from-bitbucket/
 *  
 *	We assume you did a 'git clone -mirror' to your local repo directory,
 *	And then, 'GIT_WORK_TREE=[www directory] git checkout -f [your desired branch]'  
 *  
  */

/**
* The Full Server Path to git repository and web location.
* Can be either relative or absolute path
* 
* @var string
*/
$git_serverpath = '/path/to/git/repo';
$www_serverpath = '/path/to/www';

/**
* The Secret Key so that it's a bit more secure to run this script
* 
* @var string
*/
$secret_key = 'EnterYourSecretKeyHere';

/*
 *    Webhook Sample
 *        For BitBucket, use 'POST HOOK'
 *        For GitHub, use 'Webhooks'
 *    URL Format
 *        https://[Basic Auth ID]:[Basic Auth Pass]@example.com/deployments.php?key=EnterYourSecretKeyHere
 *        (https access & Basic Auth makes it a bit more secure if your server supports it)
 */


/**
* The TimeZone format used for logging.
* @link    http://php.net/manual/en/timezones.php
*/
date_default_timezone_set('Asia/Tokyo');


class Deploy {

  /**
  * A callback function to call after the deploy has finished.
  * 
  * @var callback
  */
  public $post_deploy;
  
  /**
  * The name of the file that will be used for logging deployments. Set to 
  * FALSE to disable logging.
  * 
  * @var string
  */
  private $_log = 'deployments.log';

  /**
  * The timestamp format used for logging.
  * 
  * @link    http://www.php.net/manual/en/function.date.php
  * @var     string
  */
  private $_date_format = 'Y-m-d H:i:sP';

  /**
  * The path to git
  * 
  * @var string
  */
  private $_git_bin_path = 'git';

  /**
  * The directory where your website and git repository are located,
  * can be relative or absolute path
  * 
  * @var string
  */
  private $_git_dir;
  private $_www_dir;

  /**
  * Sets up defaults.
  * 
  * @param  string  $directory  Directory where your website is located
  * @param  array   $data       Information about the deployment
  */
  public function __construct($git_dir, $www_dir, $options = array())
  {
      // Determine the directory path
      $this->_git_dir = realpath($git_dir).DIRECTORY_SEPARATOR;
      $this->_www_dir = realpath($www_dir).DIRECTORY_SEPARATOR;

      $available_options = array('log', 'date_format', 'git_bin_path');

      foreach ($options as $option => $value)
      {
          if (in_array($option, $available_options))
          {
              $this->{'_'.$option} = $value;
          }
      }

      $this->log('Attempting deployment...');
  }

  /**
  * Writes a message to the log file.
  * 
  * @param  string  $message  The message to write
  * @param  string  $type     The type of log message (e.g. INFO, DEBUG, ERROR, etc.)
  */
  public function log($message, $type = 'INFO')
  {
      if ($this->_log)
      {
          // Set the name of the log file
          $filename = $this->_log;

          if ( ! file_exists($filename))
          {
              // Create the log file
              file_put_contents($filename, '');

              // Allow anyone to write to log files
              chmod($filename, 0666);
          }

          // Write the message into the log file
          // Format: time --- type: message
          file_put_contents($filename, date($this->_date_format).' --- '.$type.': '.$message.PHP_EOL, FILE_APPEND);
      }
  }

  /**
  * Executes the necessary commands to deploy the website.
  */
  public function execute()
  {
      try
      {
          // Update the local repository
          exec('cd ' . $this->_git_dir . ' && ' . $this->_git_bin_path . ' fetch', $output);
          $this->log('Fetching changes... '.implode(' ', $output));

          // Checking out to web directory
		  exec('cd ' . $this->_git_dir . ' && GIT_WORK_TREE=' . $this->_www_dir . ' ' . $this->_git_bin_path  . ' checkout -f', $output);
          $this->log('Checking out changes to www directory... '.implode(' ', $output));

          if (is_callable($this->post_deploy))
          {
              call_user_func($this->post_deploy, $this->_data);
          }

          $this->log('Deployment successful.');
      }
      catch (Exception $e)
      {
          $this->log($e, 'ERROR');
      }
  }

}

$deploy = new Deploy($git_serverpath, $www_serverpath);

/*
$deploy->post_deploy = function() use ($deploy) {
  // hit the wp-admin page to update any db changes
   exec('curl http://example.com/wp-admin/upgrade.php?step=upgrade_db');
   $deploy->log('Updating wordpress database... ');
};
*/

if ($_GET[key] === $secret_key)  {
	$deploy->execute();
}
?>

 

  • 必須: 19行目の「/path/to/git/repo」を自分のサーバーの git レポジトリのパスに置き換えてください。
  • 必須: 20行目の「/path/to/www」を自分のサーバーの Web 領域のパスに置き換えてください。
  • 必須: 27行目の「EnterYourSecretKeyHere」は、パスワードキーです。記号以外の半角の数字や英字の小文字大文字のコンビネーションでパスワードキーを設定してください。
  • オプション: 172〜178行目は、サーバー上で自動デプロイした後に特定のコマンドを実行するためのコマンドを入力し、ログに出力するものです。サンプルでは、WordPress の Upgrade PHPスクリプトを普通に叩くシェルを実行するサンプルになっています。上級者向けですが、いろいろ試したい人は、コメントを外して試してみてください。

PHP とシェルスクリプトが分かる人は、上記のスクリプトは、シェルを実行しているだけなので、適宜コマンドを変えて作ってやってもいいかもしれません。

 

Deploy 実行URLを作成

URL は

https://example.com/deployment.php?key=パスワードキー

と「 key= 」の後にパスワードになる半角英数字のパスワードキーを入れてください。例文は https 通信になっているので、ちょっとだけ、key がバレにくくなります。

また、BASIC 認証の設定もされている場合は、

https://[Basic認証ID]:[Basic認証パス]@example.com/deployment.php?key=パスワードキー

てな URL になります。

公開用のサーバーの場合でも、このデプロイ用 PHP ファイルは Basic 認証が設置してある https でアクセスする場所に設置するのをお勧めします。

https をサポートしていないサーバーの場合は、URLを「http」に戻すことを忘れずに。
 

GitHub や Bitbucket で URL の設定

GitHub や BitBucket で Webhook の設定を行います。

  • GitHub や Bitbucket の設定ページに行く
    • GitHub は https://github.com/[自分のID]/[GitHubレポジトリ]/settings/hooks
    • Btbucket は https://bitbucket.org/[自分のID]/[BitBucketレポジトリ]/admin/hooks
  • GitHub は Add Webhook、Bitbucket は POST を選択して Add をクリック
  • Deploy 実行URLを入力
    • GitHub では、「Payload URL」のところに。あとの項目は空白やデフォルトのママ
    • Bitbucket では、URLを項目に入れて保存

試しに Pull Request をしてサーバーに反映されているか、テストしてみましょう。

 

テストして deployment.log を確認

正常に終わっていたら、deployment.log というログファイルが生成されて、どのような変更があったかのログが見られるはずです。

※ 注意:ログファイルが肥大になるかもしれないので、定期的に削除してやってください。

 

簡単バージョン

なお、 GitHub にUPしてある「dev-server」ブランチには、直接 www 領域に git pull するサンプルスクリプトもあります。

なので、公開サーバー領域に直接 git clone して、あとは git pull するだけのもので、わざわざ、レポジトリと公開用をわけていないものになります。

BASIC認証などで非公開にしている開発用サーバー向けに使ってやってください。

 

ご意見募集

間違ってたり、ヤバかったりしたら、ご指摘や、 GitHub へのプルリクエストをお願いします。

 

それでは、自動 deploy ガンバってみてください。

宣伝:ウェブサイトを作るCMS は concrete5 で決まり!(世界では、アメリカ陸軍、スイス政府、ミニクーパー等、日本でも、市町村サイト、国公立大学で使われています。)

以上。

  • rinopo

    参考にさせていただきました。

    > 2015/1 時点、日本のレンタルホスティングでは、SSH 公開・非公開鍵を使って、非公開 Git レポジトリからもやりとりできるのは VPS などのサービスしか対応していないと思います。他にできた人、ご指摘お願いします。

    2016年1月時点ですが、「GitHubの非公開repo ↔️ さくらのレンタルサーバー」ではできました。
    (phpスクリプトの動作はまだ試していません。)

  • ありがとうございます!
    あれから1年。
    Backlog の Git と Bitbucket の Git ともさくらのスタンダートで試して成功しています。