Node.jsとGithub Webhookを使用してリモートプロジェクトの同期を保つ方法

前書き

複数の開発者がいるプロジェクトで作業する場合、ある人がリポジトリにプッシュし、別の人が古いバージョンのコードに変更を加え始めるとイライラすることがあります。 このような間違いは時間がかかるため、リポジトリを同期させるスクリプトを設定する価値があります。 本番環境でこのメソッドを適用して、修正プログラムやその他の変更をすばやくプッシュすることもできます。

この特定のタスクを完了する他のソリューションが存在しますが、独自のスクリプトを作成することは、将来のカスタマイズの余地を残す柔軟なオプションです。

GitHubを使用すると、リポジトリのwebhooksを構成できます。これは、イベントが発生したときにHTTP要求を送信するイベントです。 たとえば、誰かがプルリクエストを作成するか、新しいコードをプッシュすると、Webhookを使用して通知できます。

このガイドでは、自分または他の誰かがコードをGitHubにプッシュするたびに、GitHub Webhook通知をリッスンするNode.jsサーバーを開発します。 このスクリプトは、リモートサーバー上のリポジトリを最新バージョンのコードで自動的に更新するため、新しいコミットを取得するためにサーバーにログインする必要がなくなります。

前提条件

このチュートリアルを完了するには、次のものが必要です。

  • sudo権限を持つ非rootユーザーとファイアウォールを含む、the Ubuntu 16.04 initial server setup guideに従うことによってセットアップされた1つのUbuntu16.04サーバー。

  • ローカルマシンにインストールされたGit。 チュートリアルContributing to Open Source: Getting Started with Gitに従って、コンピューターにGitをインストールしてセットアップできます。

  • Node.js and npm installed on the remote server using the official PPA, as explained explained in How To Install Node.js on Ubuntu 16.04. distro-stableバージョンをインストールするだけで、追加の構成なしで推奨バージョンが提供されます。

  • プロジェクトコードを含むGithubのリポジトリ。 プロジェクトを考えていない場合は、チュートリアルの残りの部分で使用するfork this exampleを自由に使用してください。

[[step-1 -—- setting-up-a-webhook]] ==ステップ1—Webhookの設定

まず、リポジトリのwebhookを構成します。 この手順が重要なのは、それがないと、Githubは、事態が発生したときにどのイベントを送信するのか、またはどこに送信するのかを知らないからです。 最初にwebhookを作成し、次にリクエストに応答するサーバーを作成します。

GitHubアカウントにサインインし、監視するリポジトリに移動します。 リポジトリのページの上部メニューバーにあるSettingsタブをクリックしてから、左側のナビゲーションメニューにあるWebhooksをクリックします。 右隅にあるAdd Webhookをクリックし、プロンプトが表示されたらアカウントのパスワードを入力します。 次のようなページが表示されます。

Webhooks Page

  • Payload URLフィールドにhttp://your_server_ip:8080と入力します。 これは、まもなく作成するNode.jsサーバーのアドレスとポートです。

  • Content typeapplication/jsonに変更します。 作成するスクリプトはJSONデータを想定しているため、他のデータタイプを理解することはできません。

  • Secretには、このWebhookのシークレットパスワードを入力します。 Node.jsサーバーでこのシークレットを使用してリクエストを検証し、リクエストがGitHubからのものであることを確認します。

  • Which events would you like to trigger this webhookには、just the push eventを選択します。 コードが更新され、サーバーと同期する必要があるため、プッシュイベントのみが必要です。

  • Activeチェックボックスを選択します。

  • フィールドを確認し、Add webhookをクリックして作成します。

pingは最初は失敗しますが、Webhookが設定されたことを確認してください。 次に、サーバーにクローンを作成します。

[[step-2 -—-リポジトリをサーバーに複製する]] ==ステップ2—リポジトリをサーバーに複製する

このスクリプトはリポジトリを更新できますが、リポジトリを最初に設定することはできませんので、ここでそれを行います。 サーバーにログインします。

ssh sammy@your_server_ip

ホームディレクトリにいることを確認してください。 次に、Gitを使用してリポジトリを複製します。 必ずsammyをGitHubユーザー名に、hello_hapiをGithubプロジェクトの名前に置き換えてください。

cd
git clone https://github.com/sammy/hello_hapi.git

これにより、プロジェクトを含む新しいディレクトリが作成されます。 次の手順でこのディレクトリを使用します。

プロジェクトのクローンを作成したら、webhookスクリプトを作成できます。

[[step-3 -—- creating-the-webhook-script]] ==ステップ3—Webhookスクリプトの作成

GitHubからのWebhookリクエストをリッスンするサーバーを作成しましょう。 ポート8080でWebサーバーを起動するNode.jsスクリプトを記述します。 サーバーはwebhookからのリクエストをリッスンし、指定したシークレットを確認し、GitHubから最新バージョンのコードを取得します。

ホームディレクトリに移動します。

cd ~

NodeWebhooksという名前のWebhookスクリプト用の新しいディレクトリを作成します。

mkdir ~/NodeWebhooks

次に、新しいディレクトリに移動します。

cd ~/NodeWebhooks

NodeWebhooksディレクトリ内にwebhook.jsという名前の新しいファイルを作成します。

nano webhook.js

次の2行をスクリプトに追加します。

webhook.js

var secret = "your_secret_here";
var repo = "/home/sammy/hello_hapi";

最初の行は、ステップ1で作成したシークレットを保持する変数を定義し、GitHubからのリクエストであることを確認します。 2行目は、ローカルディスクで更新するリポジトリへのフルパスを保持する変数を定義します。 これは、ステップ2でチェックアウトしたリポジトリを指している必要があります。

次に、httpおよびcryptoライブラリをスクリプトにインポートするこれらの行を追加します。 これらを使用してWebサーバーを作成し、秘密をハッシュして、GitHubから受け取ったものと比較できるようにします。

webhook.js

let http = require('http');
let crypto = require('crypto');

次に、child_processライブラリを含めて、スクリプトからシェルコマンドを実行できるようにします。

webhook.js

const exec = require('child_process').exec;

次に、このコードを追加して、GitHub webhookリクエストを処理し、それが本物のリクエストである場合に新しいバージョンのコードを取得する新しいWebサーバーを定義します。

webhook.js

http.createServer(function (req, res) {
    req.on('data', function(chunk) {
        let sig = "sha1=" + crypto.createHmac('sha1', secret).update(chunk.toString()).digest('hex');

        if (req.headers['x-hub-signature'] == sig) {
            exec('cd ' + repo + ' && git pull');
        }
    });

    res.end();
}).listen(8080);

http.createServer()関数は、Githubからの着信要求をリッスンするポート8080でWebサーバーを起動します。 セキュリティのために、リクエストに含まれるシークレットがステップ1でwebhookを作成するときに指定したものと一致することを検証します。 シークレットはx-hub-signatureヘッダーでSHA1ハッシュ文字列として渡されるため、シークレットをハッシュして、GitHubから送信されたものと比較します。

リクエストが本物の場合、シェルコマンドを実行して、git pullを使用してローカルリポジトリを更新します。

完成したスクリプトは次のようになります。

webhook.js

const secret = "your_secret_here";
const repo = "~/your_repo_path_here/";

const http = require('http');
const crypto = require('crypto');
const exec = require('child_process').exec;

http.createServer(function (req, res) {
    req.on('data', function(chunk) {
        let sig = "sha1=" + crypto.createHmac('sha1', secret).update(chunk.toString()).digest('hex');

        if (req.headers['x-hub-signature'] == sig) {
            exec('cd ' + repo + ' && git pull');
        }
    });

    res.end();
}).listen(8080);

サーバーの初期設定ガイドに従った場合は、ポート8080でトラフィックを許可することにより、このWebサーバーが外部Webと通信できるようにする必要があります。

sudo ufw allow 8080/tcp

スクリプトが準備できたので、スクリプトが適切に機能することを確認しましょう。

ステップ4-Webhookのテスト

nodeを使用してWebhookをテストし、コマンドラインで実行できます。 スクリプトを開始し、ターミナルでプロセスを開いたままにします。

cd ~/NodeWebhooks
nodejs webhook.js

Github.comでプロジェクトのページに戻ります。 リポジトリのページの上部メニューバーにあるSettingsタブをクリックしてから、左側のナビゲーションメニューにあるWebhooksをクリックします。 手順1で設定したWebhookの横にあるEditをクリックします。 次の画像に示すように、Recent Deliveriesセクションが表示されるまで下にスクロールします。

Edit Webhook

右端の3つのドットを押して、Redeliverボタンを表示します。 ノードサーバーが実行されている状態で、Redeliverをクリックしてリクエストを再送信します。 リクエストを送信することを確認すると、成功したレスポンスが表示されます。 これは、pingを再配信した後の200 OK応答コードで示されます。

これで、スクリプトをバックグラウンドで実行し、ブート時に開始することを確認できます。 CTRL+Cを使用すると、ノードのWebhookサーバーが停止します。

[[step-5 -—- installing-the-webhook-as-a-systemd-service]] ==ステップ5—WebhookをSystemdサービスとしてインストールする

systemdは、Ubuntuがサービスを制御するために使用するタスクマネージャーです。 ブート時にwebhookスクリプトを開始し、systemdコマンドを使用して他のサービスと同様に管理できるサービスを設定します。

新しいサービスファイルを作成することから始めます。

sudo nano /etc/systemd/system/webhook.service

systemdにスクリプトの実行方法を指示する次の構成をサービスファイルに追加します。 これはSystemdにノードスクリプトの場所を伝え、サービスを説明します。

必ずsammyをユーザー名に置き換えてください。

/etc/systemd/system/webhook.service

[Unit]
Description=Github webhook
After=network.target

[Service]
Environment=NODE_PORT=8080
Type=simple
User=sammy
ExecStart=/usr/bin/nodejs /home/sammy/NodeWebhooks/webhook.js
Restart=on-failure

[Install]
WantedBy=multi-user.target

システムの起動時に開始されるように、新しいサービスを有効にします。

sudo systemctl enable webhook.service

次に、サービスを開始します。

sudo systemctl start webhook

サービスが開始されていることを確認します。

sudo systemctl status webhook

サービスがアクティブであることを示す次の出力が表示されます。

Output● webhook.service - Github webhook
   Loaded: loaded (/etc/systemd/system/webhook.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2018-08-17 19:28:41 UTC; 6s ago
 Main PID: 9912 (nodejs)
    Tasks: 6
   Memory: 7.6M
      CPU: 95ms
   CGroup: /system.slice/webhook.service
           └─9912 /usr/bin/nodejs /home/sammy/NodeWebhooks/webhook.js

これで、新しいコミットをリポジトリにプッシュし、サーバー上の変更を確認できます。

デスクトップマシンから、リポジトリのクローンを作成します。

git clone https://github.com/sammy/hello_hapi.git

リポジトリ内のファイルの1つを変更します。 次に、ファイルをコミットし、コードをGitHubにプッシュします。

git add index.js
git commit -m "Update index file"
git push origin master

webhookが起動し、変更がサーバーに表示されます。

結論

新しいコミットをリモートリポジトリに自動的にデプロイするNode.jsスクリプトをセットアップしました。 このプロセスを使用して、監視する追加のリポジトリを設定できます。 リポジトリーをプッシュするときにWebサイトまたはアプリケーションを実動にデプロイするように構成することもできます。