ほげほげ見聞録

技術メモ、備忘録、使い方はそのうち覚える

BacklogのWebhook機能でデプロイ(解決編)

前回までのあらすじ

BacklogのWebhook機能でJenkinsのジョブを実行、Publish over FTPで特定ブランチのデプロイができるようになった。
詳しくは以前の記事で。
dwmemo.hatenablog.com
この方法だとブランチの判断ができないので、どのブランチでもデプロイしてしまう問題がある。
ついでに、プロジェクト内にあるすべてのGitリポジトリのpushでWebhookが実行される…。

そこで今回は前回の手段見直しと目的の動作を実現する手段を考える。

目的の動作

  • 特定Gitリポジトリの場合だけWebhook実行
  • 特定ブランチの場合だけデプロイ実行
  • 変更のないファイルはアップロードしない

BacklogのWebhook機能

実はこの機能、二か所に設定がある。
前回いじっていたのは、「プロジェクト設定」>「Webhook」の機能。
必要なのは、「プロジェクト設定」>「Git」のWebhook機能。
ヘルプにでもなんでも導線を引いておいてほしいんじゃがーじゃがー…。
Webhookで指定したURLでパラメータ payload が取得できない – Customer Feedback for Backlog

以降は、前者をWebhook機能、後者をGitのWebhook機能と呼ぶ。
二つの違いは以下。

Webhook機能

  • Gitのpush以外のイベントも設定可能
  • イベントにGitのpushを設定すると、プロジェクトのすべてのGitリポジトリで実行される
  • POSTの形式はキーのないJSON({"revisions":})

GitのWebhook機能

  • Gitのpush時のみ実行
  • Gitリポジトリ毎に設定可能
  • POSTの形式はキーがpayloadのJSON(payload:{"revisions":})

どちらの機能も、どのブランチがpushされたかの情報をJSONに含んでいる。
なので、このPOST値をJenkinsで読み取ればデプロイの判断が可能。

JenkinsでPOST値を読み取る

WebhookでリモートからURLを叩くには、「Remote Access API」のURLを設定。
Remote access API - Jenkins - Jenkins Wiki
ただし、グローバルセキュリティの設定でCSRFをチェックしている状態では、JenkinsにPOSTする場合にはcrumbを付与する必要あり。
色々調べてみたが、CSRFのチェックをオンにしている場合のPOST値受け取りはcrumbを取得するしかなさそうだ。

余談

前回CSRFチェックの対策で使用した「Build Authorization Token Root Plugin(Build Token Root Plugin)」は試していないので、もしかするとこのプラグインを使うとPOST値を渡せるかもしれない。
https://wiki.jenkins.io/display/JENKINS/Build+Token+Root+Pluginwiki.jenkins.io
※追記
【JenkinsURL】/buildByToken/buildWithParameters?job=【ジョブ名】&token=【トークン】&payload=test これが通るか試したところ、400エラー発生。
java.lang.Exception: Use /buildByToken/build for this job since it takes no parameters」

どうやらバグの模様? 良く分からん。
[JENKINS-27524] Unable to use BuildWithParameters - Jenkins JIRA

CSRFのチェックをオフにした状態でPOST値を取得

CSRFのチェックは外さない方針で進めるが、一応確認。
payloadの取得にはパラメータ付きビルドを使用。
Parameterized Build - Jenkins - Jenkins Wiki

GitのWebhookのURLに以下を設定して、パラメータ付ビルドが成功(201)。

【JenkinsのURL】/job/【ジョブ名】/buildWithParameters?token=【トークン】

payloadにJSONでデータが入っている(中身はpushによる)。

{"revisions":[{"timestamp":"","url":"","author":{""},"id":"","modified":[""],"message":""}],"repository":{"name":"","url":"","description":""},"after":"","ref":"refs/heads/develop","before":""}



Jenkins-Crumbを取得する

crumbを取得するには以下のURLにアクセス。

【JenkinsのURL】/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)

以下の形式でcrumbを取得できる。

Jenkins-Crumb:【crumb】

BacklogだとURLを設定するだけなので、取得したcrumbをPOSTに含められない。
WebhookとJenkinsの間に一つ処理を入れれば問題なさそう。

BacklogとJenkinsの間に挟むAPI

BacklogからのPOST値と、別途取得したcrumbを設定してJenkinsに投げるAPIを作る。
NodeJS使うと楽なのでおすすめ。
今回は詳細は省くが、要はcrumbをヘッダの「Jenkins-Crumb」に設定して以下のURLをPOSTすればいい。

【JenkinsのURL】/job/【ジョブ名】/buildWithParameters?payload=' + encodeURIComponent(post.payload)

※追記
dwmemo.hatenablog.com



余談:GitBucketからPOST値を取得

GitBucketを使用しているプロジェクトの場合、buildWithParametersでパラメータpayloadを指定するだけで問題ない。
詳細は調べていないが、ジョブにGitBucketの設定があるのでCSRFをチェックしないようになっているのかもしれない。

ジョブの設定

ブランチのチェック・デプロイの判断はパイプラインで行う。
ビルドのパラメータ化にチェック、payloadを設定。

スクリプトは以下のように、POSTされたpayloadをパースしてブランチをチェックするだけ。
同じジョブの中でデプロイしてもよさそうだが、手動でデプロイしたい場合もあるかもしれないので分割。

import groovy.json.JsonSlurperClassic

node {
    stage('deploy') {
        def payload = new JsonSlurperClassic().parseText(params.payload);

        if (payload.ref == 'refs/heads/develop') {
            build '【デプロイ用ジョブ名】'
        }
    }
}

「jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException」が発生した場合は「Use Groovy Sandbox」をオフにするとビルドが通る。

余談:gitコマンドでpushしたブランチ名を取得

shで「git rev-parse --abbrev-ref HEAD」や「git symbolic-ref --short HEAD」を実行してpushされたブランチ名を取得する方法もあるらしい。
試してみたが、リモートリポジトリのブランチがランダムで取れる。マルチブランチパイプラインでないからかもしれない。
Jenkins Pipeline を使ってみたメモ - ngyukiの日記

git url: git_url, branch: "**"
git_branch = sh(script: 'git rev-parse --abbrev-ref HEAD', returnStdout: true).trim()

shell - Find Git branch name in post-update hook - Stack Overflow



デプロイ

前回はPublish over FTPを使用したが、これだと毎回全ファイルアップロードして時間が掛かる。
リモートとの差分チェックできるrsyncWinSCPを、それぞれshやbatで呼び出してデプロイした方がいい。
一、二分掛かっていた処理が数十秒で終わるようになった。
ここらへんの事は他に詳しい記事があるので詳しくはggr。

Git Pluginの機能とpollingでブランチ監視、参考

Backlogとかない環境だと以下のような方法でブランチの監視ができるらしい。
notifyCommitでgitレポジトリのポーリングを効率化 - Qiita



まとめ

特定リポジトリのpush時にBacklogからの情報をJenkinsに渡してデプロイすることができるようになった。
crumbが必要な為に遠回りをしているが、これをなくして省略化できないかなど、もう少し調査してみようと思う。

続き
dwmemo.hatenablog.com